--- url: 'https://developer.sisense.com/index.md' description: >- Build, embed, and extend analytics on the Sisense developer platform: Compose SDK, Embed SDK, Sisense.JS, and the REST API — with guides, API references, and quickstarts. --- # Empower Your App with Custom Analytics --- --- url: 'https://developer.sisense.com/guides/index.md' --- # Sisense Documentation Sisense provides APIs and additional developer tools that let you interact with the Sisense platform. These tools give you more options and flexibility for managing, enhancing and customizing the functionality and user experience of the Sisense platform. To learn more, check out the available APIs and tools below. --- --- url: 'https://developer.sisense.com/guides/accessSecurity/index.md' --- # Access, Authentication & Security When building applications with Sisense Components or APIs, your code will need to run in an authenticated context, so that Sisense can ensure your code only accesses content intended for a given user. Sisense has several ways to achieve this, for different use-cases, listed below. ## Single Sign-On The most common way for external applications to utilize Sisense embedded Components or APIs is via SSO (Single Sign-On), which is a way for your application to pass the user identity to Sisense and seamlessly log your user into Sisense in the background, without an explicit authentication challenge. SSO enables Sisense to delegate authentication to your application's existing auth mechanism. Sisense supports 3 common SSO protocols: Read more about SSO in the [Sisense Product Documentation](https://docs.sisense.com/main/SisenseLinux/using-single-sign-on-to-access-sisense.htm). ## Anonymous/Stateless Token-based Access In some cases, the named-user approach of SSO is not ideal, such as when sharing Sisense assets publicly with anonymous/unknown recipients, or when you need to embed Sisense assets in a read-only, volatile state, at scale. The Web Access Token (WAT) feature provides secure, scalable, and highly customizable view-only role access to Sisense assets, without the need to provide credentials or the use of cookies. Read more about WAT in the [Sisense Product Documentation](https://docs.sisense.com/main/SisenseLinux/using-web-access-token.htm) ## Security When embedding or building with Sisense, other security considerations such as CORS, CSRF and SSL should be considered - these are protocols and limitations that apply to web applications, intended to ensure the security of browser users by preventing various attacks. You can find documentation for setting up Sisense's behavior for each of these below. --- --- url: 'https://developer.sisense.com/guides/accessSecurity/jwt/index.md' description: >- Step-by-step tutorial for implementing JWT-based Single Sign-On (SSO) with Sisense — building an SSO handler, configuring Sisense, and testing the flow with a demo parent application. --- # SSO via JWT Tutorial ## Introduction Single Sign-On (SSO) is a mechanism that allows a system to authenticate users and subsequently tell Sisense that the user has been authenticated. The user is then allowed to access Sisense without being prompted to enter separate login credentials. This tutorial will walk you through the steps required to implement JWT-based Single Sign On with Sisense, and demonstrate this process via a demo "parent application". Before you begin, check out our SSO Documentation for general information about SSO and JWT. This tutorial includes the following required steps for setting up JWT-based SSO: ![Diagram of the required steps for setting up JWT-based SSO with Sisense](./img/jwt1.png) ## Prerequisites 1. This tutorial covers the development of a .NET-based SSO handler written using C#. Prefer an IDE capable of editing C#. 2. Download the [JWT Starter Kit](https://download.sisense.com/Tutorials/SSO/sso_jwt_starter.zip) 3. Setup the demo application according to the instructions included in the SSO-JWT Starter Kit located in the `readme.md` file. ## Developing an SSO Handler There are several steps to developing an SSO handler. 1. In this tutorial, the first step is to extract the cookie of an authenticated user so a token can be created later on. 2. The next step is to generate an object that contains the required information for a JWT token. 3. After this object has been created, the third step is to configure SSO in the Sisense Admin Console and retrieve a shared secret key that will be used in the next step to encrypt the JWT token. 4. The final step in developing an SSO handler is to redirect the request to the desired destination with the encrypted token and return\_to values. ### Step 1: Extracting the Parent Application Cookie #### Goal In this step, you will identify the user currently logged in the parent application. In this example, the username of the logged in user is stored in a browser cookie on the parent application’s domain, however in other cases, the cookie may contain an encoded token that your web server can decipher, or it may be an entirely different way of implementation. As implementations vary between applications, you can find the appropriate method of identifying the current user by looking at your application’s code or documentation. #### Actions Add the following function to your SSO Handler file, `SSOHandler.ashx` ```csharp // This method returns the username from the login cookie, or null if no user is logged in. public string ExtractUser(HttpContext context) { // Get the correct cookie from the request var Cookie = context.Request.Cookies["dummyUser"]; // Return the cookie's value if it exists if ((Cookie != null) && (Cookie.Value != null)) return Cookie.Value; // Return null otherwise return null; } ``` #### Testing First, ensure you have a cookie by going to the Login page of the sample host application, and enter a username and click Log in. ![Login page of the sample host application with username and Log in button](./img/jwt2.png) You can then test your code by adding the following lines to the main `ProcessRequest` function and accessing your handler: ```csharp string username = ExtractUser(context); context.Response.Write(username); ``` You should see the following response in your browser: ![Browser showing the extracted username returned by the SSO handler](./img/jwt3.png) *Note: Don’t forget to remove/comment out the above lines after testing!* ### Step 2: Generating a JWT - Part 1: The Object #### Goal To generate the JWT (JSON Web Token), you will need to create an object containing all the required fields as documented here. In a later step, this object will be encoded using your unique secret key to create the actual token. #### Actions To generate this object, add the following function to your SSO handler: ```csharp // This function generates the JWT object public System.Collections.Generic.Dictionary GenerateJWTPayload(string username) { TimeSpan timeSinceEpoch = (DateTime.UtcNow - new DateTime(1970, 1, 1)); int secondsSinceEpoch = (int)timeSinceEpoch.TotalSeconds; var payload = new System.Collections.Generic.Dictionary() { { "iat", secondsSinceEpoch }, { "sub", username }, { "jti", Guid.NewGuid() }, { "tenantId", tenantId } // Required for organization tenants. }; return payload; } ``` #### Testing Similarly to the previous step, add the following lines to your `ProcessRequest` function: ```csharp // Generate JWT object var payload = GenerateJWTPayload(username); var json = new JavaScriptSerializer().Serialize(payload); context.Response.Write(json); ``` Now, when you navigate to your `SSOHandler.ashx` you should see a JSON object similar to the object displayed below: ![Browser showing the JWT payload as a JSON object returned by the handler](./img/jwt4.png) ### Step 3: Configure SSO in Sisense and Retrieve Secret Key #### Goal In this step, you will turn on SSO in the Sisense Admin panel, configure it, and retrieve the secret key. #### Actions First, log in to Sisense as an Administrator and open the Admin page. Select the Single Sign On tab: ![Sisense Admin page with the Single Sign On tab selected](./img/jwt5.png) Then, turn on SSO, paste the SSO handler’s URL in the Remote Login URL field, and copy the contents of the Shared Secret field: ![Sisense SSO settings showing the Remote Login URL and Shared Secret fields](./img/jwt6.png) #### Testing There are no tests required for this step. ### Step 4: Generating a JWT - Part 2: Encoding #### Goal In this step, you will use the secret key from the Sisense Admin SSO page to encode the object created in Step #2. #### Actions Copy this function to your SSO handler: ```csharp // This function encodes a JWT object public string EncodeJWT(System.Collections.Generic.Dictionary payload) { string secret = ""; // TODO: replace with your shared secret string token = JWT.JsonWebToken.Encode(payload, secret, JWT.JwtHashAlgorithm.HS256); return token; } ``` Replace `` with your shared secret copied in the previous step. #### Testing Similarly to Steps #1 & #2, add the following lines to your `ProcessRequest` function: ```csharp // Encode JWT object into a token string token = EncodeJWT(payload); context.Response.Write(token); ``` Now, when you navigate to your `SSOHandler.ashx` you should see an encoded `string` similar to the one displayed below: ![Browser showing the encoded JWT token string returned by the SSO handler](./img/jwt7.png) #### Additional Information Note the `HS256` algorithm used in the above code sample. Sisense uses this shared-key encryption scheme. When you install Sisense, a private key and a shared key are generated. The shared key can be used to encode a message, which can only be decoded by using the private key which is known only to your Sisense instance. This ensures the user information passed between the SSO handler and Sisense remains secure, and prevents a 3rd-party from mimicking a generic JWT and accessing your Sisense deployment. Hence, this process will only work when all the below conditions are met: * You used the correct shared key provided by Sisense from the current deployment. If you had one, but re-installed Sisense or are deploying on new servers, you cannot reuse a previous key! * You use the same encryption algorithm (currently HS256) as Sisense does. ### Step 5: Joining it All Together #### Goal In this step, you will finalize your SSO Handler utilizing the results of all the previous steps. #### Actions Put the following code in your ProcessRequest function. It will extract the username from the cookie using the code from Step #1, generate the JWT object, encode it using the secret key into a token, and redirect the request to the desired destination with the token and return\_to values as parameters. ```csharp public void ProcessRequest(HttpContext context) { // Get currently logged in user string username = ExtractUser(context); // If user is not logged in, redirect to main login page if(username == null) { context.Response.Redirect("./default.aspx"); } // Generate JWT object var payload = GenerateJWTPayload(username); // Encode JWT object into a token string token = EncodeJWT(payload); // This is the Sisense URL which can handle (decode and process) the JWT token string redirectUrl = "http://reporting.mytestapp.com:8081/jwt?jwt=" + token; // Which URL the user was initially trying to open string returnTo = context.Request.QueryString["return_to"]; if (returnTo != null) { redirectUrl += "&return_to=" + HttpUtility.UrlEncode(returnTo); } // Perform the redirect context.Response.Redirect(redirectUrl); } ``` #### Testing At this stage, when navigating to your `SSOHandler.ashx` you should be redirected to the Login page (if no cookie exists) or to the reporting page. #### Additional Information Note the `return_to` query string parameter. When a user tries accessing a specific Sisense URL (for example, a specific dashboard) and is redirected to the SSO handler for login, the original URL the user tried to access is being passed to the handler. This block of code then attaches it to the redirect URL, which in turn tells Sisense to open that view once the JWT is processed and the user is authenticated. This mechanism ensures a smoother user experience. ## Implementing Log Out Now that your users can log in, you need to implement a flow for logging out. ### Background Your parent application has a Logout button that deletes the user cookie and redirects the user to the Login page. You would like the Sisense cookie to be deleted as well, to ensure the user is logged out from Sisense so that when a new user logs in they go through the SSO process again and are logged in to Sisense correctly. There are three main ways to log out from Sisense: * Navigating the iFrame to the `/api/auth/logout` endpoint * Sending a JavaScript postMessage to an Add-on that logs a user out * Using the advanced log-out API The first method is easiest to implement, but is limited - it requires an iFrame, whether visible or hidden. This is because simply sending an AJAX request from the host application to this endpoint will pass on the host application’s cookies, and not the Sisense cookies, thus not performing the desired log-out operation. The second method has the same limitations and is slightly more complex to implement, but it is considered safer and quicker. The actual logout action is performed by Sisense in this case. The third is the most complex, but also the most robust approach as it can be called from any state of your host application regardless of whether the Sisense iFrame currently exists or not. ### Step 6: Implementing Log-out #### Goal For this tutorial, you will use the first method for simplicity and brevity. #### Actions Add the following code to your host application’s `logout` function in the `reporting.aspx` page: ```js $('#logout').click(() => { // Log out by navigating the iFrame to the Logout API $('#frame1').attr('src', 'http://reporting.mytestapp.com:8081/api/auth/logout'); // Remove the user's cookie Cookies.remove('dummyUser'); // Navigate to the main page window.location.href = '/default.aspx'; }); ``` When the logout button is clicked, this function will perform three operations: 1. Log the user out of Sisense 2. Log the user out of the host application (in this case by removing the user’s cookie) 3. Return the user back to the main Login page #### Additional Information This of course is a simplified approach. In a real world use case, you would likely need the following amendments: * Ensure the iFrame exists, and create a hidden one if it doesn’t, so that the Sisense log-out operation can be performed regardless of which page the user is on * Have a more complex logout process from your own host application, and likely have this logic implemented in some centralized service and used across multiple views To read more about using postMessage and the logout API, please visit these pages: * [Using postMessage to log out (forum post)](https://community.sisense.com/forum/apis-42/topic/logging-users-out-of-sisense-from-your-site-5083/) * [Sisense Authentication API (REST API reference)](/guides/restApi/v1/) ## Testing the SSO Flow The final step is to test your SSO flow. Perform the following steps: 1. In an "incognito" window, browse to `http://reporting.mytestapp.com:8081` > You should see the Sisense Login page as you are not logged in. 2. Browse to `http://main.mytestapp.com:8083`. You should reach the host application’s login page. 3. Enter a valid email and click Log in. You should be directed to the reporting page, and Sisense should open in the iframe with the username you have entered. 4. Open a new tab and navigate to `http://reporting.mytestapp.com:8081`. You should now be logged in and reach the homepage. 5. Return to the other tab and click the logout button. You should be redirected to the host application’s login page again. 6. In the second tab, refresh. You should again reach the Sisense login page, confirming you have been logged out of Sisense. ## Summary Congratulations! You have successfully implemented basic Single Sign On with Sisense. At this stage you should comfortably understand the standard SSO flow when working with Sisense. This tutorial covers one specific scenario, when Sisense is embedded with an iFrame into a rather simple host application. However, your project might differ in many ways, such as: * A different method of login/authentication in the host application * A different method of embedding such as SisenseJS * No embedding at all * Other server-side languages (this demo uses ASP.NET and C#) If you have any further questions, you can ask on our [customer forums](https://community.sisense.com/) or contact [Sisense Support](mailto:support@sisense.com). --- --- url: 'https://developer.sisense.com/guides/accessSecurity/sso/index.md' --- # Sisense Single Sign-On Single Sign-On (SSO) is a mechanism that allows a system to authenticate users and subsequently tell Sisense that the user has been authenticated. The user is then allowed to access Sisense without being prompted to enter separate login credentials. ::: tip For general information on Sisense SSO, [click here](https://documentation.sisense.com/docs/using-single-sign-on-to-access-sisense#) ::: Sisense SSO supports three SSO protocols for securing the exchange of user authentication data, detailed below. --- --- url: 'https://developer.sisense.com/guides/accessSecurity/wat/index.md' --- # Web Access Tokens (WAT) ::: warning Feature Availability * Web Access Tokens are only available on Sisense for Linux, versions or newer. * Web Access Tokens are only available for Sisense applications that have the Web Access Token feature included in their Sisense license agreement. ::: Sisense Web Access Tokens can be used to provide anonymous, stateless viewer access to Sisense dashboards at large scales, as an alternative to named-user authentication approaches such as SSO. Below you can find links to documentation on how to generate Web Access Tokens as well as how to use them for embedding Sisense. --- --- url: 'https://developer.sisense.com/guides/choosing-an-integration.md' description: >- Compare Compose SDK, Embed SDK, iFrame embedding, Sisense.JS, and the REST API to choose the right Sisense integration method for your use case. --- # Choosing a Sisense Integration Method **Short answer:** If you are building a new analytics experience in **React, Angular, or Vue**, use the **Compose SDK** — it is the code-first, component-based way to build queries, charts, and dashboards. If you want to drop **existing Sisense dashboards** into your app with minimal code, use the **Embed SDK** (or a plain **iFrame**). To **automate or manage Sisense itself** (data models, users, security, dashboards) from a server or script, use the **REST API**. **Sisense.JS** — the older approach for embedding individual widgets without iFrames — is **legacy**: for new development use the **Compose SDK**, which replaces it. These approaches are complementary and are frequently combined. ## At a glance | Method | Best for | Languages / runtime | What you render | Auth model | Avoid when | |---|---|---|---|---|---| | **[Compose SDK](./sdk/)** | Building new, fully custom analytics in code | React, Angular, Vue, Web Components (TypeScript/JS) | Your own components: charts, tables, filters, queries, and dashboards composed in code | App-level (JWT/SSO, WAT (Web Access Token), API token) via the SDK | You only need to show an existing dashboard as-is | | **[Embed SDK](./embeddingDashboards/embed-sdk.html)** | Embedding **existing** Sisense dashboards programmatically | Any web app (framework-agnostic JavaScript) | A complete Sisense dashboard, controlled via a JS API | Sisense session / SSO | You need per-widget custom layout or code-first charts | | **[iFrame embedding](./embeddingDashboards/iframe.html)** | The fastest way to embed a dashboard | Any HTML page | A complete Sisense dashboard in an ` ``` You will also need: * The URL of your Sisense application, such as `https://sisense.myapp.com:8081` * The OID of the dashboard you would like to open initially, such as: `5d39916c17b58f235cdae1b4` Create an instance of the `SisenseFrame` class: ```js // Create an instance of SisenseFrame const sisenseFrame = new SisenseFrame({ // Sisense application URL, including protocol and port if required url: 'https://sisense.myapp.com:8081', // OID of dashboard to load initially dashboard: '5d39916c17b58f235cdae1b4', // Which panels to show in the iFrame settings: { showToolbar: false, showLeftPane: false, showRightPane: false }, // Existing iFrame DOM element element: document.getElementById('sisense-iframe') }); // Calling render() will apply the above configuration to the existing iFrame element sisenseFrame.render().then(() => { console.log("Sisense iFrame ready!"); }); ``` ### Automatically create an iFrame element You will need an existing DOM element on your page to render the iFrame into, such as a `DIV`: ```html
``` You will also need: * The URL of your Sisense application, such as `https://sisense.myapp.com:8081` * The OID of the dashboard you would like to open initially, such as: `5d39916c17b58f235cdae1b4` * Optionally, an ID you would like the automatically generated iFrame element to have, such as `'sisense-iframe'` Create an instance of the `SisenseFrame` class and render it: ```js // Create an instance of SisenseFrame const sisenseFrame = new SisenseFrame({ // Sisense application URL, including protocol and port if required url: 'https://example.com', // OID of dashboard to load initially dashboard: '5d39916c17b58f235cdae1b4', // Which panels to show in the iFrame settings: { showToolbar: false, showLeftPane: false, showRightPane: false }, // Optional ID for the iFrame element id: 'sisense-iframe' }); // Calling render(container) will create an iFrame element within the container provided and apply the above configuration to it sisenseFrame.render(document.getElementById('sisense-container')).then(() => { console.log("Sisense iFrame ready!"); }); ``` ### Using volatile mode When a user interacts with the dashboard embedded using Embed SDK, such as when modifying filters, these changes are saved to the Sisense Application Database and are thus persisted. The user will see the changes upon reload, or even when logging in from a different machine. From version , Embed SDK also supports "volatile mode", where changes are not persisted and will only apply while the user is viewing the page. When volatile mode is used, refreshing the page will result in the dashboard loading in it's original state, before any changes made by the user. To use volatile mode, pass `volatile: true` to the Embed SDK constructor: ```js // Create an instance of SisenseFrame const sisenseFrame = new SisenseFrame({ // Sisense application URL, including protocol and port if required url: 'https://example.com', // OID of dashboard to load initially dashboard: '5d39916c17b58f235cdae1b4', // Use volatile mode: volatile: true }); ``` ::: warning Volatile Mode vs Edit Mode Please note - "edit mode" for dashboards and widgets is **not compatible** with `volatile` mode (as edit mode relies on storing user changes).\ When configuring the Embed SDK with `volatile: true`, the `editMode` parameter will be ignored. ::: ### Using Web Access Tokens In Sisense for Linux versions or newer, you can use Web Access Tokens together with EmbedSDK as an alternative to standard authentication methods such as SSO. ::: tip Web Access Token For more information, please refer to the [Web Access Token documentation](https://documentation.sisense.com/docs/using-web-access-token) ::: For embedding with EmbedSDK, the WAT is passed as an argument to the SDK upon initiation. For example: ```js const sisenseFrame = new SisenseFrame({ // Sisense application URL, including protocol and port if required url: 'https://example.com:30845', // Web access token: wat: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' // OID of dashboard to load initially dashboard: '5d39916c17b58f235cdae1b4', // Which panels to show in the iFrame settings: { showToolbar: false, showLeftPane: false, showRightPane: false }, // Existing iFrame DOM element element: document.getElementById('sisense-iframe') }); ``` ## Interacting with the iFrame and Sisense application All commands available in the Embed SDK work asynchronously and return JavaScript `Promise` objects that resolve when the operation is complete. Additionally, timing your code execution correctly is enabled by using not just the promises of actions, but also various events that indicate a change in the iFrame's state, such as `"dashboardloaded"`. For example: ```js // Do something as soon as the iFrame is rendered sisenseFrame.render(frameContainerElement[0]).then(() => { console.log("iframe element rendered!"); }); // Do something when a dashboard is loaded sisenseFrame.dashboard.on(enums.DashboardEventType.LOADED, (args) => { console.log("Dashboard " + args.dashboard.oid + " loaded!"); }); ``` ### Configuring the UI panels Whether during the creation of the iFrame (via the constructor) or later using the `updateSettings()` method, you can configure which panels are shown by passing in the following configuration: ```js { showToolbar: false, showLeftPane: false, showRightPane: false } ``` For example: ```js // Update UI settings sisenseFrame.updateSettings({ showToolbar: false, showLeftPane: false, showRightPane: false }).then(() => { // Resolves when the settings have been applied console.log("New settings applied!"); }) ``` ### Getting information You can retrieve information from the Sisense application, such as information about the currently logged-in user or the currently shown dashboard. For example: ```js // Get information about the current user sisenseFrame.app.getUser().then((user) => { // Reflect some of this information in the host page UI document.getElementById('sisense-username').innerText = user.username; }); // Get information about the current dashboard sisenseFrame.dashboard.getCurrent().then((dashboard) => { // Reflect some of this information in the host page UI document.getElementById('dashboard-name').innerText = dashboard.title; }); ``` ### Events You can subscribe to events fired by the Sisense application and react to them on your host page. You can also unsubscribe handlers from events. For example: ```js // Define an event handler function dashboardLoadedHandler(args) { console.log("Dashboard " + args.dashboard.oid + " loaded!"); } // Subscribe to the dashboard loaded event sisenseFrame.dashboard.on(enums.DashboardEventType.LOADED, dashboardLoadedHandler); // Unsubscribe the handler from the event sisenseFrame.dashboard.off(enums.DashboardEventType.LOADED, dashboardLoadedHandler); ``` ### Manipulating filters You can add, replace and remove filters from the dashboard loaded in the iFrame using the Embed SDK's functions `.dashboard.applyFilters()` and `.dashboard.removeFilters()` To work with dashboard filters, you will need to be familiar with the [Sisense JAQL (JSON Analytical Query Language) syntax](/guides/querying/jaqlSyntax/) To set filters, pass one or more filter objects to the `applyFilters` function. Existing filters on the same dimensions will be replaced, and the rest will be added. For example: ```js let filter = { "jaql" : { "title": "Country", "dim" : "[Country.Country]", "datatype": "text", "filter" : { "startsWith" : "a" } } }; sisenseFrame.dashboard.applyFilters(filter); ``` You can also pass in an array of filters: ```js let filters = [ { "jaql" : { "title" : "Country", "dim" : "[Country.Country]", "datatype" : "text", "filter" : { "startsWith" : "a" } } }, { "jaql" : { "title" : "Product", "dim" : "[Products.Name]", "datatype" : "text", "filter" : { "members": ["Laptop"] } } } ]; sisenseFrame.dashboard.applyFilters(filters); ``` To remove filters, pass one or more filters to the `removeFilters` function. It is sufficient to provide just the dimension name. For example: ```js sisenseFrame.dashboard.removeFilters({ "jaql": { "dim": "[Sales.PurchaseDate (Calendar)]", "level": "days" } }); ``` Calling the `applyFilters` and `removeFilters` as described above will make volatile (temporary) changes to the dashboard filters - they will not persist once you refresh the page, and will not be seen by other users accessing the same dashboard. To persist the filter changes, pass the optional `persist` boolean parameter, like so: ```js sisenseFrame.dashboard.applyFilters(filters, true); sisenseFrame.dashboard.removeFilters(filters, true); ``` *Note that for Date dimensions, the dimension is identified by both name and level, so both must be passed to add/remove a filter* Starting from version you can also use `dashboard.clearFilters(persist)` to remove all dashboard filters at once: ```js sisenseFrame.dashboard.clearFilters(true); ``` ### Working with UI Themes As part of the [UI Customization & Themes](https://documentation.sisense.com/latest/embedding-sisense/rebranding/customizing-ui_linux.htm#gsc.tab=0) feature available on Sisense for Linux `L2021.3` or newer, you can apply any existing theme to the embedded Sisense UI using the desired theme's `oid`. To get the available theme `oid`s, you can use the REST API endpoint. This `oid` can then be used in two ways: #### Setting an initial theme To predefine a theme before the embedded dashboard is loaded, specify the `theme` property as part of the [EmbedSDK `init` object](./embed-sdk-ref.html#init) passed to the constructor when initializing the SDK: ```js var sisenseFrame = new SisenseFrame({ url: 'https://example.com', dashboard: '5d39916c17b58f235cdae1b4', theme: '605b633dc24bb7001ae42fbb' }); ``` #### Setting a theme dynamically To apply a theme dynamically at any time after initialization, use the `oid` as a parameter for the `app.setTheme(themeOid)` method of the [`SisenseFrame` Class](./embed-sdk-ref.html), which will apply the theme to the embedded Sisense application: ```js sisenseFrame.app.setTheme('605b633dc24bb7001ae42fbb').then(function() { console.log('new theme applied'); }); ``` You can also clear the set theme using `app.clearTheme()` which will revert the UI to the user's default theme. --- --- url: 'https://developer.sisense.com/guides/embeddingDashboards/iframe.md' --- # Embedding Dashboards in an iFrame The quickest & easiest way to embed Sisense dashboards in your web application is with an `iFrame` element. Sisense has built in support for this mode of embedding, with query parameters you can append to the dashboard's URL to adjust the UI for your embedding needs. ::: tip The `iFrame` method is great for simple embedding use cases. However, if your application has more complex requirements than what is described in this article, Sisense has a JavaScript library called `Embed SDK` which expands on the `iFrame` embedding capabilities, providing a rich API for a more interactive embedded experience. To learn more, please refer to the [Embed SDK Documentation](/guides/embeddingDashboards/embed-sdk.html) page. ::: ## Authentication in Embedded Dashboards and Widgets All Sisense dashboards and widgets require authentication. When embedding dashboards outside of the Sisense environment, usually the use case is to skip the Sisense authentication (login) page, and instead use SSO (Single Sign On) with existing corporate authentication. To read more on configuring Single Sign On in Sisense, [click here](https://documentation.sisense.com/docs/using-single-sign-on-to-access-sisense#). ## Walkthrough *Follow the steps below to embed your first dashboard in mere minutes.* ### 1. Getting the dashboard's URL for embedding The easiest way to get a dashboard's URL is by navigating to that dashboard in the Sisense UI and simply copying the entire URL from your browser. However, to construct the URL which might be required when dynamically choosing which dashboard to display, use the following template: ``` {protocol}://{hostname}[:{port}]/app/main#/dashboards/{dashboard_id} ``` For example: ``` https://example.com/app/main#/dashboards/5ec13529b61a73002db725d3 ``` ::: warning Dashboards in folders If your dashboard is within a folder, and you copied the URL from the browser after opening the dashboard, you will need to delete the `?folder={folder id}` query parameter from the URL and everything that follows, before continuing to the next step. For example: Original dashboard URL for dashboard within folder: ``` https://example.com/app/main#/dashboards/550952417404b2981a000029?folder=550955a27404b2981a00003b ``` Embedded URL for dashboard within folder: ``` https://example.com/app/main#/dashboards/550952417404b2981a000029?embed=true ``` ::: ### 2. Embedding a Dashboard To embed the dashboard in your application, use an `iFrame` HTML element and append the query parameter `embed=true` to the URL retrieved in the previous step. For example: ```html ``` When `embed=true` is appended to the URL, Sisense will display the dashboard in embedded mode which means all toolbars and panels will be hidden except for the right-side filters panel, and the dashboard will be shown in "view" mode (it cannot be modified or re-arranged). ### 3. Customizing the UI for embedding By adding specific query parameters appended to the dashboard's URL, you can customize which parts of the Sisense UI are being displayed, and how the UI behaves. You can show panels otherwise hidden in embedded mode, switch the dashboard to "edit" mode, and even apply temporary dashboard filters - all using the query parameters in the URL. For example, by appending the query parameter `l=true` you indicate that Sisense should show the left-side panel that contains the dashboard navigation: ``` https://example.com/app/main#/dashboards/5ec13529b61a73002db725d3?embed=true&l=true ``` ::: tip You can append multiple parameters to the URL by separating them with the `&` character. ::: #### 3.1. Applying Themes As part of the [UI Customization & Themes](https://documentation.sisense.com/latest/embedding-sisense/rebranding/customizing-ui_linux.htm#gsc.tab=0) feature available on Sisense for Linux `L2021.3` or newer, you can apply any existing theme to the embedded dashboard by passing the theme's `oid` with the `theme` query parameter. To get the available theme `oid`s, you can use the REST API endpoint. Then, use the `oid` field from the response as the value for the optional `theme` query parameter: ``` https://example.com/app/main#/dashboards/605c78b1316210002b2f8bb7?embed=true&theme=605b633dc24bb7001ae42fbb ``` You can also choose a theme when using the [Embed Code](https://documentation.sisense.com/latest/embedding-sisense/embedding/embed-sisense.htm#gsc.tab=0) feature, and this parameter will be included in the generated URL and sample code. ### 4. Using Edit Mode As mentioned above, when embedding a Sisense dashboard using an `iFrame` the default behavior is "view" mode, but a query parameter can be used for "edit" mode. * **View** mode: Users can only view the dashboards and widgets, regardless of whether they are a viewer or a designer for that dashboard. In addition, Designer users can perform the actions available through the toolbar buttons of the dashboard/widget, such as duplicating and downloading dashboards, and renaming and deleting widgets. This mode is identical to "view mode" in the Sisense UI * **Edit** mode: Designer users can modify the dashboard - move and resize widgets and even edit them directly within the embedded iFrame. Viewers can still only view the dashboard. As the default behavior is "view" mode, you don't need to do anything to enable it. To enable "edit" mode, append the parameter `edit=true` to the URL. For example: ``` https://example.com/app/main#/dashboards/5ec13529b61a73002db725d3?embed=true&l=true&edit=true ``` Note that in edit mode, a designer will be able to click the "widget edit" button and navigate the iFrame to the widget editor UI. As that UI has different panels than the dashboard, the various available query parameters will have a different affect. See [Available Query Parameters](#available-query-parameters) for more details. ### 5. Appending filters You can provide an array of [Sisense JAQL](/guides/querying/useJaql/) filters via the `filter` query parameter, as a URI-encoded `string`. You can write the JAQL filters yourself or extract them from an existing dashboard. That JAQL needs to be turned into a `string` and URI-encoded. For example, this code will correctly encode the first filter in the currently viewed dashboard (you can run this in the browser's developer console): ```js // Get the first filter in the current dashboard var filterObject = prism.activeDashboard.filters.item(0); // Filters need to be in an array var filtersArray = [filterObject]; // Serialize the filters array into a string var filterString = JSON.stringify(filtersArray); // Encode the string for use in a URI var uriEncoded = encodeURIComponent(filterString); ``` This process will turn a JSON object like this: ```json [ { "jaql": { "dim": "[Admissions.Death]", "filter": { "explicit": true, "multiSelection": true, "members": ["Yes"] }, "collapsed": false, "title": "Death" } } ] ``` Into a `string` like this: ``` %5B%7B%20%22jaql%22%3A%20%7B%22dim%22%3A%22%5BAdmissions.Death%5D%22%2C%22filter%22%3A%7B%22explicit%22%3Atrue%2C%22multiSelection%22%3Atrue%2C%22members%22%3A%5B%22Yes%22%5D%7D%2C%22collapsed%22%3Afalse%2C%22title%22%3A%22Death%22%7D%7D%5D ``` Which can then be appended to the iFrame's URL as the query parameter `filter`: ``` https://example.com/app/main#/dashboards/5ec13529b61a73002db725d3?embed=true&filter=%5B%7B%20%22jaql%22%3A%20%7B%22dim%22%3A%22%5BAdmissions.Death%5D%22%2C%22filter%22%3A%7B%22explicit%22%3Atrue%2C%22multiSelection%22%3Atrue%2C%22members%22%3A%5B%22Yes%22%5D%7D%2C%22collapsed%22%3Afalse%2C%22title%22%3A%22Death%22%7D%7D%5D ``` ### 6. Using Web Access Tokens In Sisense for Linux versions or newer, you can use Web Access Tokens together with iFrame embedding as an alternative to standard authentication methods such as SSO. ::: tip Web Access Token For more information, please refer to the [Web Access Token documentation](https://documentation.sisense.com/docs/using-web-access-token) ::: For embedding with iFrames, the WAT is used in the same way as described in the WAT documentation - the token is inserted as part of the URL path: ``` https://{host}:{port}/wat/{WebAccessToken}/app/main#/dashboards/{DashboardOid}?embed=true ``` For example: ``` http://example.com:30845/wat/eyJhbGciOtT0[…]pyjKglx.F5eR8BZddcjNLYD1DBEQpA/app/main#/dashboards/5ec13529b61a73002db725d3?embed=true ``` ## Available Parameters *Remember: all of the parameters below are optional and can be combined!* | Name | Type | In Dashboards | In Widgets | |----------|-----------|-----------------------------------------------------------------------------------------|-----------------------------------------------------| | `embed` | `boolean` | Shows the dashboard in embedded mode | Shows the widget in embedded mode | | `edit` | `boolean` | Enables dashboard designers to edit the dashboard | N/A | | `h` | `boolean` | Show/hide the environment header | Show/hide the environment header | | `t` | `boolean` | Show/hide the dashboard toolbar | Show/hide the widget toolbar | | `l` | `boolean` | Show/hide the Navigation Panel to the left | Show/hide the data panel to the left | | `r` | `boolean` | Show/hide the filter panel to the right | Show/hide the filter and design panel to the right | | `filter` | `string` | Adds temporary filters to the dashboard. See [Appending filters](#_5-appending-filters). | N/A | | `theme` | `string` | Applies the provided theme (by `oid`) to the dashboard | Applies the provided theme (by `oid`) to the widget | --- --- url: 'https://developer.sisense.com/guides/embeddingSisense/index.md' --- # Embedding Sisense Sisense supports 3 embedding methods, listed below: ::: warning SameSite Cookie Policy Some browsers, such as Chrome 80 (and newer), have implemented stricter policies regarding cookies that affect embedding. For an embedded application to work in these browsers, SSL must be enabled and some additional configuration must be done in Sisense. Please refer to [Product Documentation: Security Settings](https://docs.sisense.com/main/SisenseLinux/security-settings.htm) ::: ## Choosing the right embedding approach Use this table to determine which approach fits your use case: | Method | **iFrames** | **Embed SDK** | **Sisense.js** | |--------------------------|--------------------------------------|--------------------------------------------|----------------------------------------------| | **Skill level** | Basic HTML | JavaScript | JavaScript | | **Time to deployment** | Very fast | Fast | Medium | | **Flexibility** | Limited | Medium | High | | **What can be embedded** | Entire UI, dashboards, widget editor | Entire UI, dashboards, widget editor | Individual widgets, filters panel | | **Best use case** | Simple static embedding | Interactive embedding of entire dashboards | Embedding individual widgets, custom layouts | --- --- url: 'https://developer.sisense.com/guides/querying/index.md' --- # Querying Sisense Data models Sisense Dashboards, Widgets and Filters use [JAQL](/guides/querying/useJaql/) to query Sisense Data models. However, Sisense also provides ways to run standard SQL queries to get data from Data models. --- --- url: 'https://developer.sisense.com/guides/querying/jaqlSyntax/index.md' --- # JAQL Syntax Reference **Properties** | Name | Type | Required | Description | Default | | ------------------ | -------------------- | -------- | -------------------------------------------------------------------------------------------------------------- | ----------- | | `datasource` | `string` or `object` | **Yes** | States the connection (ElastiCube) name against which to execute the query. | N/A | | `metadata` | `object[]` | **Yes** | Contains an array of JAQL elements. A JAQL element is essentially dimension or measure. | N/A | | `format` | `string` | No | States the expected query result data type; CSV or JSON | `"json"` | | `offset` | `number` | No | Cuts the query result by setting the row offset and row count | `undefined` | | `count` | `number` | No | Cuts the query result by setting the row offset and row count | `undefined` | | `csvSeparator` | `string` | No | Defines the CSV separator that is used when rendering the CSV query result | `","` | | `isMaskedResponse` | `boolean` | No | Whether returned values are formatted. When `true`, values will be objects with a `data`+`text` property pair. | `true` | ## Datasource The `datasource` property tells Sisense which Data Model the query should run against. ::: tip You can use the [ endpoint](/guides/restApi/v0/?platform=linux\&spec=L2023.3#/management-rest-controller/listUsingGET) to get a list of objects in this format for each of the Data Models available on your environment. ::: **Properties** | Name | Type | Required | Description | | ---------- | -------- | -------- | --------------------------- | | `title` | `string` | Yes | Name of Data Model to query | | `fullname` | `string` | No | Internal use (not required) | | `id` | `string` | No | Internal use (not required) | | `address` | `string` | No | Internal use (not required) | | `database` | `string` | No | Internal use (not required) | **Example** ```json { "title": "Sample Healthcare", "fullname": "LocalHost/Sample Healthcare", "id": "localhost_aSampleIAAaHealthcare", "address": "localHost", "database": "aSampleIAAaHealthcare" } ``` ## Metadata Items This array specifies all of the dimensions (columns), measures (aggregations) and filters that will participate in the query, whether they are part of the result set or not. Each item in the array (aka `MetadataItem`) represents one dimension/measure/filter. ::: warning Please Note Each `MetadataItem` regardless of content has the following structure: ```json { "jaql": { // Query properties here }, // Other properties } ``` The sections below will describe the contents of the `jaql` property - there are other properties used internally which do not need to be provided. ::: ### Dimensions **Properties** | Name | Type | Required | Description | | -------- | -------- | -------- | ----------------------------------------- | | `dim` | `string` | **Yes** | The dimension name | | `level` | `string` | No | States the date level in a Date dimension | | `filter` | `object` | No | Defines the element's filter | **Supported Date Levels** * `years` * `quarters` * `months` * `days` * `hours` * `minutes` * `timestamp` **Example** ```json { "jaql": { "dim": "[Users.CreatedOn (Calendar)]", "level": "days" } } ``` ### Simple Aggregations **Properties** | Name | Type | Required | Description | | -------- | -------- | -------- | ------------------------------------------------------------------------------ | | `dim` | `string` | **Yes** | The dimension name | | `level` | `string` | No | States the date level in a Date dimension | | `agg` | `string` | **Yes** | Defines the measure aggregation over the dimension defined in the dim property | | `filter` | `object` | No | Defines the element's filter | **Supported Aggregations** * `avg` * `count` * `countduplicates` * `min` * `max` * `median` * `stdev` * `stdevp` * `sum` * `var` * `varp` **Example** ```json { "jaql": { "dim": "[Users.ID]", "agg": "count" } } ``` ### Formulas **Properties** | Name | Type | Required | Description | | --------- | -------- | -------- | --------------------------------------------------------- | | `formula` | `string` | **Yes** | Defines the formula string | | `context` | `object` | **Yes** | Defines the context of the dimensions used in the formula | **Example** ```json { "jaql": { "formula": "count([users]) / 10", "context": { "users": { "dim": "[Users.ID]" } } } } ``` ## Filtering In JAQL, filters are created by adding a `filter` property to the `MetadataItem` as follows: ```json { "jaql": { "dim": "[Table.Column]", "filter": { // Filter properties go here } } } ``` ::: warning Please Note For brevity, the sections below describe the inner contents of the `filter` property without specifying the surrounding Metadata in its entirety ::: ::: tip Foreground vs Background Filtering There are 2 possible behaviors for filters: **Foreground filters** are filters applied to a dimention that participates in the query, and are part of the result set. For example, consider this query: ```json [{ "jaql": { "dim": "[Customers.Country]" } }, { "jaql": { "dim": "[Customers.ID]", "agg": "count" } }] ``` This query will return a list of countries, with how many customers exist in each country. By adding a filter to the participating Country dimension, we create a foreground filter - the result set will include only one row, as the filter only allows the inclusion of one country: ```json { "jaql": { "dim": "[Customers.Country]", "filter": { "members": ["Ukraine"] } } } ``` **Background Filters** are filters on a dimension that does not participate in the query result, which can be created by specifying the `MetadataItem` property `panel: scope`. For example, this query will return a list of countries with a count of users in each country, but only counting the active users.\ There will be no `Active` column in the result set, and it will not be used to group data. ```json [{ "jaql": { "dim": "[Customers.Country]" } }, { "jaql": { "dim": "[Customers.ID]", "agg": "count" } },{ "jaql": { "dim": "[Customers.Active]", "filter": { "members": ["Yes"] } }, "panel": "scope" ] ``` ::: ### Members **Supported Datatypes:** `text`, `number`, `datetime` Filter by specific unique values of the dimension **Properties** | Name | Type | Required | Description | | --------- | ---------- | -------- | ------------------ | | `members` | `string[]` | **Yes** | An array of values | **Example** ```json { "members": ["USA", "China"] } ``` ### Text Filters **Supported Datatypes:** `text` **Supported Filters** * `equals` * `doesntEqual` * `contains` * `doesntContain` * `startsWith` * `doesntStartWith` * `endsWith` * `doesntEndWidth` * `like` **Example** ```json { "equals": "USA" } ``` ### Mathematical Filters **Supported Datatypes:** `number`, `datetime` **Supported Filters** * `equals` * `doesntEqual` * `from` * `fromNotEqual` * `to` * `toNotEqual` **Example** ```json { "equals": 42 } ``` ### Relative Date Filters **Supported Datatypes:** `datetime` **Supported Filters** * `last` * `next` **Properties** | Name | Type | Required | Description | | -------- | -------- | -------- | ------------------------------------------------------------------- | | `count` | `number` | No | How many time units to include. Defaults to `1` | | `offset` | `number` | No | How many time units to skip. Defaults to `0` | | `anchor` | `string` | No | Set a custom date to calculate from. Can also be `first` or `last`. | **Example** ```json { "last":{ "count": 5, "offset": 10 } } ``` ### Top/Bottom Filters **Supported Datatypes:** `text`, `number`, `datetime` **Supported Filters** * `top` * `bottom` **Properties** | Name | Type | Required | Description | | ----------------- | -------- | -------- | -------------------------------------------------------------- | | `top` or `bottom` | `number` | **Yes** | How many items to include | | `by` | `object` | **Yes** | A JAQL aggregation object (`dim`+`agg` or `formula`+`context`) | **Example** ```json { "top": 2, "by": { "dim": "price", "agg": "sum" } } ``` ### Negative Filters **Example** ```json { "exclude": { "members": [ "London", "Paris" ] } } ``` ### Combining Filters **Supported Filters** * `and` * `or` **Example** ```json { "or":[ { "like": "%acer%" }, { "contains": "toshiba" } ] } ``` --- --- url: 'https://developer.sisense.com/guides/querying/sql/index.md' --- # Running SQL Queries in Sisense You can run SQL queries to extract data from Sisense Datamodels. You can either use the [SQL Runner](#using-sql-runner), or use a [REST API](#using-rest-api). Both options are explained below. ::: warning Note This feature is only available to Administrators. You can allow additional user roles to access it by [customizing user roles](https://documentation.sisense.com/customizing-user-roles/). ::: ## SQL Syntax & Limitations * The syntax is standard SQL * Sisense supports `SELECT` queries only. You cannot use this interface for `INSERT`, `UPDATE`, `DELETE` and other SQL operations. * The table and column names are the same names that appear in your Datamodel **Example** Given the following Datamodel: ![Example Sisense Datamodel showing table and column names used in SQL queries](./img/sqlrunner3.png) You could run this SQL statement: ```sql SELECT "first name", "last name" FROM customers ``` And get the following JSON response: ```json { "headers": [ "first name", "last name" ], "values": [ [ "Britney", "Britton" ], [ "Candace", "Horton" ], [ "Annie", "Thorisdottir" ], [ "Amy", "Ricana" ] ] } ``` ## Using REST API You can run SQL queries using API calls from your own REST client such as Postman, or a script/application. This is done via a `GET` HTTP request to the SQL endpoint's path: **Parameters** | Name | Location | Type | Required | Details | |--------------|----------|----------|----------|------------------------------------------------------------| | `datasource` | path | `string` | **Yes** | Your Datamodel's title | | `query` | query | `string` | **Yes** | Your SQL query as a single-line `string` | | `format` | query | `string` | No | Response format. Defaults to `json` and also accepts `csv` | The endpoint returns results in JSON format. **Example** To execute the following SQL query: ```SQL SELECT city, count(*) FROM sales GROUP BY city ``` against the `laptopsales` Datamodel, use the following URi: ``` https://example.com/api/datasources/laptopsales/sql?query=select sales.city count(*) from sales group by sales.city ``` ## Using SQL Runner Sisense includes a simple UI to execute SQL queries, which can be useful for one-off operations and debugging. To access the SQL Runner: 1. Open Sisense web in a browser and log in. 2. In your browser, navigate to `/app/sqleditor?datasource=`. For example: ``` https://example.com/app/sqleditor?datasource=Training ``` 3. Enter your SQL query in the left panel and click execute. Results will be returned in the right panel ![Sisense SQL editor with a query in the left panel and results in the right panel](./img/sqlrunner2.png) ## Usage Analytics Sisense [Usage Analytics](https://docs.sisense.com/main/SisenseLinux/overview-of-usage-analytics.htm) collects usage information for your Sisense system. This information is collected and stored in a CSV file. You can download this file with the SQL Runner with the following call: Sisense for Windows: ``` https://example.com/api/datasources/LocalHost/Usage%20Analytics%20Model/sql?query=select%20*%20from%20usage&format=csv ``` Sisense for Linux: ``` https://example.com/api/datasources/Usage%20Analytics%20Model/sql?query=select%20*%20from%20usage&format=csv ``` --- --- url: 'https://developer.sisense.com/guides/querying/useJaql/index.md' --- # Tutorial: Using the JAQL syntax By following this tutorial, you will learn what the Sisense JAQL syntax is, how to construct queries with it, and how you can utilize this new skill to extract more value out of Sisense. This tutorial is composed of three parts, each containing several steps: ## Prerequisites You will need: 1. Sisense installed on a non-production server or your own PC 2. The credentials of an Admin user 3. The following Data model and Dashboards, imported into Sisense: * [For Windows versions](https://data.sisense.com/Product/Docs/JAQL-Tutorial.zip) * For Linux versions 4. A quick review of the JAQL Reference ## Part 1: Building your First JAQL Query In this section, you will construct a JAQL query containing a sampling of the syntax’s various abilities, as well as learn how to test your query and modify its output. ### Step 1: Using the JAQL Runner Utility Sisense comes bundled with a utility that lets you write and run JAQL queries. You will use this utility for all the following steps, running your query after each step to test it. Log in to Sisense using Admin credentials, and then navigate to `https://example.com/app/jaqleditor` ::: tip Your Sisense URL might differ - in that case, make sure to replace `example.com` with your Sisense URL, and append your web server’s port if it differs from `80` or `443`. ::: You should see the following UI: ![Sisense JAQL editor UI with a query pane on the left and a JSON results pane on the right](./img/jaql1.png) You will write your query in the left pane, click **execute**and see the results (in JSON format) in the right pane. Another useful utility is the "Elasticube Fields API", which will show you the various dimensions that exist in your cube. You can access this API by navigating to this URL: (Where `Training` is the Elasticube’s name) You will only need the `id` property of each field to build your queries. ### Step 2: Retrieving a Dimension In this step, you will query the Elasticube for all the existing values of the `Customers.Country` dimension. The result will be a list of unique countries (no duplicates will be shown). Each possible value of a dimension is called a "member". Paste the following query to JAQL Runner’s left pane and click execute: ```json { "datasource": "Training", "metadata": [ { "dim": "[Customers.Country]" } ] } ``` You should see a result in the right pane, under the `values` property. Note that your JAQL query has two main properties: `datasource`and `metadata`. `datasource` tells the Sisense Web Server which Elasticube it should query, and can be a simple `string` name (to query a local Elasticube) or an object. `metadata` describes the query itself, and is the main property you will work with when writing JAQL queries. You will also notice that you represented the field ("dimension") you wished to retrieve using an object with a single property, `dim`, the value of which is the dimension’s ID. You do not need to specify the exact location of this dimension, its type, or any other information - even though when looking at queries executed by the Sisense UI you might see this additional information included. ### Step 3: Adding a Simple Aggregation In this step, you will add a simple aggregation to your query; a ‘count unique’ of customers. Since the query already contains the "Country" dimension, it will be used to group the result, so your query will return a list of countries and how many customers exist in each of them. Add the following object to your query’s metadata array: ```json { "dim": "[Customers.CustomerID]", "agg": "count" } ``` Notice that just like the first object in the array, the `dim`property specifies the ID of a dimension. However, this object has an additional property, `agg` which specifies which aggregation to use on this dimension, turning it into an aggregation ("measure"). In this case, the chosen aggregation is "count" which will count how many unique values ("members") the `CustomerID` dimension has. Execute the query, and you should see a result similar to this: ![JAQL editor results pane showing the aggregated query result in JSON format](./img/jaql2.png) Notice that each member of the `values` array is an array in itself - and each member of the sub-array is an object containing the value appropriate for one of the metadata items in the query. The first item is a value from the countries dimension, and the second - its corresponding "count of customerID". Thus, the "values" array’s members can be referred to as table rows, and the inner array’s members as each row’s cells, or columns. ### Step 4: Adding a Filter In this step, you will add a simple filter to the query, so that it returns only countries within the Americas. You will specify the countries you wish to include, so this filter will be applied to the existing dimension in your query ("country"). Add the following property to your "country" metadata object: ```json "filter": { "members": [ "Argentina", "Canada", "USA", "Mexico", "Venezuela", "Brazil" ] } ``` Note that the `filter` property is an object, which defines what kind of filtering you want to apply to a dimension. Filter objects can get quite complex, and contain nested JAQL metadata objects. In this case, a filter called "members" is used, which simply defines which values should be included in the query result. Don’t forget to run the query and ensure the results now only include the countries specified! ### Step 5: Adding a Background Filter In this stage, you will add another filter to the query - this time, on a dimension that isn’t used in the query. A filter like that is called a "scope filter". This is the type of filter applied by the Sisense UI when filters are added to a widget or dashboard. You wish to reduce the result set to include only countries in which customers bought Tofu, so you’d like to add a filter for the `ProductName` dimension, like so: ```json { "dim": "[Products.ProductName]", "filter": { "members": ["Tofu"] } } ``` However, if you add this object to the metadata collection you’ll find that it adds another field/column to the query result, "ProductName". If you included more than 1 member in the filter, you’d also find out that results are now grouped by `ProductName` as well as `Country`, splitting up your result set in an undesirable manner. Instead, you’d like to filter the data by this dimension, but not include it in the query at all. To do so, add the following property to the `ProductName` metadata object: `"panel":"scope"` Run the query now - you should see the result set is now only countries in the Americas where some customers bought Tofu, and a count of such customers for each country. ### Step 6: Adding a Measure Filter In this step, you will add a filter based on a measure in your query. You wish to only show countries where more than 1 customer fits the various conditions set up so far. To do so, you will apply a filter to the measure metadata item ("count unique customerID"). Add the following filter object to your measure: ```json "filter": { ">": 1 } ``` Run the query - you should now see the countries that had only 1 customer disappear from the result set. ### Step 7: Adding a Formula In this step, you will learn how to add more complex aggregations to your query using formulas. You will calculate, for each country, the yearly average of orders placed. To do so, you will need a formula like this: `Average of (count unique OrderID) per OrderDate year` As you can see, you will need to use two dimensions in this query (OrderID and OrderDate) as well as one simple aggregation (count unique) and one function (Average with grouping). Add the following object to your query’s metadata collection: ```json { "formula": "AVG([OrderDateYears], [CountOrderID])", "context": { "[OrderDateYears]": { "dim": "[Orders.OrderDate (Calendar)]", "level": "years" }, "[CountOrderID]": { "dim": "[Orders.OrderID]", "agg": "count" } } } ``` Note that unlike other JAQL metadata items so far, this one does not contain the `dim` property or any of the other properties you are familiar with. Instead, a formula is composed of two parts: the `formula`itself as a `string`, and a `context` object to represent various "tokens" found in the formula as JAQL objects. In this case, the formula uses the `AVG` function with a dimension to group by, and a numeric measure to apply the average to. The dimension is represented by the identifier `OrderDateYears` which is translated to the `OrderDate` dimension with `years` level. The measure is represented by the identifier `CountOrderID` and is translated to the dimension `OrderID` with the aggregation `count` applied to it. Run this query and you should now see 3 cells per each row of your resultset - the third being the result of this formula, calculated for each country. ### Step 8: Using Additional Properties At this stage, your query should look like this: ```json { "datasource": "Training", "metadata": [ { "dim": "[Customers.Country]", "filter": { "members": [ "Argentina", "Canada", "USA", "Mexico", "Venezuela", "Brazil" ] } }, { "dim": "[Customers.CustomerID]", "agg": "count", "filter": { ">":1 } }, { "dim": "[Products.ProductName]", "filter": { "members": ["Tofu"] }, "panel": "scope" }, { "formula": "AVG([OrderDateYears], [CountOrderID])", "context": { "[OrderDateYears]": { "dim": "[Orders.OrderDate (Calendar)]", "level": "years" }, "[CountOrderID]": { "dim": "[Orders.OrderID]", "agg": "count" } } } ] } ``` While most of your query building revolves around the metadata property of JAQL, there are some other useful properties you might need. Below are a few of them, which you can add to your query and execute to test them out: 1. Try adding the property `"format" : "csv"` to the query object’s root (parallel to `metadata` and `datasource`) - executing the query will return data in CSV format, instead of JSON 2. Try adding the properties `"count": 1` and `"offset": 0`  to the query object’s root which will return only the first row. This is useful for paging and lazy-loading of data. 3. Try adding the property `"sort": "asc"` to one of your metadata objects, which will sort the query results by that dimension or measure. ## Part 2: Using JAQL for Custom Filters In this part of the tutorial, you will utilize your new understanding of JAQL to achieve more advanced filtering on a dashboard, that you wouldn’t otherwise be able to achieve using the Sisense UI. Open the example dashboard "JAQL-Training-1" attached to this tutorial in order to begin. You will see an empty pivot table and a filter on OrderDate, which is why the pivot is empty. Your goal is to filter the dashboard for data from the last 8 years, which isn’t one of the options in the Time Frame filter UI. ### Step 1: Viewing the JAQL of a Filter Click the **Edit** button on the OrderDate dashboard filter: ![Edit button on the OrderDate dashboard filter](./img/jaql3.png) You can see that the maximum number of years back the UI allows is 2 ("Last 2 Years"), but you would like to increase that range and display data from the past 8 years, for example. The first stage to do so is finding the JAQL filter created by this UI. Click the Advanced tab, and you will see the following: ![Advanced tab of the filter showing the underlying JAQL with the "last" keyword](./img/jaql4.png) Now you can determine the way the filter works via the "last" keyword. JAQL indicates the filter is for past years. "count" and "offset" work exactly as you would expect in the context of paging, and in this case an offset of 0 and count of 1 means data from the current year. ### Step 2: Constructing a Custom Filter To construct a custom filter, you only need to modify the JAQL extracted from the time frame filter - by changing the "count" property to 8, you will retrieve data from the past 8 years. However, in some cases you will need to perform a more elaborate modification of the JAQL. For this purpose, the JAQL Reference will be helpful. You can use some capabilities that aren’t present in the UI, such as composite filters using the "and" and "or" keywords. An important note to remember is that this method applies a filter to a specific dimension, and the Advanced tab only edits the "filter" property of a JAQL metadata object - you cannot combine several dimensions using the "Advanced" tab. ### Step 3: Applying Custom Filters Use the "**Test**" button to execute a simple 1 dimensional query using the filter in the "Advanced" tab’s left textbox. You will see up to 10 results, representing which members of the dimension will be retrieved with this filter. Once you’re satisfied with the result, click **OK** to create the filter, and it will be applied to the dashboard or widget. Note that since the filter is a custom one, it has no UI to represent it, and will appear on your filter pane as an empty panel, like so: ![Custom JAQL filter shown as an empty panel in the dashboard filter pane](./img/jaql5.png) ## Part 3: Using JAQL Queries in Scripts In this part of the tutorial, you will learn how to extract the JAQL queries behind various widgets on your dashboard, and a simple method of running JAQL queries from a script and parsing the result. This is a powerful skill with many different uses - you could execute JAQL yourself for advanced cases of embedding (writing your own visualizations with Sisense data, developing mobile apps with Sisense data), to log or capture the state of various metrics over time, for various automation purposes, and so on. ### Step 1: Extracting JAQL from a widget Open the attached dashboard called "JAQL-Training-2". You will find an Indicator from which you will extract the underlying JAQL query. Follow these steps: 1. Edit the widget. 2. Open your browser’s developer console (usually by pressing F12). 3. Type in the following code in the console: prism.debugging.GetJaql(prism.activeWidget) You can now copy the JAQL into a JSON editing tool such as [JSON Editor Online](https://jsoneditoronline.org/). ### Step 2: Setting Up and Authentication **Note**: This example is written in Node.js. For other languages such as Python, implementation will vary slightly. Create a new Node.js project, and implement Sisense API authentication by following the instructions in our [Using the REST API Tutorial](/guides/restApi/using-rest-api.html) The Node.js code below is a simple example of this implementation using the `request-promise` and `querystring` npm modules. ```js // Import Modules const rp = require('request-promise'); const querystring = require('querystring'); /** * Get Sisense API token */ const authenticate = (username, password) => { const data = querystring.stringify({ username, password }); const options = { url: "https://example.com/api/v1/authentication/login", method: "POST", headers: { "content-type":"application/x-www-form-urlencoded", "Content-Length": Buffer.byteLength(data) }, body: data }; return rp(options).then((res) =>{ const response = JSON.parse(res); token = response.access_token; return token; }).catch((err) => { console.error("An error has occurred attempting API call to the authentication/login endpoint."); throw err; }); } ``` ### Step 3: Executing the Query The Sisense UI executes JAQL queries via the REST API endpoint: * On Sisense for **Windows**: * On Sisense for **Linux**: Now that you have extracted a JAQL from an existing widget, you can simply execute it by sending it as the payload. Don’t forget to include the API token retrieved in the previous step. The Node.js code below is a simple example of a JAQL request to a Sisense for Windows server: ```js const runJaql = (jaql, cube, token) => { const options = { url: "https://example.com/api/elasticubes/"+cube+"/jaql", method: 'POST', headers: { "Content-Type": "application/json", "Authorization": 'Bearer ' + token }, body: JSON.stringify(jaql); }; return rp(options).then((data) => { try { return JSON.parse(data); } catch (e) { return data; } }).catch((err) => { console.error("An error has occurred attempting API call to JAQL endpoint."); throw err; }); } ``` ### Step 4: Parsing the Result Running the runJaql function from the previous step will return a JavaScript Promise which, if the HTTP call is successful, resolves to a response JSON object which should look like this: ```json { "headers": [ "Average Orders Per Customer" ], "datasource": { "fullname": "LocalHost/Training", "revisionId": "f07d89ab-1313-4fff-8f77-52b921f2de76" }, "metadata": [ { "jaql": { "type": "measure", "formula": "AVG([99CFE-E7A], [AD2EA-8D0])", "context": { "[AD2EA-8D0]": { "table": "Orders", "column": "OrderID", "dim": "[Orders.OrderID]", "datatype": "numeric", "merged": true, "agg": "count", "title": "# of unique OrderID" }, "[99CFE-E7A]": { "table": "Customers", "column": "CustomerID", "dim": "[Customers.CustomerID]", "datatype": "text", "merged": true, "title": "CustomerID" } }, "title": "Average Orders Per Customer" }, "format": { "mask": { "type": "number", "abbreviations": { "t": true, "b": true, "m": true, "k": false }, "separated": true, "decimals": "auto", "isdefault": true }, "color": { "color": "#00cee6", "type": "color" } }, "source": "value", "handlers": [ {}, {} ] } ], "values": [ { "data": 4.744186046511628, "text": "4.74418604651163" } ] } ``` There are several bits of information you can extract from this response and use in various scenarios, from data sampling, through automation, and up to advanced embedding. Here are a few examples: * The `values` property contains the actual data (query results). In this case, as the Indicator only returns 1 or 2 measures (numbers) - `values[0]` and `values[1]` will represent those values. In other cases where dimensions are also involved, the "values" collection will be a matrix (array of arrays) where values\[n] represents the n-th row and each object in it is a cell. * The `headers` property contains an array of table headers, based on the various JAQL object’s titles or dimensions. It can help you discern which object represents which "column" and in rendering the results into a UI or other data format, such as CSV. * The `metadata` property contains metadata on the query results - JAQL objects representing the various columns in the result set. Note that it does not contain all of the fields in the query - only those that end up in the result set. It contains useful information such as each column’s unique formatting configuration. --- --- url: 'https://developer.sisense.com/guides/restApi/index.md' --- # Sisense REST API ## What is the Sisense REST API? Sisense provides advanced users and developers a RESTful web API to most of its server functionalities, from user management to manipulating datamodels, dashboards & widgets. To get started with the Sisense REST API, refer to [Using the Sisense REST API](./using-rest-api.md) ## What can I use the Sisense API for? These are a few examples of common use-cases for the REST API: * Use Sisense data in your application or website\ *For example: you could implement a widget in your own corporate website that takes data from Sisense using the JSON Analytical Query Language (JAQL) or standard SQL and the query API.* * Automate a process\ *For example: you could write a script that adds multiple users from a CSV file, using the users API.* * Implement your own UI on top of Sisense\ *For example: you could develop an elaborate dashboard navigation page to function as a "home page" for your organization, using the dashboards API.* ## API Versions The Sisense REST API currently supports 3 API Versions, represented as part of the API endpoint path: These versions were created as a result of changes in the product features & API standards over time, and therefore do not contain the same set of endpoints and capabilities - for instance, a feature added recently will be represented in API v2, but will not have corresponding endpoints in the two older API versions. ### Which API version to use In most cases, the endpoint you need will only exist in one of the API versions. For example, Datamodel schema manipulation is only available via the `v2` API. In case the capability exists in two (or more) API versions, it is recommended to use the newest one (highest version number). ### How APIs change over time * Incremental changes to APIs that are non-breaking, such as adding new optional parameters or supported parameter values, are implemented on the most recent API version. * Significant reworks of an API usually result in either a new API version or the migration of an existing older API to a new version while keeping the original APIs intact to avoid breaking changes. * APIs that have been upgraded/migrated to a newer API version will remain supported as long as they are in wide use. * Once most use cases have been updated to use the new APIs, the older endpoints will be marked as deprecated. * Deprecated APIs will remain active for several major versions to ensure all users have the opportunity to update their code. --- --- url: 'https://developer.sisense.com/guides/restApi/custom-data.md' --- # Using the Custom Data API ## Intro While Sisense Add-ons allow for a wide range of customizations, they are limited in their abilities by being client-side only. In some cases, additional data required for an Add-on's functionality can be attached to existing entities within the Sisense application database, however, this approach isn't always applicable. Sisense's Custom Data REST API is a tool developed specifically to eliminate this limitation, by providing an easy-to-use method for storing and accessing any kind of JSON-serializable data, allowing your Add-ons to transcend beyond the scope of a user's browser session and persist their data or settings across the entire system. ### Sample Use Cases * Tagging Dashboards, ElastiCubes, Users etc. with a custom status, to be displayed using an icon or tooltip to every user * Storing non-sensitive custom settings for individual users, that will persist no matter where the user logs in from (unlike browser local data collections or cookies), such as whether the user as accepted a custom terms & conditions popup * Custom bookmarks for user's favorite dashboards ## What is the Custom Data API? The Sisense web application uses an internal application database to store various entities and metadata, such as your users, dashboards, and widgets. The Custom Data API provides a safe and easy way of accessing a special, dedicated collection (table) within the application database where you can store any JSON objects, query them and update them. ::: warning Note The collection is shared across the entire system and is accessible to any user with a valid cookie or API token. As such, it is not intended to store sensitive or personal data. ::: ## Custom Data API Endpoints The Custom Data API is exposed via the Sisense `v1` REST API, at the path `/api/v1/custom_data` and provides the following endpoints for reading, creating, editing and deleting objects from the collection: ### Reading Data #### List all *Endpoint:* Returns all available objects in the Custom Data collection #### By query *Endpoint:* Returns all available objects in the Custom Data collection that match the provided query **Arguments** | Name | Type | Data Type | Required | Example | |---------|-------------|-----------|----------|-------------------------------------| | `query` | Query Param | `string` | **Yes** | `{"someProperty":{"$exists":true}}` | ### Creating Data *Endpoint:* Adds a single JSON object to the Custom Data collection. This endpoint requires a request body to be present, however there is no pre-defined structure and the JSON provided will be stored in the collection entirely. **Arguments** | Name | Type | Data Type | Required | Example | |------|------|-----------|----------|----------------------------------| | N/A | Body | `JSON` | **Yes** | `{"someProperty": "some value"}` | ### Updating Data *Endpoint:* Updates all objects in the Custom Data collection that match provided query. This endpoint expects a JSON request body with two arguments, the query and the new data. **Arguments** | Name | Type | Data Type | Required | Example | |-------------------|------|-----------|----------|-------------------------------------| | `queryForSearch` | Body | `JSON` | **Yes** | `{"someProperty":{"$exists":true}}` | | `objectForUpdate` | Body | `JSON` | **Yes** | `{"someProperty": "some value"}` | ### Deleting Data *Endpoint:* Deletes all objects in the Custom Data collection that match provided query. The endpoint expects a JSON request body representing the query to delete by. **Arguments** | Name | Type | Data Type | Required | Example | |------|------|-----------|----------|-------------------------------------| | N/A | body | `JSON` | **Yes** | `{"someProperty":{"$exists":true}}` | ## Custom Data API Queries As shown in the previous section, the Custom Data API relies on queries to retrieve and manipulate entries in the Custom Data selection. For maximum flexibility and ease of use, the syntax for these queries follows a *similar* syntax to what's used in the `query` parameter of MongoDB's `find()` function. You can find documentation on the `find()` function's syntax [here](https://docs.mongodb.com/manual/reference/method/db.collection.find/), and the syntax reference for the `query` format [here](https://docs.mongodb.com/manual/reference/operator/query/). *Note that only the `query` part is supported - the Custom Data API does not support a `projection` at this time.* ### Examples #### Objects where a property exists ```js { "someProperty" : { "$exists":true } } ``` Will select all objects in the collection that have a property called `someProperty` at their root level. #### Objects with a specific property value ```js { "someProperty" : "someValue" } ``` Will select all objects in the collection that have a property called `someProperty` with the exact value `"someValue"` at their root level. ## Demo To demonstrate how the Custom Data API can be used to create advanced, persistent Add-ons, consider the following case: > In my organization, many dashboards are used and shared to many users. Sometimes, dashboards need to be removed when they are no longer relevant or accurate, however simply deleting (or "un-sharing") a dashboard can lead to confusion with users who may have been relying on this dashboard. Instead, I would like the ability to mark dashboards as deprecated, so that other users get a clear visual indication that soon the dashboard will be removed. Then, they can ask for access to the alternative or make their own copy of it, reducing frustration. A very simple implementation is suggested: * Add a "deprecated" toggle switch to the dashboard menu, so editors can switch between normal and deprecated state * When a dashboard is deprecated, its name should be grayed out and have a strike through in the navigation panel for any user who can see that dashboard ![An Image](./img/custom-data-1.png "Custom Data") ![An Image](./img/custom-data-2.png "Custom Data") There are 2 reasons to use Custom Data API and not attach the `deprecated` flag to the `dashboard` object: 1. Once the deprecated state is set, it will be immediately visible to all users, without republishing the dashboard which may not be desirable 2. Adding custom properties to existing Sisense entities is generally discouraged. To implement this solution, the following steps are required: 1. Add a "toggle" type menu item to the dashboard menu, using the `prism.on("beforemenu")` [global event](/guides/customJs/jsApiRef/prism/#beforemenu) 2. Create a CSS class to apply to the dashboard items in the navigation bar, with the desired text styling 3. Create logic to apply the CSS class to the applicable dashboards (in the example, an AngularJS Directive is used) 4. Write functions to wrap the interaction with the Custom Data API to update and retrieve a collection of deprecated dashboards **The solution is implemented using an Add-on which can be [downloaded here](https://drive.google.com/open?id=18hGCgaQh4ifTkqR3t7x1W-8lQCCLy6Id).** The Add-on contains 2 main files: 1. `index.6.js` adds the menu item, applies the style to menu items, and manages the feature's flow 2. `custom-data.6.js` implements methods to add, remove and retrieve deprecated dashboards from the Custom Data API using `jQuery.ajax()` to execute authenticated HTTP requests. Read the Add-on's `readme.md` file for usage instructions. --- --- url: 'https://developer.sisense.com/guides/restApi/data-security.md' --- # Using the Data Security API *Automating row-level security using Sisense REST API* ## Abstract This article will guide you through the process of automating your row-level security management using the Sisense REST API. ## When To Use Data Security API Often, managing data security rules can be done adequately and efficiently through the Sisense Admin page manually. However, with an increased scale or complexity of a Sisense deployment, this task can become time consuming and prone to human error, justifying the investment in automation. Such cases include: * When there are too many users or groups to manage * When users or groups are added and removed frequently * When users are added automatically and should have immediate access to dashboards and data * When users' permissions need to change frequently This is especially true when several of the above factors are combined. ## How Row-level Security Works Data Security in Sisense is defined as a list of rules associated to a specific, single Elasticube. These rules are stored in the Sisense Application Database and are evoked whenever a query is run on the associated Elasticube, narrowing down the query's result-set to only the allowed data, before the results are sent to the client. In essence, Data Security applies additional background constraints that are generated on the Sisense Server (and not passed via the HTTP request) to a query's `WHERE` clause so that any and all associated data is filtered, based on user context only and with no consideration of where the query is sent from, resulting in rules that cannot be overridden. A data security rule is comprised of three distinct entities: 1. A Sisense User or Group (aka "the party") 2. A column (field/dimension) along with the Elasticube and Table it belongs to 3. One or more values (members) of the column to which the party is allowed access ![Diagram of a data security rule: a party, a column, and the allowed values](./img/image2019-1-14_16-59-37.png "Sisense Developers > Data Security API") For each Elasticube, once a user has any security rules applied to them, Sisense will limit query results to data associated with the specified values in the rule across all linked tables in the schema. ::: tip More Information For further information, please see the [Row Level Security documentation](https://docs.sisense.com/main/SisenseLinux/data-security.htm) ::: ## Designing Your Data Security Strategy There are several decisions that must be made while designing a Data Security approach and automation. Covered below are the decisions related to the technical aspect of the task, and not the data or business aspect (such as *which dimension should data security apply to?*). ### Rule Application Scope *Should rules be applied to individual users or groups?* Deciding whether to apply rules to individual users or groups depends mostly on how diverse the settings are for each user. For example: * A small company/department with few employees, using Sisense internally, might need to give access to different areas of their Elasticube to each user. As there are few rules to set, and they diverge significantly, it would be acceptable to assign rules to individual users. * A SaaS company using Sisense as an OEM with multi-tenant data in each Elasticube, using Data Security to segregate tenants, would likely have multiple users per tenant and thus would be better off applying rules to groups representing the tenants. ### Default Access *Should users/groups see all or none of the data by default?* In most cases, it is preferable to set the default rule to "forbid all", so that a user that isn't assigned any rules or groups with rules will not be able to see any data. While this approach has the disadvantage of users being unable to see data should their rule assignment go wrong, it is the safer approach that avoids exposure of data in the very same case. When dealing with sensitive data, such as PII and PHI, always choose this approach. Sometimes, when dealing with non-sensitive data and when limitations need to apply only to a small portion of users, it would be beneficial to set the default to "allow all", meaning that any user can see the entire data set unless a limitation was explicitly applied to them or one of the groups they belong to. This approach can be useful when, for example, most of a company's employees should have access to the same data, except for a handful of contractors or external users. ### Automation Timing *When should Data Security automation scripts run?* Depending on the Data Security scope chosen, timing the application of Data Security rules changes significantly: * When applied to individual users, data security rules should normally apply as soon as a User entity is created. This can be done by running Data Security automation as part of user provisioning, which is normally automated as well and often triggered by a user being added to a database, an API call, or a manual trigger of some sort. * In some cases, when using a "forbid all" default strategy and when it is acceptable for users to not have access to data immediately after creation, this process could be relegated to a scheduled batch job. This is rarely recommended or required. * When applied to groups, data security should be applied when the group is created, and based on the method of group creation. The timing is less crucial in the case of groups that are created empty and are not assigned to users immediately, in which case Data Security needs to be defined only before the first users are assigned to the group. * For Data Security assigned to groups, the critical component is shifted from the Data Security automation to the Group assignment automation - users must be assigned to groups at the right time to ensure they have access to their data. * In both cases, changes to Data Security must come into consideration. Should a user's or group's data access permission change, such as when an employee changes position in the company or a tenant buys out another tenant, automation must re-run in some form to reflect these changes in the Data Security rules. For this reason it is recommended to ensure Data Security automation scripts are either idempotent or aware of current vs. desired state. ## Using The Data Security API ::: warning Datamodel Types While most of this tutorial applies to all types of Datamodels, please note that the endpoints and payloads differ slightly for `extract` type Datamodels ("Elasticubes") and `live` type Datamodels. ::: ### Authentication See parent article - [Using the REST API](../restApi/using-rest-api.md) ### For Extract Datamodels #### Endpoints *All of these endpoints are in the `v0.9` REST API version.* | Method | Path | Purpose | |----------|------------------------------------------------------------------------|--------------------------------------------------------| | `GET` | `/api/elasticubes/{server}/{elasticube}/datasecurity` | Get all rules for a cube | | `GET` | `/api/elasticubes/{server}/{elasticube}/datasecurity/{table}/{column}` | Get all rules for a dimension of a cube | | `GET` | `/api/elasticubes/{server}/{elasticube}/{user}/datasecurity` | Get all rules for a cube & user | | `POST` | `/api/elasticubes/datasecurity` | Create rules (bulk - multiple cubes, users and values) | | `POST` | `/api/elasticubes/{server}/{elasticube}/datasecurity` | Create rules for a cube (bulk - multiple users/values) | | `PUT` | `/api/elasticubes/datasecurity/{id}` | Update a rule by ID | | `DELETE` | `/api/elasticubes/{server}/{elasticube}/datasecurity` | Delete all rules for a cube's dimension | | `DELETE` | `/api/elasticubes/datasecurity/{id}` | Delete a specific rule | #### Rule Schema This is a generic description of a "rule" object as it is retrieved and stored by the Data Security API. While the exact properties may change depending on the endpoint and action performed, understanding which attributes and entities are part of Data Security rules will help you use these APIs more efficiently. **Object structure:** ```json { "_id": "string", "table": "string", "column": "string", "datatype": "string", "members": [ "string" ], "allMembers": false, "exclusionary": false, "shares": [ { "party": "string", "type": "string" } ] } ``` **Main object fields:** | Name | Type | Required | Description | |------------------|------------|-----------------------|------------------------------------------------------------------------------------------------------------| | `_id` | `string` | Sometimes1 | Unique identifier | | `server` | `string` | Sometimes2 | Address of the server hosting the Elasticube | | `elasticube` | `string` | Sometimes2 | Elasticube name | | `table` | `string` | **Yes** | Table name | | `column` | `string` | **Yes** | Column (dimension) name | | `datatype` | `string` | **Yes** | Column data type (either `text` or `numeric`)4 | | `members` | `string[]` | Yes3 | List of values the parties are allowed to access | | `allMembers` | `boolean` | Yes3 | Should rule apply to all of a column's possible values | | `exclusionary` | `boolean` | No | When set to `true`, the rule is "reversed" & the values will be hidden from the users. Defaults to `false` | | `shares` | `object[]` | **Yes** | List of parties (Users & Groups) to whom the rule applies ([See Details](#shares-object-fields)) | | `shares.*.party` | `string` | No5 | UUID/OID of the User or Group entity | | `shares.*.type` | `string` | **Yes** | Party type (either `user`, `group`, or `default`) | **Notes:** **1** This field will be returned with any `GET` request; It will be automatically assigned by `POST` requests and should not be specified; It is required for `PUT` and some `DELETE` requests. **2** Some API endpoints don't require the `server` and `elasticube` properties to be specified in the payload, as they are present in the API path. **3** The properties `members` and `allMembers` are mutually exclusive - only one of them is required. When `allMembers` is specified, `members` will be ignored. Note that the field `allMembers` is required, and when not in use the value needs to be `null` and not `false`. **4**Date dimensions are not supported for Data Security rules **5** The `party` attribute is not required when `type=default` and is required when `type=user` or `type=group`. **All** All `string` type fields are case sensitive **Elasticube Sets** When applying Data Security to an elasticube set, use the set's name as the `elasticube` attribute and the term `set` as the `server` attribute. This applies both to properties of the payload and to parts of the API URL path or query parameters. ### For Live Datamodels #### Endpoints *All of these endpoints are in the `v1.0` REST API version.* | Method | Path | Purpose | |----------|------------------------------------------------------------------|------------------------------------------------------------------| | `GET` | `/api/v1/elasticubes/live/{title}/datasecurity` | Returns the data security rules set up for a live Datamodel | | `POST` | `/api/v1/elasticubes/live/{title}/datasecurity` | Creates data security rules for a live Datamodel | | `DELETE` | `/api/v1/elasticubes/live/datasecurity/{dataSecurityId}` | Removes a data security rule by ID | | `DELETE` | `/api/v1/elasticubes/live/{title}/datasecurity/{table}/{column}` | Removes the data security rules for a column of a live Datamodel | #### Rule Schema This is a generic description of a "rule" object as it is retrieved and stored by the Data Security API. While the exact properties may change depending on the endpoint and action performed, understanding which attributes and entities are part of Data Security rules will help you use these APIs more efficiently. **Object structure:** ```json { "_id": "string", "fullname": "string", "table": "string", "column": "string", "datatype": "string", "members": [ "string" ], "allMembers": false, "live": true, "shares": [ { "partyId": "string", "type": "string" } ] } ``` **Main object fields:** | Name | Type | Required | Description | |--------------------|------------|-----------------------|--------------------------------------------------------------------------------------------------| | `_id` | `string` | Sometimes1 | Unique identifier | | `fullname` | `string` | **Yes** | Datamodel name in the format: `live:my-datamodel` (always has a `live:` prefix) | | `table` | `string` | **Yes** | Table name | | `column` | `string` | **Yes** | Column (dimension) name | | `datatype` | `string` | **Yes** | Column data type (either `text` or `numeric`)4 | | `members` | `string[]` | Yes3 | List of values the parties are allowed to access | | `allMembers` | `boolean` | Yes3 | Should rule apply to all of a column's possible values | | `live` | `boolean` | Yes | Must always be `true`. | | `shares` | `object[]` | **Yes** | List of parties (Users & Groups) to whom the rule applies ([See Details](#shares-object-fields)) | | `shares.*.partyId` | `string` | No5 | UUID/OID of the User or Group entity | | `shares.*.type` | `string` | **Yes** | Party type (either `user`, `group`, or `default`) | **Notes:** **1** This field will be returned with any `GET` request; It will be automatically assigned by `POST` requests and should not be specified; It is required for `PUT` and some `DELETE` requests. **2** Some API endpoints don't require the `server` and `elasticube` properties to be specified in the payload, as they are present in the API path. **3** The properties `members` and `allMembers` are mutually exclusive - only one of them is required. When `allMembers` is specified, `members` will be ignored. Note that the field `allMembers` is required, and when not in use the value needs to be `null` and not `false`. **4**Date dimensions are not supported for Data Security rules **5** The `party` attribute is not required when `type=default` and is required when `type=user` or `type=group`. **All** All `string` type fields are case sensitive **Elasticube Sets** When applying Data Security to an elasticube set, use the set's name as the `elasticube` attribute and the term `set` as the `server` attribute. This applies both to properties of the payload and to parts of the API URL path or query parameters. ### Different Types of Rules With optional attributes, a Data Security rule can take on several forms. Below are the common types of rules used: #### Default Rule A default rule applies to all users for whom a specific user/group rule does not exist. Most commonly, it will be a "forbid all" rule, meaning that any user who does not have an explicit rule associated with them or their group, will be blocked from seeing any data linked to the dimension in question. This is achieved by creating a single `shares` object with `type: "default"` (defines this rule as a default rule to apply to all non-explicit parties) and setting `allMembers: false` so that the rule blocks access to all values of the dimension. In some (rare) cases, the Data Security strategy is to allow full access to all users *except* those with explicitly set limitations, aka an "allow all" rule. This is also done via the `default` rule, by setting `allMembers` to `true`. Note however that this is a less secure approach, as any user that has not been assigned an explicit rule or a group with an explicit rule will be able to see all available data. This approach can be valid when access needs to be limited only to a handful of users (for example, contractors and temps with a specific scope of work) and when the data in question is of low sensitivity (for example, non PII or PHI data). For more information on which access strategy to choose, please see [Designing Your Data Security Strategy](#designing-your-data-security-strategy). #### User/Group rule Normally, a user or group rule will be specific and explicit - it is by definition associated with an explicit & finite list of users/groups and it will have an explicit & finite list of values the associated parties are allowed to see. This is achieved by not specifying the `allMembers` attribute (setting its value to `null`), and instead providing an array of 1 or more specific values in the `members` attribute, as well as specifying 1 or more parties of type `user` or `group` in the `shares` attribute. *Note* that a single rule can support both multiple allowed values and multiple associated parties. ## Implementing Data Security Automation There are a few more decisions to make before writing the scripts to automate Data Security. ### Which Language to Automate With? * Generally, script languages that aren't compiled, such as JavaScript (Node.js), Python and PowerShell are preferable for automation. * Choose a language you and your colleagues are comfortable with, so that your various automation scripts can co-exist, share components (DRY), and be maintained by a wide group of engineers. ### Authentication Approach * To run Data Security automation you will need to authenticate, receive, and use an API Token as an Administrator level user. Only administrators can set Data Security rules. * You can create a dedicated Administrator user for automation purposes, or use the credentials of a real administrator. * You will need to choose whether to authenticate only once (and store the Token for all subsequent use), authenticate every time the script runs, or authenticate before every API call. With a dedicated API user, pre-authenticating and using the Token is a safer, better choice. Re-authenticating provides a way of handling possible password changes and other scripts re-generating the Token, but also slows down the process and adds complexity. * Whether using credentials or a token, it is recommended not to store these sensitive values as a hard-coded part of the script. Use a more secure method to store and access these values when the script runs, such as AWS EC2 Parameter Store. * It is recommended to check for authentication (ensure the Token is valid) before running the scripts, exiting the script cleanly if authentication fails. This can be done by executing a `GET` request to the `/auth/isauth` endpoint with the authentication header. ### Bulk operations * Applying rules can be done in bulk or individually. * Applying individual rules can result in many API calls, adding significant overhead to the process and load to the host system. However, by keeping each "transaction" small, it is easier to handle changes and concurrent API calls. * Applying the rules in bulk saves on API call overhead resulting in faster performance, however a single bulk call may not be sufficient; mind the maximum size of an HTTP request, as well as the time a call of that size can take and the risk of it failing. It might be beneficial to break large operations into multiple bulk calls. ## Code Example The example below is written in Windows PowerShell syntax, and makes the following assumptions: * The datamodel to which rules are applied is an `extract` type model (aka "Elasticube") * Using a static API token that is stored in AWS Parameter Store, that is retrieved using the AWS CLI * Data security rules are applied to user groups only (not individual users) * Taking group names as the input (requires an additional step to convert group name to ID, but is more user-friendly) * Adding all data security rules at once * Supports both separate Elasticubes and Elasticube Sets * One value associated per group *This code can easily be customized to your specific requirements, and is intended only as a demo of the process described in this article.* ```powershell ###################################################### # Data Security API Example # # ------------------------- # # Script demonstrating how the Sisense Datasecurity # # REST API can be invoked to create a full set of # # Group-based rules in a "White-list" configuration # ###################################################### <### Inputs ###> # Get the API token from AWS SSM Parameter Store $apiToken = (aws ssm get-parameter --region eu-west-1 --name sisense-api-token --query Parameter.Value --output text); # Properties of the Sisense web server $protocol = "https"; $url = "example.com"; $port = "443"; # A collection of Elasticubes to apply data security to, and which rules should be applied $datasources = @( @{ "name" = "Sample ECommerce_set"; "server" = "Set"; "fields" = @( @{ "column" = "Brand"; "table" = "Brand" "type" = "text"; "rules" = @( @{ "group" = "Customer1"; "value" = "Addimax WorldWide " } ) } ) } ); <### Internals ###> # Generic HTTP headers that apply to all API calls $headers = @{ "Authorization" = "Bearer $apiToken"; "accept" = "application/json"; "Content-Type" = "application/json;charset=UTF-8"; }; # Generic function to generate a valid API path for requests function Generate-URI { param($APIPath); "$protocol`://$url`:$port/api$APIPath"; } # Handler for fatal errors - exits the script function Be-Sad { param($Message); Write-Host ":( Error! $Message"; exit 1; } # Handler for non-fatal errors that can be skipped function Be-Apathetic { param($Message); Write-Host ":| $Message"; } # Handler for success messages function Be-Happy { param($Message); Write-Host ":) $Message"; } <### Script Flow Starts Here ###> # Ensure authentication (check if API token is valid) $RestError = $null; $response = $null; Try { $response = Invoke-WebRequest -Uri (Generate-URI -APIPath "/auth/isauth") -Method "GET" -Headers $headers; if(!($response.Content | ConvertFrom-Json).isauthenticated) { Be-Sad -Message "Invalid token"; } else { Be-Happy -Message "Authentication valid."; } } Catch { $RestError = $_; Be-Sad -Message $RestError; } # Create an empty array of rules to set $body = @(); # Start batch process - iterate over Elasticubes and the Fields of each Elasticube foreach ($cube in $datasources) { foreach ($field in $cube.fields) { # Define whitelist setting for current field (set default to no access) $body += @{ "server" = $cube.server; "elasticube" = $cube.name; "allMembers" = $false; "table" = $field.table; "column" = $field.column; "datatype" = $field.type; "shares" = @(@{ "type" = "default"; }); "members" = @(); }; # Create rules for each group foreach ($rule in $field.rules) { # Get group ID from provided group name $groupId = $null; $RestError = $null; $response = $null; Try { $response = Invoke-WebRequest -Uri (Generate-URI -APIPath "/v1/groups?fields=_id&name=$($rule.group)") -Method "GET" -Headers $headers; $groupId = ($response.Content | ConvertFrom-Json)[0]._id; if ($groupId -eq $null) { Be-Sad -Message "Can't find groupID for group $($rule.group)"; } Be-Happy -Message "Got group ID for group $($rule.group)"; } Catch { $RestError = $_; Be-Sad -Message $RestError; } # Define rule and add to collection $body += @{ "server" = $cube.server; "elasticube" = $cube.name; "allMembers" = $null; "table" = $field.table; "column" = $field.column; "datatype" = $field.type; "shares" = @(@{ "type" = "group"; "party" = $groupId; }); "members" = @($rule.value); }; } } } # When all rules have been defined, Apply all of them via one API call $RestError = $null; $response = $null; Try { $response = Invoke-WebRequest -Uri (Generate-URI -APIPath "/elasticubes/datasecurity") -Method "POST" -Headers $headers -Body ($body | ConvertTo-Json -Depth 50); Be-Happy -Message "Applied Data Security rules!"; } Catch { $RestError = $_; Be-Sad -Message $RestError; } ``` --- --- url: 'https://developer.sisense.com/guides/restApi/datamodels/index.md' --- # Using the Datamodel API The Datamodels API allows you to develop scripts and applications that create and modify Sisense Datamodels. This guide will walk you through the steps required to understand the API's structure and correct use, including a basic use-case example. The Datamodels API is fully RESTful and JSON-based, and is currently available on Sisense Linux versions starting from `L8.1`. ### Datamodel API Webinar ## Resources The primary resource (entity) this API deals with is called `Datamodel`, which contains a hierarchy of child-resources comprising your Datamodel's schema. See the diagram below depicting this object model structure. ![Object Model Diagram](https://www.plantuml.com/plantuml/png/SoWkIImgAStDuN99B4dCpKz9pL7GrRLJ0F6AKqlGH8DISn9BClFpkA3IOC6GnAISL2wkv9p4uc85XFfgBWM5_CmK89cNc9iAf4eg2q1K2aHhCP2ffwV7LGlN2H77gIyvFoylDHbaEsv386x1OqHEQMuUkYQuhy3w78kA4Ykj5AA1p4CB3Ys0MWGo3YfI0hO2mOSO2XM81Phga9gN0WnG0000 "Object Model Diagram") ### Datamodel The `Datamodel` resource is the root of the object model hierarchy. Its OID will be used throughout all actions related to Datamodels. A Datamodel can currently be of either `extract` or `live` type - mixed type Datamodels are not supported. ### Dataset A `Dataset` resource represents a single data source of your Datamodel - for example, if your Datamodel contains data from a mix of several CSV files and MySQL databases, each CSV file and each database will be represented by a Dataset entity. Accordingly, the Dataset entity must include a [connection](./connectors.html) to the corresponding data source. In `extract` type Datamodels, Datasets will be of either `extract` or `custom` type. In `live` Datamodels, Datasets will be of either `live` or `custom` type. ### Table A `Table` resource always belongs to a specific Dataset, representing various tables or collections in the data source. Table resources can be of either `base` or `custom` type. Tables of `base` type contain a collection of columns, telling Sisense how to represent the columns/properties of the original table in your datasource, and which ones. Tables of `custom` type will instead be built around the `expression` property, which is a SQL expression. Each Table is assigned a build behavior, specifying how and when the Table's data should change when a Build or Publish occur. Additionally, Tables may have supplemental configuration telling Sisense how to process the data from the original data source. Tables can also be hidden, so they cannot be queried directly, which is handy when they are only used as a base for a more elaborate or user-friendly custom Table. #### Column Table Columns are not represented as a resource in the RESTful sense, but they are an object contained within the `Table` resource and have their own specific structure you should be familiar with. A `Column` object represents a Column or Dimension in your Datamodel, and can take on one of three forms: 1. A normal Column that represents a column or field in your original data source 2. A custom (calculated) column, defined by a SQL expression in its `expression` property 3. An auto-generated Column in a **custom** Table, derived from the Table's SQL expression In all cases, Columns are described by various attributes such as their [data type](./data-types.html), size/precision (where applicable), name (which will be used to query the Column) and whether the Column should be visible/queryable or not. ### Relation A `Relation` resource represents a relationship between 2 or more Tables. Each Relation contains an array of Columns from different tables that should be connected. These Relations are used as the JOIN path when running queries across multiple Tables. ## Endpoints Each of the resource types described above except for Columns is represented by an API endpoint that supports CRUD (Create Read Update Delete). Generally, each endpoint is represented by a specific URI path according to REST architecture standards, that is comprised of the following components: 1. A host address, containing the protocol, IP/DNS and port. For example: `https://reporting.myapp.com:8081` 2. An API base path, containing the API version (this API exists in version `2.0`): `/api/v2` 3. The resource name, and sometimes `oid`: `/datamodels` or `/datamodels/1234-some-resource-id`) Child resources as seen in the diagram above, such as Tables, have a nested URI structure and are accessible via the endpoints and `oid`s of their parent resources.\ For example: `/datamodels/1234-some-resource-id/datasets/5678-other-resource-id`. With only a few exception, for each resource/endpoint the following operations are available: * **Read (List):** By using the HTTP method without a resource's `oid`, returning a list of matching resources * **Read (Get):** (aka `get by id`) By using the HTTP method and providing a specific resource's `oid`, returning that specific resource * **Create:** By using the HTTP method without a resource `oid`, creating a new resource of that type * **Update:** By using the HTTP method and providing a specific resource's `oid`, updating that specific resource's properties * **Delete:** By using the HTTP method and providing a specific resource's `oid`, removing that entire resource ## Creating a new Datamodel ### Creating a blank Datamodel object To create a new Datamodel, use the endpoint, with the following payload structure: ```JSON { "title": "My Datamodel", "type": "extract" } ``` The only required field for the payload is `title`, as `type` defaults to `extract`. ### Creating a Dataset To create a Dataset, you will need your Datamodel's `oid` as well as the `oid` of an existing connection to the corresponding data source. For details on creating new connections, refer to the [Data Connector Reference](./connectors.html) Use use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}`, with the following payload structure: ```JSON { "name": "My Dataset", "type": "extract", "connection": { "oid": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }, "database": "string", "schemaName": "string" } ``` #### Custom Datasets To create Custom Tables in your model, you will need to create a Custom type Dataset to contain it. A Custom Dataset is created just like any other Dataset, but with the `type` property set to `"custom"` and with no `connection` property provided, like so: ```JSON { "name": "My Custom Dataset", "type": "custom" } ``` ### Creating a Table To create a table, you will need the `oid` of your Datamodel and Dataset. You will also have to be familiar with your data source's schema, to be able to specify the columns that will be queried from it. Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}` and your Dataset's `oid` instead of `{DatasetId}`, with the following payload structure: ```JSON { "id": "OriginalTable", "name": "MyTable", "description": "My new table with 2 columns", "columns": [ { "id": "OriginalColumn1", "name": "MyColumn1", "type": 8 }, { "id": "OriginalColumn2", "name": "MyColumn2", "type": 18 } ], "buildBehavior": { "type": "sync" }, "configOptions": { // Some connector types, such as CSV, require additional configuration here. } } ``` In the example above, a `base` type table (default) called `MyTable` will be created to represent a table called `OriginalTable` in your data source. It will contain 2 columns, called `MyColumn1` and `MyColumn2` in your Datamodel, representing the columns `OriginalColumn1` and `OriginalColumn2` respectively in the original table in your data source. If you omit the `name` properties, they will default to the same name as the original table had (as specified in the `id` properties). Each Table also has a build behavior defined, applicable only to `exract` type Tables. Note that many additional properties are supported for both the `Table` and the `Column` objects, and this example only shows the very minimum required payload. #### Creating custom Columns When creating (or updating) a Table, the `columns` array can contain custom columns, that are not extracted/queried from the data source but rather calculated within Sisense. These columns vary slightly in structure from regular Columns. In addition to the fields used before, 2 additional fields are provided for custom Columns: 1. The property `isCustom` is set to `true` to define this Column as a custom Column 2. The property `expression` contains a SQL expression used to calculate the value of this Column for each row in the Table. The SQL expression will use the column identifiers as they are seen in the Datamodel itself, as specified in the `name` property of each column, and **not** the original names from the data source. Example: ```JSON { "id":"MyCustomColumn", "name": "MyCustomColumn", "type": 18, "expression": "select [MyColumn1] + ' ' + [MyColumn2]", "isCustom": true } ``` *For more information on custom Columns, see [Creating Custom Columns](https://documentation.sisense.com/latest/managing-data/transforming-data/add-custom-field.htm)* #### Creating custom Tables To create a custom Table, you will first need to create a custom Dataset. Refer to [Creating Custom Datasets](#custom-datasets). Once you have a custom Dataset's ID you can create custom Tables using the same endpoint as regular (`base`) tables, with a few differences: 1. Table's `type` is set to `custom` instead of `base` 2. An `expression` property is provided 3. The `columns` property is **not** provided Example: ```json { "id": "custom-table-1", "name": "custom1", "type":"custom", "description": "Custom table", "expression": "select 'UK' as Country, 'UK' as Code UNION\nselect 'Canada' as Country, 'CA' as Code" } ``` ### Creating a Relation To link 2 or more tables together, you will need the exact coordinates of the Columns you wish to connect through. Those coordinates are the `oid`s of the Dataset, Table and Column. Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}`, with the following payload structure: ```JSON { "columns": [ { "dataset": "", "table": "", "column": "" }, { "dataset": "", "table": "
", "column": "" } ] } ``` ## Modifying a Datamodel While the Datamodel entity itself has no editable fields, the various sub-entities do. Various manipulations can be performed via the REST APIs, such as: * Changing a dataset's connection * Renaming, hiding and showing tables and columns * Adding or removing tables, columns and relations ### Changing a Dataset's connection Datasets can be updated by using the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}` and your Dataset's `oid` instead of `{DatasetId}`. *Notes:* * Within the Dataset entity, **only the `connection` property can be modified.** by specyfying `oid` of the desired connection. For example, the following Dataset PATCH payload: ```json { "connection": { "oid": "3fa85f64-5717-4562-b3fc-2c963f66afa6" } } ``` ### Updating a Table Tables can be updated by using the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}`, your Dataset's `oid` instead of `{DatasetId}`, and your Table's `oid` instead of `{TableId}`. As operations can accept partial payloads, there are no strict requirements for which fields must be present in the body, but at least one property of the Table object must be present. Properties that are read-only will be ignored, while properties that do not belong to the Table object will result in an error. For example, the following payload: ```JSON { "name": "MyTable2" } ``` Will change the Table's name to `MyTable2`. Meanwhile, as the `id` property is unchanged, this Table will still represent the same corresponding Table in your original data source. However, any SQL expressions (such as those in custom Columns and custom Tables) will have to be updated as they *do* use the Table's `name`! #### Updating (or removing) a Table's Columns As Columns are not represented by their own API endpoint, any changes to a Table's Columns is done via the Tables endpoint's operation. Because of the way methods work, *properties provided in the payload are replaced, **not** merged!* This behavior means you MUST provide in the request's payload *all the columns you would like the Table to have after the operation, in their entirety* and not just the ones you have modified. The easiest way to do this is by using the corresponding request first, making the necessary changes, and sending that as the request payload for your update operation. *Special attention needs to be paid to existing Column's `oid` property - that's the way Sisense recognizes an existing Column vs a new one. If the `oid` property is missing, a new Column will be created!* For example, the Table created earlier in this guide contains 2 Columns: ```JSON { // ... rest of table object ... "columns": [ { "id": "OriginalColumn1", "name": "MyColumn1", "type": 8 }, { "id": "OriginalColumn2", "name": "MyColumn2", "type": 18 } ] // ... rest of table object ... } ``` Assume you wish to: 1. Add a new custom Column 2. Hide the first Column 3. Delete the second Column The payload for the request would be: ```JSON { // ... rest of table object ... "columns": [ { "oid": "", // as retrieved from the GET request "id": "OriginalColumn1", "name": "MyColumn1", "type": 8, "hidden": true }, { "id":"MyCustomColumn", "name": "MyCustomColumn", "type": 18, "expression": "select [MyColumn1] + ' is cool!'", "isCustom": true } ] // ... rest of table object ... } ``` And will result in the following: 1. The new custom Column does not yet have an OID, and will be created 2. The first Column, with all the properties it had including `oid` will be recognized as an existing Column to update. It will be updated so that `hidden` is now `true` 3. The last column was omitted from the payload and will thus be deleted ### Updating Relations You can update a single Relation object to: * Link new Columns to an existing Relation, such as when adding a table that uses the same key * Remove a Column from a relation, such as when deleting a Table or a Column * Change which of a Table's Columns is used to link it to other Tables *Note that any Relation object must have at least 2 Columns specified at any time, or the Schema becomes invalid.* In a similar manner to the process for updating a Table's Columns described above, when updating a Relation object you must provide a payload containing **all** columns that should be kept or added, and not just the ones you have modified. The easiest way to do this is by using the corresponding request first, making the necessary changes, and sending that as the request payload for your update operation. Any Column you omit from the `columns` property in the payload will be removed from the Relation and become unlinked. Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}` and your Relation's `oid` instead of `{RelationId}`, with the same payload structure as a : ```JSON { "columns": [ { "dataset": "", "table": "
", "column": "" }, { "dataset": "", "table": "
", "column": "" } ] } ``` ### Deleting Resources All endpoints support deleting the resources they represent, by `oid`, using the HTTP method, but not all operations are achieved the same way. #### Deleting an entire Datamodel Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}`, to delete an entire Datamodel. Note that this will remove the Datamodel from the system entirely, and it will no longer be possible to query it or use it for Dashboards. Additionally, Dashboards using the Datamodel are not removed automatically and need to be migrated to a new datasource or removed explicitly. #### Deleting a Dataset Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}` and your Dataset's `oid` instead of `{DatasetId}`, to delete a specific Dataset from your Datamodel. Note that while the Dataset and all Tables within it will be deleted, you must remove any references to them from Relations as well as any custom SQL expressions. This operation may impact Dashboards that use any of the Tables removed by this operation, and they will need to be updated accordingly. #### Deleting a Table Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}`, your Dataset's `oid` instead of `{DatasetId}`, and your Table's `oid` instead of `{TableId}`. Note that you must remove any references to the deleted Table from Relations as well as any custom SQL expressions. This operation may impact Dashboards that use the Table removed by this operation, and they will need to be updated accordingly. #### Deleting a Column As Columns are not represented by an endpoint of their own, removing Columns is achieved by updating the Table object, as described in [Updating (or removing) a Table's Columns](#updating-a-table). #### Deleting Relations To delete an entire Relation, removing the link between all participating tables, Use the endpoint, including your Datamodel's `oid` instead of `{DatamodelId}` and your Relation's `oid` instead of `{RelationId}`. To remove one or more Columns from a Relation without deleting the rest, use the process described in [Updating Relations](#updating-relations). ## Building/Publishing a Datamodel To make a Datamodel queryable and its data available to users, it needs to be built or published. * `extract` type models (formerly called "Elasticube") are built, extracting data from all included sources into the proprietary Sisense columnar database (Elasticube) * `live` type models aren't built, instead the model is "published", setting the structure of the queryable model In both cases, the action is performed via the `/api/v2/builds` endpoint. ### API Structure The `/builds` endpoint is constructed around a "virtual" resource, the `buildTask`. This entity represents a request for Sisense to build or publish a model. Creating this entity queues up a task, generating an ID that can be used for checking the task's status (by retrieving it) and for cancelling the task (by deleting it), Eventually, these entities are deleted and do not persist. The endpoint supports the following actions: * List all Build tasks * Start a Build/Publish * Cancel/Stop build task(s) for a specific Datamodel * Get a specific Build Task by ID - used to inspect the task's status * Cancel/Stop a specific Build Task by ID ### Building Extract Datamodels To start building a Datamodel, use the endpoint. It requires a payload, for which you will need 2-3 parameters: 1. The `oid` of the Datamodel you wish to build. 2. The desired build type: * `full` to build the entire Datamodel from scratch * `by_table` to build the Datamodel according to each table's settings, such as accumulative table builds * `schema_changes` to only build the parts of the Datamodel that have been modified 3. Optionally, a `rowLimit` if you wish to run a sample build which imports a set number of rows from datasources. 4. The optional `schemaOrigin` field determines which version of the schema will be built:\ *Note: this parameter is available starting from * * `latest` will build the Datamodel as seen in the Data page, including all changes to it since the last build *(default)* * `running` will build the last succesfully built schema - use this to avoid exposing incomplete changes to the schema. The payload has the following structure: ```json { "datamodelId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "buildType": "full", "schemaOrigin": "latest", "rowLimit": 0 } ``` Once executed, you will receive a response which includes the build task's `oid`. At this stage, your task is queued up and Sisense will start the build process as soon as possible. Receiving this response only confirms that Sisense has accepted the task, and **does not** indicate that the build has finished or even started. To check the status of the task, use the endpoint providing the build task's `oid` as `{buildId}`. The response body will contain a `status` field, indicating the task's current status. You can create a script that "polls" this endpoint at a preset interval in order to wait for the build task to complete. Keep in mind that a build can last anywhere from a few minutes to several hours, depending on how much data needs to be imported and how many custom tables and columns need to be calculated from it, amongst other factors. ### Publishing Live Datamodels Publishing a `live` type model is very similar to the build process described [above](#building-extract-datamodels). To publish a `live` type mode, simply set the `buildType` property to `publish`, like so: ```json { "datamodelId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "buildType": "publish" } ``` Additionally, note that the publish operation generally takes much less time than a build, as it only updates the queryable model. However, this task is still performed asynchronously and therefore a response from **does not** indicate that the publish task is done - only that it was accepted by Sisense and entered the build queue. Use the same method described above to check the task's status and determine when the Datamodel was published successfully. --- --- url: 'https://developer.sisense.com/guides/restApi/datamodels/connectors.md' --- # Data Connector Reference A connection is a mandatory part of any dataset, as it enables Sisense to fetch data from the target data source into the application. Connections can be created either through the [Connection Management GUI](https://docs.sisense.com/main/SisenseLinux/data-source-connection-management.htm) or via the dedicated set of [Connection Management REST API endpoints.](/guides/restApi/v2/#/Connections%20Management) For each connector type, the connection property has a specific structure. In some cases, additional configuration at the Table level is required, such as for CSV and Excel connectors, defined through the Table's configOptions property.For each connector type, there's a different structure for the Dataset's `connection` property. In some cases, additional configuration at the Table level is also required, such as for `CSV` and `Excel` type connectors, defined via the Table's `configOptions` property. The basic structure for any `connection` is: ```json { "provider": "string", "schema": "string", "parameters": {}, "globalTableConfigOptions": {} } ``` **Properties:** | Name | Type | Required | Description | |----------------------------|----------|----------|----------------------------------------------------------------| | `provider` | `string` | **Yes** | Connector type name (case sensitive!) | | `schema` | `string` | **Yes** | Schema to connect to (required value varies by connector type) | | `parameters` | `object` | **Yes** | Connection configuration (varies by connector type) | | `globalTableConfigOptions` | `object` | No | Some connectors require additional configuration | **For individual connector types see:** * [CSV](#csv) * [Excel](#excel) * [Sisense Elasticube](#ec2ec) * [MySQL](#mysql) * [Microsoft SQL Server](#microsoft-sql-server) * [PostgreSQL](#postgresql) * [Oracle](#oracle-db) * [Amazon RedShift](#amazon-redshift) * [Snowflake](#snowflake) * [Salesforce](#salesforce) ## CSV CSV File connectors allow for one or more CSV files from the cluster storage to be imported into a Datamodel. *CSV connections require additional configuration at the Table level via each Table's `configOptions` property.* **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"CSV"` | | `schema` | `string` | **Yes** | Path of first file | | `fileName` | `string` | **Yes** | Original file name of first file | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|-----------------|----------|---------------|------------------------------------------------------------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `files` | `string[]` | **Yes** | | An array of file paths for all files | | `unionAll` | `boolean` | No | | Optional: set to `true` to union all files in the dataset as one table | **`configOptions` Properties** | Name | Type | Required | Example Value | Description | |---------------------|-----------|----------|---------------|-----------------------------------| | `delimiter` | `string` | **Yes** | `","` | Delimiter for CSV columns | | `hasHeader` | `boolean` | **Yes** | `true` | Is CSV's 1st row a header row | | `excludeIfBegins` | `string` | No | `""` | | | `excludeIfContains` | `string` | No | `""` | | | `stringQuote` | `string` | **Yes** | `"\""` | Character denoting a string value | | `ignoreFirstRows` | `string` | No | `""` | | | `culture` | `string` | **Yes** | `"en-US"` | CSV file culture code | **Example** ```json "connection": { "provider": "CSV", "schema": "/opt/sisense/storage/datasets/storage/a8/a813808a-1df3-4193-a530-2eb9271005d1.csv", "parameters": { "ApiVersion": 2, "files": [ "/opt/sisense/storage/datasets/storage/a8/a813808a-1df3-4193-a530-2eb9271005d1.csv", "/opt/sisense/storage/datasets/storage/77/77ad3229-f84e-4a78-92d8-323f7a5e1802.csv" ], "unionAll": true }, "uiParams": {}, "globalTableConfigOptions": {}, "fileName": "Mostcommonprismfunctionsused.csv" } ``` ```json "configOptions": { "delimiter": ",", "hasHeader": true, "excludeIfBegins": "", "excludeIfContains": "", "stringQuote": "\"", "ignoreFirstRows": "", "culture": "en-US" } ``` ## Excel Excel File connectors allow for one or more Microsoft Excel files from the cluster storage to be imported into a Datamodel. *Excel connections require additional configuration at the Table level via each Table's `configOptions` property.* **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"Excel"` | | `schema` | `string` | **Yes** | Path of first file | | `fileName` | `string` | **Yes** | Original file name of first file | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|-----------------|----------|---------------|------------------------------------------------------------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `files` | `string[]` | **Yes** | | An array of file paths for all files | | `unionAll` | `boolean` | No | | Optional: set to `true` to union all files in the dataset as one table | **`configOptions` Properties** | Name | Type | Required | Example Value | Description | |--------------------|-----------|----------|---------------|---------------------------------| | `fieldsInFirstRow` | `boolean` | **Yes** | `true` | Is Excel's 1st row a header row | | `staticRange` | `string` | No | `""` | | | `culture` | `string` | **Yes** | `"en-US"` | CSV file culture code | **Example** ```json "connection": { "provider": "Excel", "schema": "/opt/sisense/storage/datasets/storage/4e/4eb09302-ad0d-4ed1-a733-31613c682504.xlsx", "parameters": { "ApiVersion": 2, "files": [ "/opt/sisense/storage/datasets/storage/4e/4eb09302-ad0d-4ed1-a733-31613c682504.xlsx" ] }, "uiParams": {}, "globalTableConfigOptions": {}, "fileName": "tenants.xlsx" } ``` ```json "configOptions": { "fieldsInFirstRow": true, "culture": "en-US", "staticRange": "" } ``` ## EC2EC **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"EC2EC"` | | `schema` | `string` | **Yes** | Name of datamodel | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|----------|---------|------------------------|----------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `Database` | `string` | **Yes** | `Sample Healthcare` | Name of datamodel | | `userName` | `string` | **Yes** | `"tester@sisense.com"` | Sisense username | | `password` | `string` | **Yes** | `"password"` | Sisense password | **Example** ```json "connection": { "provider": "EC2EC", "schema": "Sample Healthcare", "parameters": { "ApiVersion": 2, "userName": "tester@sisense.com", "password": "password", "Database": "Sample Healthcare" } } ``` ## MySQL **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"MySql"` | | `schema` | `string` | **Yes** | Name of SQL Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|-----------|----------|-----------------------|-----------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `Server` | `string` | **Yes** | `mysql.example.com` | MySQL Server location | | `userName` | `string` | **Yes** | `"admin"` | MySQL username | | `password` | `string` | **Yes** | `"password"` | MySQL password | | `Database` | `string` | **Yes** | `"test1"` | Name of database | | `SslSupport` | `boolean` | **Yes** | `false` | Use SSL | **Example** ```json "connection": { "provider": "MySql", "schema": "test1", "parameters": { "ApiVersion": 2, "Server": "mysql.example.com", "userName": "admin", "password": "password", "SslSupport": false, "Database": "test1" } } ``` ## Microsoft SQL Server **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"sql"` | | `schema` | `string` | **Yes** | Name of SQL Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|-----------|----------|-----------------------|----------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `Server` | `string` | **Yes** | `sql.example.com` | SQL Server location | | `UserName` | `string` | **Yes** | `"admin"` | SQL Server username | | `Password` | `string` | **Yes** | `"password"` | SQL Server password | | `Database` | `string` | **Yes** | `"AdventureWorks"` | Name of database | | `encrypt` | `boolean` | **Yes** | `false` | Use encryption | **Example** ```json "connection": { "provider": "sql", "schema": "Sales", "parameters": { "ApiVersion": 2, "Server": "sql.example.com", "UserName": "username", "Password": "password", "DefaultDatabase": "", "encrypt": false, "AdditionalParameters": "", "Database": "AdventureWorks" }, "uiParams": {}, "globalTableConfigOptions": {} } ``` ## PostgreSQL **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"PostgreSQL"` | | `schema` | `string` | **Yes** | Name of SQL Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|-----------|----------|------------------------|----------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `Server` | `string` | **Yes** | `postgres.example.com` | SQL Server location | | `UserName` | `string` | **Yes** | `"admin"` | SQL Server username | | `Password` | `string` | **Yes** | `"password"` | SQL Server password | | `Database` | `string` | **Yes** | `"postgres"` | Name of database | | `SslSupport` | `boolean` | **Yes** | `false` | Use SSL | **Example** ```json "connection": { "provider": "PostgreSQL", "schema": "public", "parameters": { "ApiVersion": 2, "Server": "postgres.example.com", "UserName": "username", "Password": "admin", "DefaultDatabase": "", "AdditionalParameters": "", "SslSupport": false, "Database": "postgres" }, "uiParams": {}, "globalTableConfigOptions": {} } ``` ## Amazon RedShift **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"RedShift"` | | `schema` | `string` | **Yes** | Name of Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |---------------------|-----------|----------|-------------------------------------------------------------|----------------------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `Server` | `string` | **Yes** | `myinstance.somekey.us-east-1.redshift.amazonaws.com:5439` | SQL Server location | | `UserName` | `string` | **Yes** | `"admin"` | SQL Server username | | `Password` | `string` | **Yes** | `"password"` | SQL Server password | | `Database` | `string` | **Yes** | `"dev"` | Name of database | | `EncryptConnection` | `boolean` | **Yes** | `false` | Should uses encrypted connection | **Example** ```json "connection": { "provider": "RedShift", "schema": "ec_sample_ecommerce", "parameters": { "ApiVersion": 2, "Server": "myinstance.somekey.us-east-1.redshift.amazonaws.com:5439", "UserName": "admin", "Password": "password", "DefaultDatabase": "dev", "EncryptConnection": false, "AdditionalParameters": "", "Database": "dev" }, "uiParams": {}, "globalTableConfigOptions": {} } ``` ## Oracle DB **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|----------------------------------| | `provider` | `string` | **Yes** | `"Oracle"` | | `schema` | `string` | **Yes** | Name of SQL Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |---------------------|-----------|----------|----------------------------|--------------------------------------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `ConnectionType` | `string` | **Yes** | `"Service ID"` | Connection type | | `Server` | `string` | **Yes** | `oracle11g.someserver.com` | Oracle DB Server location | | `Port` | `string` | **Yes** | `"1521"` | Oracle DB Server Port | | `UserName` | `string` | **Yes** | `"admin"` | SQL Server username | | `Password` | `string` | **Yes** | `"password"` | SQL Server password | | `ServiceId` | `string` | No | `"xe"` | Required if `ConnectionType: Service ID` is used | | `Database` | `string` | **Yes** | `"dev"` | Name of database | | `EncryptConnection` | `boolean` | **Yes** | `false` | Should uses encrypted connection | **Example** ```json "connection": { "provider": "Oracle", "schema": "TEST", "parameters": { "ApiVersion": 2, "ConnectionType": "Service ID", "Server": "oracle11g.someserver.com", "UserName": "admin", "Password": "password", "ServiceId": "xe", "Port": "1521", "AdditionalParameters": "", "Database": "TEST" }, "uiParams": {}, "globalTableConfigOptions": {} } ``` ## Snowflake **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|--------------------------| | `provider` | `string` | **Yes** | `"SnowflakeJDBC"` | | `schema` | `string` | **Yes** | Name of Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------------|-----------|----------|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `connectionString` | `string` | **Yes** | `"jdbc:snowflake://example.eu-central-1.snowflakecomputing.com/?warehouse=DEMO_WH"` | Connection string | | `userName` | `string` | **Yes** | `"admin"` | Snowflake username | | `password` | `string` | **Yes** | `"password"` | Snowflake password | | `Database` | `string` | **Yes** | `"BASICDEMO"` | Name of database | | `useKeyPairAuth` | `boolean` | **Yes** | `false` | `true` to use Key-Pair authentication, `false` to use username/password | **Example** ```json "connection": { "provider": "SnowflakeJDBC", "schema": "PUBLIC", "parameters": { "ApiVersion": 2, "connectionString": "jdbc:snowflake://example.eu-central-1.snowflakecomputing.com/?warehouse=DEMO_WH", "userName": "ADMIN", "password": "password", "useKeyPairAuth": false, "AdditionalParameters": "", "Database": "BASICDEMO" }, "uiParams": {}, "globalTableConfigOptions": {} } ``` ## Salesforce **Main Properties** | Name | Type | Required | Value | |----------------------------|----------|----------|--------------------------| | `provider` | `string` | **Yes** | `"SalesforceJDBC"` | | `schema` | `string` | **Yes** | Name of Schema | | `parameters` | `object` | **Yes** | Connection configuration | | `globalTableConfigOptions` | `object` | No | N/A | **`parameters` Properties** | Name | Type | Required | Example Value | Description | |--------------|-----------|----------|------------------------------|-------------------------| | `ApiVersion` | `number` | **Yes** | `2` | Use value `2` | | `userName` | `string` | **Yes** | `"admin@example.com"` | Snowflake username | | `password` | `string` | **Yes** | `"password"` | Snowflake password | | `dToken` | `string` | **Yes** | `"VDPa3wOf0zeZftGWv3ApJI8J"` | Security token | | `UseSandbox` | `boolean` | **Yes** | `false` | Connect in sandbox mode | **Example** ```json "connection": { "provider": "SalesforceJDBC", "schema": "Salesforce", "parameters": { "ApiVersion": 2, "userName": "admin@example.com", "password": "password", "dToken": "VDPa3wOf0zeZftGWv3ApJI8J;", "UseSandbox": false, "prevProviderTypeIsSalesforce": false, "AdditionalParameters": "" }, "uiParams": {}, "globalTableConfigOptions": {} } ``` --- --- url: 'https://developer.sisense.com/guides/restApi/datamodels/data-types.md' --- # Reference - Column Data Types When creating Tables and Columns, each Column has a `type` property specifying the data type of the Column using an `int` code. Specifying a `type` is only required when creating Columns in a regular Table. For custom Tables, Columns are created automatically with the most appropriate `type` based on the SQL expression specified for the custom Table. ## Versions L2021.9 or newer The datatypes below apply to Datamodels in Sisense for Linux versions and higher. | Code | Type | | ---- | :-------------------- | | `0` | BigInt | | `2` | Boolean | | `3` | Char | | `4` | Timestamp | | `5` | Decimal | | `6` | Float | | `8` | Integer | | `13` | Real | | `16` | SmallInt | | `18` | VarChar | | `20` | TinyInt | | `31` | Date | | `32` | Time | | `40` | Double | | `41` | Numeric | | `43` | TimestampWithTimezone | | `44` | TimeWithTimezone | **Changes compared to previous versions:** * `DateTime (4)` replaces `Timestamp (19)`\ *This change is backwards compatible - Sisense will still accept the type `19` via the API.* ## Older linux versions The datatypes below apply to Datamodels in older versions of Sisense for Linux. | Code | Type | |-------|-----------| | `8` | Int | | `0` | BigInt | | `5` | Decimal | | `13` | Real | | `6` | Float | | `19` | Timestamp | | `18` | Text | Note that Sisense Datamodels do not have a specific representation for types such as `bit` or `boolean` - these are converted to the most appropriate supported data type, most commonly `Text`. --- --- url: 'https://developer.sisense.com/guides/restApi/infusion-api.md' --- # Infusion API This reference serves as a practical guide for developers looking to manage Viewpoints via API using a set of standard CRUD endpoints. This documentation provides information about the data objects, data structures and endpoints and their behaviors. Below are some potential use cases for the Infusion API. **Use Case:** An OEM has a self-service BI portal where users are authenticated. The OEM provides a list of Viewpoints that are available, and allows users to request which Viewpoints they want in which apps. When requested, API calls are made to change the permissions on the Viewpoint. **Use Case:** As new employees join organizations, they need a Sisense login to be generated for them. Developers can use the existing User APIs to add new users, assign them to a user group, etc. When users are assigned to groups, developers can create automation to automatically assign them other assets depending on their role or group. **Use Case:** When a new user group is created, automation can be in place to assign a set of Viewpoints that have been parameterized in order to work out of the box, with the appropriate data security rules applied. See [Using the REST API](using-rest-api.md) for more general information about authentication, conventions, etc. ## Limitations The Infusion API is intended to work with Sisense instances that are using Infusion Apps. In order to use the Infusion API, the Sisense instance must have Infusion Apps enabled on the license. The Infusion API uses the Viewpoint object, which is only available for Sisense versions `L2023.11` and later. ## ViewpointObject The data related to an individual Viewpoint is stored in a single entity. This entity holds additional data structures that can be requested explicitly, like ViewpointVisibility and ViewpointDetails. | Property | Type | Description | Example | | ------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `id` | `string` | The unique identifier of the Viewpoint. | 6513fcdc03524c03b96bdfad | | `name` | `string` | The name that end users will see in Infusion Apps, and will reference when writing natural language queries. *This value must be unique.* | Secure Fraud South | | `tenantId` | `string` | The id of the tenant the Viewpoint is associated with. | 6512cd66d01d6d001b493488 | | `description` | `string` | The description that end users will see in their Infusion Apps next to the View Name. | Includes suspicious patterns, transaction volumes, and anomalies. Data is filtered to the US-South region. | | `enabled` | `boolean` | The status of the Viewpoint. When set to true, the Viewpoint is published. | true | | `createdBy` | `string` | The userId of the user who created the Viewpoint | 6512cd65d01d6d001b49347b | | `updatedBy` | `string` | The userId of the user who updated the Viewpoint | 6512cd65d01d6d001b49347b | | `createdAt` | `timedate` | The time/date the Viewpoint was created | 2023-09-27T09:58:52.669Z | | `updatedAt` | `timedate` | The time/date that Viewpoint was updated | 2023-09-27T09:58:52.669Z | | `visibility` | `object` | Defines the visibility of the Viewpoint. See [ViewpointVisibility](#viewpointvisibility) | | `details` | `object` | Contains the data that is part of the Viewpoint. See [ViewpointDetails](#viewpointdetails) | ### ViewpointObject ```json { "id": "string", "tenantId": "string", "name": "string", "description": "string", "details": { "datasource": { "id": "string", "title": "string", "fullname": "string", "modelType": "live" }, "columns": [ { "table": "string", "column": "string", "dim": "string", "title": "string", "datatype": "numeric", "infusion": { "visible": true, "uid": true, "defaultMeasure": true, "defaultDate": true, "header": true, "filterOnly": true }, "filter": { "all": true, "from": 0, "to": 0, "members": ["string"] } } ] }, "visibility": { "apps": ["Google"] } } ``` ### ViewpointObject Example ```json { "name": "Secure Fraud South", "tenantId": "6512cd66d01d6d001b493488", "description": "Includes suspicious patterns, transaction volumes, and anomalies.", "details": { "datasource": { "id": "localhost_aOrdersDB", "title": "OrdersDB", "fullname": "localhost/OrdersDB" }, "columns": [ { "table": "ORDERS1", "dim": "[ORDERS1.ORDER_ID]", "title": "ORDER_ID", "column": "ORDER_ID", "datatype": "numeric", "filter": { "explicit": true, "multiSelection": true, "members": ["10002", "10003", "10004", "10005"] }, "infusion": { "filterOnly": false, "uid": true } }, { "table": "CUSTOMERS1", "dim": "[CUSTOMERS1.COMPANY_NAME]", "title": "Customer", "column": "COMPANY_NAME", "datatype": "text", "filter": {} }, { "table": "SALES_EMPLOYEES1", "dim": "[SALES_EMPLOYEES1.Employee Name]", "title": "Sales Rep", "column": "Employee Name", "datatype": "text", "filter": { "explicit": true, "multiSelection": true, "members": ["Andrew Green", "Ben Franklin", "Cormack Coyle"] } } ] }, "visibility": { "apps": ["Google", "Slack", "Teams", "Office"], "groups": ["Admins", "Admins", "6512cd64d01d6d001b49347a", "6512cd64d01d6d001b493479"], "users": ["admin@sisense.com", "admin@sisense.com"], "enabled": true }, "createdBy": "6512cd65d01d6d001b49347b", "createdAt": "2023-09-27T09:58:52.669Z", "updatedAt": "2023-11-20T22:29:34.641Z", "updatedBy": "6512cd65d01d6d001b49347b", "id": "6513fcdc03524c03b96bdfad" } ``` ## ViewpointDetails The `ViewpointDetails` data structure stores the details of the data that is included in the Viewpoint. The `ViewpointDetails` data structure includes two sub-structures: | Data Structure | Description | | -------------- | --------------------------------------------------------------------------------- | | `datasource` | Describes the source of the tables and columns being referenced in the Viewpoint. | | `column` | Describes the columns, filters and metadata that is included in the Viewpoint. | `ViewpointDetails` can be: * Requested using the or * Created using * Updated using the * Deleted using ### ViewpointDetails Structure ```ts { datasource: datasource: { Id: String, title: String, fullname: String, modelType: live | external | perspective }, columns: ColumnMetadata[], } ``` ### ViewpointDetails Example ```json { "details": { "datasource": { "id": "localhost_aOrdersDB", "title": "OrdersDB", "fullname": "localhost/OrdersDB", "modelType": "live" }, "columns": [ { "table": "ORDERS1", "dim": "[ORDERS1.ORDER_ID]", "title": "ORDER_ID", "column": "ORDER_ID", "datatype": "numeric", "filter": { "explicit": true, "multiSelection": true, "members": ["10002", "10003", "10004", "10005"] }, "infusion": { "filterOnly": false, "uid": true } } ] } } ``` ### datasource Datasource defines the source of the tables and columns being referenced in the Viewpoint. | Property | Type | Description | Example | | ----------- | ------ | ------------------------------------------------------------------------------------- | ------------------- | | `id` | string | The id of the data source. | localhost\_aOrdersDB | | `title` | string | The title of the data source. | OrdersDB | | `fullname` | string | The full name of the data source. | localhost/OrdersDB | | `modelType` | string | The type of datasource. A datasource can be defined as live, external or perspective. | live | #### Example ```json "datasource": { "id": "localhost_aOrdersDB", "title": "OrdersDB", "fullname": "localhost/OrdersDB", "modelType": "live" } ``` ### column The column sub-structure describes the columns, and metadata that is included in the Viewpoint. This includes the column data, format, filters. column contains an additional nested structure called infusion, which describes the Ask Me configurations applied to each column, if any. | Property | Type | Description | Example | | ------------ | ------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | `table` | string | The name of the table where the column is located | CUSTOMERS1 | | `column` | string | The name of the column within the table | COMPANY\_NAME | | `dim` | string | The dimension referenced in the table. | \[CUSTOMERS1.COMPANY\_NAME] | | `datatype` | string | Describes the column’s data type. The datatype can be a number, text or datetime. | datetime | | `title` | string | The title of the column as displayed to the user | Customer | | `level` | string | Defines the granularity of time. Only relevant for datetime data types. This property is optional. | days, months, quarters, years, weeks | | `agg` | string | Defines the aggregation applied to the column. Supported aggregations are count, sum and average This property is optional. | [See JAQL docs](https://developer.sisense.com/guides/querying/jaqlSyntax/) | | `filter` | | The filter parameter as defined by the JAQL syntax. This property is optional. | [See JAQL docs](https://developer.sisense.com/guides/querying/jaqlSyntax/) | | `members` | string | The values included in the filter argument. | members": \[ "Andrew Green", "Ben Franklin", "Cormack Coyle" ] | | `filterOnly` | boolean | | false | | `format` | | JaqlFormat of the column data. This property is optional. | [See JAQL docs](https://developer.sisense.com/guides/querying/jaqlSyntax/) | | `sort` | | Describes the sort order. This property is optional. | asc | #### Example ```ts { table: string; column: string; dim: string; datatype: number | text | datetime; title: string; level: days | months | quarters | years | weeks; agg: AggType; filter: JaqlFilter; (see JAQL docs) Infusion: { visible: boolean; uid: boolean; defaultMeasure: boolean; defaultDate: boolean; header: boolean; hyperlink: string; aliases: string[]; filterOnly: boolean; } format: []; sort: asc | desc; } ``` #### Example ```json "columns": [ { "table": "ORDERS1", "dim": "[ORDERS1.ORDER_ID]", "title": "ORDER_ID", "column": "ORDER_ID", "datatype": "number", "filter": { "explicit": true, "multiSelection": true, "members": [ "10002", "10003", "10004", "10005" ] }, "infusion": { "filterOnly": false, "uid": true } } ] ``` ### infusion `infusion` is present or defined when [Ask Me configurations](https://docs.sisense.com/main/SisenseLinux/viewpoints.htm?tocpath=Embedding%20and%20Infusing%20Analytics%7CInfusion%20Apps%7C_____9#AskMeSettings) are applied to a column within a Viewpoint. Generally, these configurations are named differently in the JSON, than in the UI. This sub-structure is optional, and should only be used if the Viewpoint is intended to be shared and used with Slack or Teams. | Property | Type | Description | Example | | ---------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | `visible` | boolean | This is a deprecated field. This may be shown, but do not rely on it. | | | `uid` | boolean | The field designated as the Unique Identifier. If defined, a filter must be added as a Filter field. A Viewpoint can have a maximum of one Unique Identifier. This property is optional. | true | | `defaultMeasure` | boolean | The field designated as the Default Measure. A Viewpoint can have a maximum of one Default Measure. This property is optional. | false | | `defaultDate` | boolean | The field designated as the Default Date. Only date fields can be selected as a Default Date. A Viewpoint can have a maximum of one Default Date. This property is optional. | true | | `header` | boolean | Column will display as a header in the response from the chatbot. This property is optional. | true | | `members` | string | The values included in the filter argument. | members: \[ "Andrew Green", "Ben Franklin", "Cormack Coyle" ] | | `uid` | boolean | The unique id of the filter condition | true | | `hyperlink` | string | This is a deprecated field. This may be shown, but do not rely on it. | | | `aliases` | string | This is a deprecated field. This may be shown, but do not rely on it. | | | `filterOnly` | boolean | | false | #### Example ```ts Infusion: { visible: boolean; uid: boolean; defaultMeasure: boolean; defaultDate: boolean; header: boolean; hyperlink: string; aliases: string[]; filterOnly: boolean; } ``` ## ViewpointVisibility `ViewpointVisibility` defines which Infusion Apps, user(s) and [user group(s)](https://docs.sisense.com/main/SisenseLinux/managing-user-groups.htm) the Viewpoint is visible to. When working with Viewpoints, sharing and publishing are two separate actions which can influence visibility. A Viewpoint can be shared with Infusion Apps, users or user groups, but not be visible to them until published (UI) or enabled (API). See [Sharing and Publishing a Viewpoint](https://docs.sisense.com/main/SisenseLinux/viewpoints.htm?tocpath=Embedding%20and%20Infusing%20Analytics%7CInfusion%20Apps%7C_____9#SharingandPublishingaViewpoint) for more information. When changing the visibility of a Viewpoint, the object must meet the minimum requirements: * One or more Infusion Apps * At least one User or one User Group `ViewpointVisibility` can be changed using the [ method](#put-infusionviewpointsid). | property | Type | Description | Example | | --------- | ------- | ---------------------------------------------------------- | ----------------- | | `apps` | string | The Infusion App(s) that the Viewpoint is shared with. | Google | | `groups` | string | The list of user groups that the Viewpoint is shared with. | Admins | | `users` | string | The list of individual users the Viewpoint is shared with | admin@sisense.com | | `enabled` | boolean | The visibility state of the Viewpoint | true | #### Example ```json "visibility": { "apps": [ "Google", "Slack", "Teams", "Office" ], "groups": [ "Admins", "Admins", "6512cd64d01d6d001b49347a", "6512cd64d01d6d001b493479" ], "users": [ "admin@sisense.com", "admin@sisense.com" ], "enabled": true } ``` # Infusion API Endpoints | Method | Path | Purpose | | ------ | ------------------------- | -------------------------------------------------------------------- | | GET | /infusion/status | Returns the status of the Infusion Service. | | GET | /infusion/viewpoints | Returns a list of Viewpoints from an instance. | | GET | /infusion/viewpoints/{id} | Returns a single Viewpoint using a unique identifier. | | POST | /infusion/viewpoints | Creates a new Viewpoint. | | PUT | /infusion/viewpoints/{id} | Updates the contents of a Viewpoint. | | DELETE | /infusion/viewpoints/{id} | Deletes a Viewpoint and associated Bookmarks from all Infusion Apps. | ## GET infusion/status This endpoint returns the status of the Infusion Service used by all Infusion Apps APIs. It provides a way to troubleshoot and verify if the service is working if Infusion Apps are experiencing issues. This endpoint does not require authorization as it is just returning the status of the Infusion service. All other endpoints require authorization. ### Request Parameters | Parameter | Data Type | Description | Required | Example | | --------------- | ---------------- | ------------------------------------------------------------------------------------------ | -------- | ------------------------ | | `x-tenent-id` | headerstring | Used to define the Tenant ID on behalf of whom the operation will be executed. | No | 6512cd66d01d6d001b493488 | | `authorization` | headerstring | The user's API token preceded by the keyword Bearer (with space between it and the token). | No | "Bearer " + your token | ### Request URL `https://infusion-test.sisense.com/api/v1/infusion/status` ## GET infusion/viewpoints This endpoint returns an array of Viewpoints. The default response includes a total count of Viewpoints within an instance, ordered by `id`. The request can include `ViewpointSearch`, passed as an additional parameter, which can be used to return results matching the parameters. ### Request Parameters | Parameter | Data Type | Description | Required | Example | | --------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------ | | `x-tenent-id` | headerstring | Used to define the Tenant ID on behalf of whom the operation will be executed. | No | 6512cd66d01d6d001b493488 | | `authorization` | headerstring | The user's API token preceded by the keyword Bearer (with space between it and the token). | No | "Bearer " + your token | | `search` | querystring | The search parameters that can be defined to return a sub-set of Viewpoints matching search parameters. See ViewpointsSearch. | No | `{“name”: “e”, “description”: “a”, "text": "srch", “visibility”: [“Google”], “datasource”: { “fullname”: “local” }}` | | `with` | querystring | Request additional information in the response by defining a single parameter (visibility | details), or both parameters separated by a comma. Requesting visibility will return data from [ViewpointVisibility](#viewpointvisibility), and requesting details will return data from [ViewpointDetails](#viewpointdetails). | No | with: “visibility” | | `offset` | querynumber | Determines the starting point for the response, skipping the number of offset records. | No | | | `limit` | querynumber | A limit on the number of objects to be returned. The default will return 100 rows. There is no limit to a minimum or maximum value. | No | 25 | ### Request URL `http://localhost:8080/api/v1/infusion/viewpoints?with=visibility,details&search=%7B%22text%22:%22%22%7D` ### Request Example `https://infusion-test.sisense.com/api/v1/infusion/viewpoints?search=%7B%22text%22%3A%20%22s%22%2C%20%22visibility%22%3A%20%7B%22apps%22%3A%20%5B%22Google%22%5D%7D%2C%20%22details%22%3A%20%7B%22datasource%22%3A%20%7B%22fullname%22%3A%20%22local%22%7D%7D%7D&with=visibility%2Cdetails` ### ViewpointSearch ViewpointSearch can be passed as a search parameter when using the [ method](#get-infusionviewpoints) in order to return a sub-set of Viewpoints that match the search parameters. ### Request Parameters | Property | Type | Description | Example | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `name` | `string` | Searches the text value in a Viewpoint name. Partial strings are accepted and will be matched. *This value must be unique and is case sensitive.* | Secure Fraud South | | `description` | `string` | Searches the text value in the description. Partial strings are accepted and will be matched. *This value is case sensitive.* | Includes suspicious patterns, transaction volumes, and anomalies. Data is filtered to the US-South region. | | `text` | `string` | Searches the text value in the name or description. Partial strings are accepted and will be matched. *This value is case sensitive.* | Secure Fraud South | | `lastUpdate` | `object` | Object includes two operators that can be passed individually or together. Timestamp and ISO datetime formats are supported. | `{before: 1242341, after: 1112421}` | | `id` | `string` | The unique identifier of the Viewpoint. | 6513fcdc03524c03b96bdfad | | `details` | `object` | A partial object with search parameters that can be passed | `{"datasource": {"datasource": {fullname: "o"}}}` | | `visibility` | `object` | A partial object. | `{"apps":["Google"], enabled: true}` | #### Example ```ts { id: String, name: String, description: String, text: String lastUpdate: {before: , after:} visibility: ViewpointVisibility details: ViewpointDetails } ``` ## GET infusion/viewpoints/{id} This endpoint returns a single `Viewpoint` object using its unique identifier. The response includes a comprehensive set of information related to the Viewpoint object, including the Viewpoint's details and permissions. The response will contain the entire Viewpoint object, including the [ViewpointDetails](#viewpointdetails) and [ViewpointVisibility](#viewpointvisibility). ### Request `GET api/v1/infusion/viewpoints/7554cd1ad8fd93cc9d6b5f05` ### Request Parameters | Parameter | Data Type | Description | Required | Example | | --------------- | ---------------- | ------------------------------------------------------------------------------------------ | -------- | ------------------------ | | `x-tenent-id` | headerstring | Used to define the Tenant ID on behalf of whom the operation will be executed. | No | 6512cd66d01d6d001b493488 | | `authorization` | headerstring | The user's API token preceded by the keyword Bearer (with space between it and the token). | No | "Bearer " + your token | | `id` | pathstring | The unique identifier of the Viewpoint. | Yes | 7554cd1ad8fd93cc9d6b5f05 | ## PUT infusion/viewpoints/{id} This endpoint updates a `Viewpoint` using its unique identifier. It allows users to modify the `Viewpoint` object. If passing complex properties like [DataSource](#datasource), [ViewpointDetails](#viewpointdetails) or [ViewpointVisibility](#viewpointvisibility), these properties should be passed in full. For example, if you want to update a field title, you should pass full details objects with all columns (fields) where the required column title was changed. ### Request Parameters | Parameter | Data Type | Description | Required | Example | | --------------- | ---------------- | ------------------------------------------------------------------------------------------ | -------- | ------------------------ | | `authorization` | headerstring | The user's API token preceded by the keyword Bearer (with space between it and the token). | No | "Bearer " + your token | | `id` | pathstring | The unique identifier of the `Viewpoint`. | Yes | 6513fcdc03524c03b96bdfad | | `x-tenent-id` | headerstring | Used to define the Tenant ID on behalf of whom the operation will be executed. | No | 6512cd66d01d6d001b493488 | ### Request Body ``` PUT api/v1/infusion/viewpoints/6512cd66d01d6d001b493488 BODY: {name: “Updated Name”} ``` ## POST infusion/viewpoints This endpoint allows users to create a new `Viewpoint` within an instance. The endpoint creates an empty Viewpoint, with name and description as required fields. The request body can contain a partial definition of the [ViewpointObject](#viewpointobject). **Note:** The endpoint provides no validation on the contents of the object. This means that a Viewpoint may be created successfully, but contain errors when trying to be used by Infusion App end users. On creation, Viewpoints will be in an [unpublished status](https://docs.sisense.com/main/SisenseLinux/viewpoints.htm?tocpath=Embedding%20and%20Infusing%20Analytics%7CInfusion%20Apps%7C_____9#SharingandPublishingaViewpoint) unless defined otherwise in the request body. A Viewpoint in Unpublished status is not visible to any Infusion Apps, users, or user groups. You must use the to perform updates to the `ViewpointObject`, including `ViewpointVisibility` where the ViewpointVisibility can be changed. ### Request Parameters | Parameter | Data Type | Description | Required | Example | | --------------- | ---------------- | ------------------------------------------------------------------------------------------ | -------- | ------------------------ | | `authorization` | headerstring | The user's API token preceded by the keyword Bearer (with space between it and the token). | No | "Bearer " + your token | | Viewpoint | bodystring | A new Viewpoint object. | Yes | 6513fcdc03524c03b96bdfad | | `x-tenent-id` | headerstring | Used to define the Tenant ID on behalf of whom the operation will be executed. | No | 6512cd66d01d6d001b493488 | ### Viewpoint Properties | Parameter | Data Type | Description | Required | Example | | ------------- | --------- | ----------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | `name` | string | The name of the Viewpoint. This value must be unique. | Yes | Secure Fraud South | | `description` | string | The description of the Viewpoint. | Yes | Includes suspicious patterns, transaction volumes, and anomalies. Data is filtered to the US-South region. | | `details` | object | See ViewpointDetails | No | | | `visibility` | object | See ViewpointVisibility | No | | ### Request Body ```json { "name": "string", "description": "string", "details": {}, "visibility": {} } ``` ## DELETE infusion/viewpoints/{id} This endpoint allows users to delete a single `Viewpoint` by providing its unique identifier. When a Viewpoint is deleted, its associated Bookmarks are also deleted. Deleted Viewpoints will be removed immediately and inaccesible by users in the Infusion Apps. ### Request Parameters | Parameter | Data Type | Description | Required | Example | | --------------- | ---------------- | ------------------------------------------------------------------------------------------ | -------- | ------------------------ | | `authorization` | headerstring | The user's API token preceded by the keyword Bearer (with space between it and the token). | No | "Bearer " + your token | | `id` | pathstring | The unique identifier of the Viewpoint. | Yes | 6513fcdc03524c03b96bdfad | | `x-tenent-id` | headerstring | Used to define the Tenant ID on behalf of whom the operation will be executed. | No | 6512cd66d01d6d001b493488 | --- --- url: 'https://developer.sisense.com/guides/restApi/reference/index.md' description: >- Every Sisense REST API endpoint on Linux — method, path, and summary — across API v2, v1, v0. --- # Sisense REST API Endpoint Index — Linux 2026.1 > Every Sisense REST API endpoint on Linux (API v2, v1, v0) on one page: method, path, and a one-line summary. For an interactive explorer with request/response schemas and try-it-out, use the Swagger UI reference ([v2](../v2/) · [v1](../v1/) · [v0](../v0/)). The raw specs are listed at [`/swagger-specs/index.json`](https://developer.sisense.com/swagger-specs/index.json). **147** endpoints across API v2, v1, v0. ## REST API v2 (Linux 2026.1) > **58** endpoints across **11** tags (OpenAPI 3). Raw spec: [`/swagger-specs/linux/latest/v2.openapi.json`](/swagger-specs/linux/latest/v2.openapi.json) (stable latest alias) or [`/swagger-specs/linux/2026.1/v2.openapi.json`](/swagger-specs/linux/2026.1/v2.openapi.json) (this exact version). ### B2D Connection * **GET** `/api/v2/b2d-connection` — Get all Build to Destination Connections * **POST** `/api/v2/b2d-connection` — Build to Destination connections * **PATCH** `/api/v2/b2d-connection` — Update Build to Destination connection * **DELETE** `/api/v2/b2d-connection/{destination}` — Delete Build to Destination Connection ### Builds * **GET** `/api/v2/builds/{buildId}` — Get Build Task by ID * **DELETE** `/api/v2/builds/{buildId}` — Cancel/Stop a Build Task * **GET** `/api/v2/builds/elementdependencies` — Get element dependencies ### Cluster * **GET** `/api/v2/cluster-nodes` — List Cluster Nodes * **GET** `/api/v2/cluster-nodes/{nodeId}` — Get Cluster Node ### Connections Management * **GET** `/api/v2/connections` — Retrieves Managed Connections List * **POST** `/api/v2/connections` — Create a New Managed Connection * **GET** `/api/v2/connections/{connectionId}` — Retrieves a Managed Connection * **PATCH** `/api/v2/connections/{connectionId}` — Update specified Managed Connection * **DELETE** `/api/v2/connections/{connectionId}` — Delete specified Managed Connection * **PATCH** `/api/v2/connections/{connectionId}/shares/update` — Update Managed Connection Shares ### Data Groups * **GET** `/api/v2/data-groups` — List Data Groups * **POST** `/api/v2/data-groups` — Create Data Group * **GET** `/api/v2/data-groups/` — List Data Groups * **POST** `/api/v2/data-groups/` — Create Data Group * **GET** `/api/v2/data-groups/{dataGroupId}` — Get Data Group by ID * **PATCH** `/api/v2/data-groups/{dataGroupId}` — Update Data Group * **DELETE** `/api/v2/data-groups/{dataGroupId}` — Delete Data Group * **GET** `/api/v2/data-groups/{dataGroupId}/` — Get Data Group by ID * **PATCH** `/api/v2/data-groups/{dataGroupId}/` — Update Data Group * **DELETE** `/api/v2/data-groups/{dataGroupId}/` — Delete Data Group * **POST** `/api/v2/data-groups/{dataGroupId}/datamodels` — Assign Datamodels to Data Group * **POST** `/api/v2/data-groups/{dataGroupId}/datamodels/` — Assign Datamodels to Data Group * **GET** `/api/v2/data-groups/default` — Get Default Data Group * **GET** `/api/v2/data-groups/default/` — Get Default Data Group ### Datamodel Imports/Exports * **GET** `/api/v2/datamodel-exports/stream/full` — Export Datamodel as stream * **GET** `/api/v2/datamodel-exports/stream/full/` — Export Datamodel as stream * **POST** `/api/v2/datamodel-imports/stream/full` — Import full Datamodel as stream ### Destination Storage * **GET** `/api/v2/storage` — storage parameters * **POST** `/api/v2/storage` — Create storage * **PATCH** `/api/v2/storage` — Update storage parameters * **DELETE** `/api/v2/storage` — Delete All Storage Parameters * **GET** `/api/v2/storage/{storageType}` — storage parameters * **DELETE** `/api/v2/storage/{storageType}` — Delete Storage ### Scheduled Build * **GET** `/api/v2/datamodels/{datamodelId}/schedule` — get schedule build by cube id * **POST** `/api/v2/datamodels/{datamodelId}/schedule` — Add build schedule for cube * **DELETE** `/api/v2/datamodels/{datamodelId}/schedule` — delete schedule ### backups * **GET** `/api/v2/backups` — List backups * **POST** `/api/v2/backups` — Start on-demand backup * **GET** `/api/v2/backups/{backup_id}` — Get backup info * **DELETE** `/api/v2/backups/{backup_id}` — delete backup * **POST** `/api/v2/backups/cancel` — cancel ongoing backup process * **POST** `/api/v2/backups/restore/{directory}` — Start system restore * **GET** `/api/v2/backups/schedules` — list backup schedules * **POST** `/api/v2/backups/schedules` — Create backup schedule * **GET** `/api/v2/backups/schedules/{schedule_id}` — get backup schedule * **PATCH** `/api/v2/backups/schedules/{schedule_id}` — Update backup schedule * **DELETE** `/api/v2/backups/schedules/{schedule_id}` — Delete backup schedule ### connectors * **POST** `/api/v2/connectors/{provider}/table_preview` — Returns table preview stream ### tests * **GET** `/api/v2/tests` — Get all tests configurations * **GET** `/api/v2/tests/{idOrName}` — Get test configuration details * **PUT** `/api/v2/tests/{idOrName}` — Set test configuration * **GET** `/api/v2/tests/results` — Get last tests results * **POST** `/api/v2/tests/run` — Run tests and save / return tests results according to the provided flag ## REST API v1 (Linux 2026.1) > **52** endpoints across **10** tags (OpenAPI 3). Raw spec: [`/swagger-specs/linux/latest/v1.openapi.json`](/swagger-specs/linux/latest/v1.openapi.json) (stable latest alias) or [`/swagger-specs/linux/2026.1/v1.openapi.json`](/swagger-specs/linux/2026.1/v1.openapi.json) (this exact version). ### build-rest-controller * **POST** `/api/v1/elasticubes/next/getContextFromExpression` — check auto complete for custom query * **POST** `/api/v1/elasticubes/next/runColumnQuery` — check column query validation * **POST** `/api/v1/elasticubes/next/runTableQuery` — check table query validation * **GET** `/api/v1/elasticubes/servers/next/{server}/{dataSourceTitle}/isBuilding` — Build contract: checks if there is running build for given data source * **POST** `/api/v1/elasticubes/servers/next/{server}/build` — run build cube * **POST** `/api/v1/elasticubes/servers/next/{server}/explain_build` — explain build without type * **POST** `/api/v1/elasticubes/servers/next/{server}/explain_build/{buildType}` — explain build by build type ### connections * **GET** `/api/v1/connection` — Returns a list of connections * **GET** `/api/v1/connection/{id}` — Returns a connection by its ID * **DELETE** `/api/v1/connection/{id}` — Removes a connection * **GET** `/api/v1/connection/recent` — Returns recently used connection objects ### connectors * **GET** `/api/v1/connectors/{provider}/ui_config` — Returns object that describes the UI connection manifest ### data-exploration * **POST** `/api/v1/data-exploration/refresh` — Refresh data exploration * **POST** `/api/v1/data-exploration/refresh/datasource` — Refresh data exploration by Datasource * **GET** `/api/v1/data-exploration/status` — Return status of data exploration * **GET** `/api/v1/data-exploration/user-influences` — Returns user likes ### formulas * **GET** `/api/v1/formulas` — Get multiple formulas * **POST** `/api/v1/formulas` — Create a new formula * **GET** `/api/v1/formulas/{formulaId}` — Get a specific formula by id * **PATCH** `/api/v1/formulas/{formulaId}` — Update an existing formula * **DELETE** `/api/v1/formulas/{formulaId}` — Delete a formula * **GET** `/api/v1/formulas/usage` — Get multiple formulas usage data ### live-connectors * **GET** `/api/v1/live_connectors` — Returns the list of available live connector services * **GET** `/api/v1/live_connectors/{provider}` — Returns object that describes the connection manifest (parameters) * **POST** `/api/v1/live_connectors/{provider}/count` — Returns count of entries in table * **POST** `/api/v1/live_connectors/{provider}/list_databases` — Returns list of databases * **POST** `/api/v1/live_connectors/{provider}/list_schemas` — Returns a list of schemas in a database * **POST** `/api/v1/live_connectors/{provider}/list_table_schemas` — Returns a list of tables in a database * **POST** `/api/v1/live_connectors/{provider}/table_preview` — Returns data preview * **POST** `/api/v1/live_connectors/{provider}/table_schema_details` — Returns a table's schema * **POST** `/api/v1/live_connectors/{provider}/test_connection` — Returns an object with status = OK after a successful connection ### management-rest-controller * **GET** `/api/v1/elasticubes/servers/next` — Get servers with next ECubes * **GET** `/api/v1/elasticubes/servers/next/{server}/{cubeTitle}` — Get Ecm Model of a specific elasticube ### notebook-router-controller * **GET** `/api/v1/notebooks` * **POST** `/api/v1/notebooks` * **PUT** `/api/v1/notebooks` * **PATCH** `/api/v1/notebooks` * **DELETE** `/api/v1/notebooks` * **HEAD** `/api/v1/notebooks` * **OPTIONS** `/api/v1/notebooks` * **GET** `/api/v1/notebooks/**` * **POST** `/api/v1/notebooks/**` * **PUT** `/api/v1/notebooks/**` * **PATCH** `/api/v1/notebooks/**` * **DELETE** `/api/v1/notebooks/**` * **HEAD** `/api/v1/notebooks/**` * **OPTIONS** `/api/v1/notebooks/**` ### suggestion * **POST** `/api/v1/suggestions` — Return suggestions for an entity * **POST** `/api/v1/suggestions/refresh` — Refresh suggestions data and ranking * **POST** `/api/v1/suggestions/refresh/datasource` — Refresh suggestions data and ranking by Datasource * **GET** `/api/v1/suggestions/status` — Return status of suggestions ### translator-utils-rest-controller * **GET** `/api/v1/settings/translation` — Returns translation settings ## REST API v0 (Linux 2026.1) > **37** endpoints across **2** tags (OpenAPI 3). Raw spec: [`/swagger-specs/linux/latest/v0.openapi.json`](/swagger-specs/linux/latest/v0.openapi.json) (stable latest alias) or [`/swagger-specs/linux/2026.1/v0.openapi.json`](/swagger-specs/linux/2026.1/v0.openapi.json) (this exact version). ### management-rest-controller * **GET** `/api/datasources` — Gets all datasources * **DELETE** `/api/elasticubes/{server}/{cubeName}/delete` — Delete Cube by name * **POST** `/api/elasticubes/{server}/{dataSourceId}/detach` — Detach Cube * **POST** `/api/elasticubes/{server}/{instanceId}/detachInstanceId` — Detach Cube * **POST** `/api/elasticubes/{server}/attach` — Attach Cube * **POST** `/api/elasticubes/{server}/availableDatasourcesInfo` — info available Datasources * **POST** `/api/elasticubes/{server}/info` — info Cubes * **GET** `/api/elasticubes/sample` — Attach Sample * **GET** `/api/elasticubes/servers` — Get servers with ECubes * **GET** `/api/elasticubes/servers/{server}` — Get cubes * **GET** `/api/elasticubes/servers/{server}/identity` — Get server identity * **GET** `/api/elasticubes/servers/{server}/simple` — Get database simple ### query-rest-controller * **POST** `/api/datasources/{cubeName}/jaql` — execute query * **POST** `/api/datasources/{cubeName}/jaql/csv` — Execute query * **POST** `/api/datasources/{cubeName}/jaql/explain` — Explain query * **POST** `/api/datasources/{cubeName}/jaql/sql` * **GET** `/api/datasources/{cubeName}/sql` — Execute sql * **POST** `/api/datasources/{cubeName}/sql` — Execute sql * **GET** `/api/datasources/{dataSourceFullName}/{tableName}/connectedTables` — Get connected tables * **POST** `/api/datasources/{fullName}/calculated-dimension/parse` — Parse calculated dimension * **POST** `/api/datasources/{fullName}/parse` — Parse jaql * **POST** `/api/datasources/{liveFullName}/fields/search` — Get fields * **POST** `/api/datasources/{server}/{cubeName}/cancel_queries` — Cancel queries * **POST** `/api/datasources/{server}/{title}/fields/search` — Get fields * **POST** `/api/datasources/{server}/elasticubes/{cubeName}/preview` — Execute sql for preview * **POST** `/api/datasources/{server}/elasticubes/{cubeName}/sql` — Execute sql * **GET** `/api/datasources/{title}` — Cancel all queries * **GET** `/api/datasources/{title}/fields` — Get fields * **POST** `/api/datasources/{title}/fields/searchByDisplayName` — Retrieve fields by their display name. If a display name is defined for a field, the search will not use its identity name * **POST** `/api/elasticubes/{server}/{cubeName}/cancelAllQueries` — Cancel all queries * **POST** `/api/elasticubes/{server}/{dataSourceId}/stopDataSourceId` — Stop cube by dataSourceId name * **POST** `/api/elasticubes/{server}/{instanceId}/startInstanceId` — Start Cube * **POST** `/api/elasticubes/{server}/{instanceId}/stopInstanceId` — Stop cube ny instance id * **POST** `/api/elasticubes/{server}/{title}/restart` — Restart cube by cube name * **POST** `/api/elasticubes/{server}/{title}/start` — Start Cube * **POST** `/api/elasticubes/{server}/{title}/stop` — Stop cube by cube name * **GET** `/api/elasticubes/servers/{server}/{title}/lastBuildTime` --- --- url: 'https://developer.sisense.com/guides/restApi/using-rest-api.md' --- # Getting started with the Sisense REST API ## Accessing the Sisense API There are 3 main ways to access the Sisense API: * Via the interactive documentation ("Swagger-UI") that lets you see which APIs exist, what parameters they take, and even lets you try them out. * Via a tool meant to run HTTP requests, such as `postman` or `curl`. * Via your own application or script. For any type of use outside of our documentation, you will need to obtain and use an authentication token, using a process outlined below. To use our internal tool, you will only need to be logged in to Sisense.\* ### Using the API documentation You can open the API reference by following these steps: 1. Open the Sisense Web Application and go to the "Admin" page. 2. Click on the "REST API" tab in the left-side navigation panel. 3. Select the desired REST API version (defaults to `v1.0`). ![An image](./img/Adminref.png) ::: tip REST API Versions Currently, Sisense has 3 REST API versions: `v0.9`, `v1.0`, and `v2.0`.\ You will need to select the correct version for the API endpoint you need.\ Most API operations are exclusive to a specific API version, and do not repeat across other API versions.\ Each version represents a significant change in the Sisense API standard. ::: ::: warning When using SSL/HTTPS If you're accessing Sisense via HTTPS, in some versions of Sisense you will have to manually select the `HTTPS` schema at the top of the Interactive API Reference screen: ![HTTPS schema selector at the top of the Interactive API Reference screen](./img/https.png) ::: ### Using a programming language If you're familiar with `javascript` and would like to work with the Sisense API from a script, we recommend trying out [Node.js](https://nodejs.org/en/), but you can use other scripting languages such as [Python](https://www.python.org/), any non-script language (such as `Java` or `C#`) or by sending requests from your web application, using a library that supports `ajax` requests such as `jQuery`, etc. ## Using the Sisense API Below you'll find important information on how to use the Sisense REST API correctly, regardless of the method you choose. ### Authentication The Sisense REST API requires that you send an authentication token with each request. The token lets the server verify your identity. In Sisense each user has their own API Token that must be included in the header of the request. This procedure is described below. #### Getting the API Token from User Profiles ::: warning Feature Availability This is a new feature available only from version , and is not enabled by default. ::: Starting from version you can use the [**User Profiles**](https://docs.sisense.com/main/SisenseLinux/setting-user-profile.htm?Highlight=regenerate%20an%20api%20token#copy-andor-regenerate-an-api-token) UI in order to retrieve or renew your API token. ![User Profiles](./img/Profile_APIToken_TG.png) ::: danger Caution Exercise caution before regenerating an API token. Integrations using the existing API token stop working until updated with the new token. ::: #### Getting the API Token from the Authentication API 1. In the Sisense Application, select **Admin** > **REST API**. 2. Click **REST API Reference** to view the list of operations and API documentation. 3. Access the endpoint in v1 of the REST API at \ ![An image](./img/authrun.png) 4. In the authentication/login endpoint, enter the following details: **Body** *(Using `x-www-form-urlencoded`)* | Property | Value | |------------|-----------------------------------------| | `username` | The username you log in to Sisense with | | `password` | The password you log in to Sisense with | The resulting HTTP request should look like: ``` POST /api/v1/authentication/login HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded username=user%40example.com&password=12345678 ``` ::: tip Note The username must be URL encoded. For example @ should be written as %40. ::: You will then receive a response with your authentication token in a JSON response. **Code Example** Below is an example of JavaScript code that sends this request, using `jQuery`: ```js var settings = { "async": true, "crossDomain": true, "url": "https://example.com/api/v1/authentication/login", "method": "POST", "headers": { "content-type": "application/x-www-form-urlencoded" }, "data": { "username": "user@example.com", "password": "12345678" } } $.ajax(settings).done(function (response) { console.log(response); }); ``` #### Using the API token in requests For every API call you must include the following header: | Header | Value | |-----------------|------------------------| | `Authorization` | "Bearer " + your token | ::: tip Note There is a single space between "Bearer" and your token. ::: **Example** ``` GET /api/v1/some/api HTTP/1.1 Host: example.com Content-Type: application/json Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiNTZiOWM3YTFiZGQ2YmViNDY1MDNjZWNlIiwiYXBpU2asdffaDU3N2QtODZiOC01MmU2LTQyOTYtYjIxZjk3NzhjZjYyIiwiaWF0IjoxNDYxNTgzNzc5fQ.vTB2fugl72db0tPr184tP5fd6e1SBZqDfYN2vedZOEY ``` ### Using an HTTP request tool #### Postman A popular tool for running REST API calls is [Postman](https://www.postman.com/) , which lets you run HTTP requests using a dedicated GUI. The HTTP requests can be executed to view the result, or saved as code snippets in a variety of programming languages. 1. Select the method type and enter the endpoint in the URL field.![An image](./img/authapi3.png) 2. Under the Header tab, in the **Key** field, enter **Authorization** . 3. In the **Value** field, enter your the word Bearer and your User Token, which can be retrieved through the Post method authentication/login/ endpoint in version 1.0 of the REST API.\ ![An image](./img/authapi4.png) 4. Click **Send** . The response is displayed in the Body area. ### cURL You can also use [cURL](https://curl.haxx.se/) from command line, or any other tool that can run HTTP requests with your headers and data.  The token you must include in the Header for version 6.0 and later is the User Token, which you can retrieve through the Post method authentication/login/ endpoint in version 1.0 of the REST API ``` curl -X GET --header "Accept: application/json" "https://example.com/api/dashboards" --header "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiNTZiOWM3YTFiZGQ2YmViNDY1MDNjZWNlIiwiYXBpU2asdffaDU3N2QtODZiOC01MmU2LTQyOTYtYjIxZjk3NzhjZjYyIiwiaWF0IjoxNDYxNTgzNzc5fQ.vTB2fugl72db0tPr184tP5fd6e1SBZqDfYN2vedZOEY" ``` ### Conventions Below we will outline some of the conventions used in the Sisense REST API. Please note that some of these conventions are only relevant to `>0.9` versions. #### HTTP Methods An HTTP request can be of the following 5 types, each explicitly defining the kind of result this request is trying to achieve. ##### GET `GET` requests are meant to retrieve data. When applied to a collection, such as `users` they will usually return the collection, and when applied to an identity such as `users/someuserid` they will usually return the entity this id represents. ##### POST `POST` requests are meant to create entities or perform actions. A `post` request will contain an entity's data as it's payload, and it will be added to the relevant collection (for example, adding a new user). *Note that for most entities, such as users, there are validation rules preventing duplication (for example, it's impossible to add 2 users with the same user id) meaning that a second `POST` with the same user JSON will result in an error.* ##### PUT `PUT` requests are a way to replace an existing entity. When passing an object via `PUT` if the entity of the same ID exists it will be replaced, meaning that if certain fields that existed previously are absent from the passed data, they will not be kept. *This means that a `PUT` request's payload should contain all required fields of the entity!* ##### PATCH `PATCH` requests are a way to update an entity without replacing it. The provided data will be merged with the existing data, so that only fields specified in the request will be updated and the rest will remain as they were. ##### DELETE `DELETE` requests are meant to delete an entity. Sometimes, it might be possible to `DELETE` an entire collection. #### Special Fields The new Sisense API supports a standard set of fields for `GET` requests, allowing for increased flexibility in the way data is retrieved and used. ##### Fields Allows you to declare specific fields of the entity you're getting. Can be comma delimited, **without spaces**, and `-` can be used for exclusion. For example: * `fields: "firstName,email"` will get only the name and email fields of a user:\ `/api/v1/users?fields=firstName,email` returns ```js [{ "email": "user@sisense.com", "firstName": "you.ser" }] ``` * `fields: "-hash"` will get all fields of a user except for the password hash *Notes:* 1. If a field that doesn’t exist is passed, the request will **not** result in an error but rather will return an array of empty objects (in essence will return a value for each object with no fields) 2. Passing both inclusive and exclusive fields in one request will result in an error code 500. For example, `fields:"firstName,-lastName".` ##### Sort Allows you to sort the returned data by the specified field, where normally data is sorted in ascending order, and `-` indicated descending order. For example: * `sort: "name"` will sort users by their name, ascending * `sort: "-name"` will sort users by their name, descending ##### Limit & Skip These two fields allow you to get a specified number of results (`limit`) at a specified offset (`skip`), which is useful for server-side paging. For example: `limit: 10, skip: 20` will return the third set of 10 results ##### Expand The `expand` field lets you define foreign-key fields which you would like to replace with their actual entities. For example, a `user` might have a property called `groups`, an array of ID's where each group is an entity in it's own. Using `expand: "groups"` we will get each `user` object with an array of actual `group` objects instead of their IDs. This field can be combined with others (for example `expand: "groups(fields:name)"` will get only the name field of each group to replace the group ID) and can be nested (`expand: "groups,groups.users"` will get a user's groups, and their collection of users). For example, the request may return: ```js [ { "_id": "552398f8e5fd8174a8000003", "active": true, "created": "2015-04-07T08:44:40.169Z", "email": "user@sisense.com", "firstName": "you.ser", "groups": [ "562fb6bb479b8c3442000068" ] } ] ``` While will return: ```js [ { "_id": "552398f8e5fd8174a8000003", "active": true, "created": "2015-04-07T08:44:40.169Z", "email": "user@sisense.com", "firstName": "you.ser", "groups": [ { "roleId": null, "name": "some group", "ad": false, "objectSid": "", "dn": "", "uSNChanged": "", "mail": "", "created": "2015-10-27T17:39:07.470Z", "lastUpdated": "2015-10-27T17:39:07.470Z", "_id": "562fb6bb479b8c3442000068" } ] } ] ``` ### Errors Errors returned by the new API have been standardized in structure, and will always contain the following properties: * `status` - an HTTP status code such as `404` or `401` * `httpMessage` - the standard message for the HTTP status, such as `Not Found` * `code` - the Sisense error code, which lets you identify the actual problem (see reference) * `message` - a text message explaining the error code Depending on the error type, other fields might be present. For example, for a `not found` exception, the fields `resourceType` and `resourceName` will be present, to indicate exactly what resource was not found. An error example: ```js "error": { "code": 202, "message": "User 'd59665_athisisauserid6235_fs' not found", "resourceType": "user", "resourceName": "d59665_athisisauserid6235_fs", "status": 404, "httpMessage": "Not Found" } ``` --- --- url: 'https://developer.sisense.com/guides/restApi/v0/index.md' --- # REST API v0.9 Reference --- --- url: 'https://developer.sisense.com/guides/restApi/v1/index.md' --- # REST API v1.0 Reference --- --- url: 'https://developer.sisense.com/guides/restApi/v2/index.md' --- # REST API v2.0 Reference ::: tip Additional References for Datamodels API * [Connectors Reference](../datamodels/connectors.md) * [Data Types Reference](../datamodels/data-types.md) ::: --- --- url: 'https://developer.sisense.com/guides/sdk/index.md' --- # Compose SDK Compose SDK is a software development kit that enables a composable, code-driven way to use Sisense platform capabilities. Build analytics and data-driven experiences into your product with code using Compose SDK, a set of client-side libraries and components for query composition, data visualization, and more. * **Create Sisense queries, charts, and filters directly from your application code.** No predefined dashboards or widgets required - or render existing widgets by ID. Mix and match approaches to fit your needs. * **Composable, modular and extensible.** Use our components, customize them, or bring your own. Compose SDK works equally well for building new applications or upgrading existing ones to use Sisense’s powerful analytics platform. * **Built with developer experience in mind.** The SDK is available via GitHub and NPM, supports TypeScript and and common UI frameworks (React, Angular, Vue), and includes documentation, code samples and CLI tools that help you get things done with ease. ::: tip Demo Application Check out our demo applications for Compose SDK + [React](https://csdk-react.sisense.com), [Angular](https://csdk-angular.sisense.com), or [Vue](https://csdk-vue.sisense.com). ::: ### What can I do with Compose SDK? * Execute composable queries on Sisense Data Models directly from your client application and use the data to drive your application's behavior, or render custom visualizations * Render ad-hoc data visualizations generated from code, based on Sisense data or your own * Embed visualizations from pre-defined Sisense Widgets * Create interactive data exploration with Filter components ### Who can use Compose SDK? Compose SDK is available to all Sisense Customers. To use Compose SDK, you will need: * Access to a Sisense instance with Sisense Linux version `L2022.10` or later * An application built using [React](./getting-started/quickstart.md), [Angular](./getting-started/quickstart-angular.md), or [Vue](./getting-started/quickstart-vue.md) and TypeScript * A NodeJS package manager (`npm` or `yarn`) --- --- url: 'https://developer.sisense.com/guides/sdk/CHANGELOG.md' --- # Changelog ## \[2.33.1] - 2026-08-06 ### Changed * Fix misaligned date labels and data points in charts with continuous timeline enabled. ## \[2.33.0] - 2026-08-04 ### Added * Promote `SankeyChart` to General Availability (GA) for React, Angular and Vue * Promote widget renaming to General Availability (GA) * Promote widget header menu customization to General Availability (GA) * Add dashboard header customization support for Angular and Vue * Add cross-filtering support for widgets using calculated dimensions * Add filter editing support for text and numeric calculated dimensions * Add `FilterWidget` component (for internal testing) * Extend `KpiChart` (beta): * Add typed conditional icons supporting 16 built-in icons, custom text and SVG paths * Add `showValueTitle` and `showCategoryTitle` options * Add `ofGoalText` and `toGoText` overrides for the target comparison text * Localize built-in strings, percent formatting ### Changed * Change `KpiChart` (beta): * Rename `trend` to `category` for `KpiChartDataOptions` * Rename `big-comparison` option to `comparison-first` for `KpiStyleOptions.layout` * Change `KpiTitleStyleOptions.enabled` to also include the category caption * Change `KpiIconCondition.icon` to accept a `KpiIcon` object instead of a string * Fix value clipping and comparison truncation on narrow and short cards * Fix `KpiCardStyleOptions.textAlign` to also align `comparison` * Fix `comparison` to apply `name` and `numberFormatConfig` when using `StyledMeasureColumn` * Fix `% of goal` decimal rounding issue * Change default color for `value` to use the first color in the palette * Fix the `highlights` prop for `SankeyChart` to set chart highlights not filters * Change `SankeyChart` tooltip style to align with the other chart types * Fix member filters on numeric columns to avoid duplicate members, keep selections, and preserve deactivated members * Add `title` on `QueryResultData` columns in addition to the existing `name` property * Fix `useGetDataSourceDimensions` and underlying field search to work correctly for all data model types (including perspectives) ## \[2.32.0] - 2026-07-21 ### Added * Add `KpiChart` including comparison, conditional colors, and sparkline (Beta) * Promote widget Excel export to General Availability (GA) * Add calculated dimension filter support: * Create calculated dimension filters in code * Load calculated dimension filters created in Fusion dashboards ### Changed * Fix empty Sunburst chart when configured with four or more categories * Fix drilling and highlighting for Sankey chart * Fix error when a data source column is named "title" or "description" ## \[2.31.1] - 2026-07-13 ### Changed * Extend `DashboardById` with Sankey widget support and series labels settings (font color, size, and style) * Fix query building error when filter and breakdown dimensions use different date levels on the same date column ## \[2.31.0] - 2026-07-07 ### Added * Add table column resizing, with configurable minimum/maximum widths * Add embed code generation for custom widgets in React, Angular, and Vue * Move following to to General Availability (GA): * dashboard header customization API * widget narrative configuration * "Download as CSV" widget action * Move module registration API and widget plugin state persistence APIs to beta ### Changed * Improve dashboard header layout so taller custom items are no longer clipped * Fix Fusion table widgets losing their per-column widths when rendered in Compose SDK * Fix an explicit "no sort" setting bug in table queries * Fix incorrect forecast/trend chart titles for certain measures * Deprecate `DashboardConfig.toolbar.visible` in favor of `DashboardHeaderConfig.visible` ## \[2.30.0] - 2026-06-23 ### Added * Promote Widget Plugins to public beta * Add new `SankeyChart` visualization component for React (beta) * Add `includeRowCount` query option to retrieve the total row count alongside query results (beta) * Add Top and Bottom (ranking) filter options to the filter editor * Add dashboard module system and dashboard header customization APIs (for internal testing) ### Changed * `PivotTable` fixes: * Fix incorrect row sorting when sorting by measure on the last row * Fix the measure column being dropped when sorting the last row dimension by its values * Fix the loading spinner never clearing when a query fails, now showing the error * Fix Jump to Dashboard icon overlapping the widget warning icon * Fix `MemberFilterTile` to show inline errors when a member query fails * Fix break-by colors not applying to members whose keys contain encoded characters * Fix number formatting in chart series names * Fix calculated dimensions truncating dates when a date column is used in the formula context * Fix issue with multiple filters on calculated dimension filters returning incorrect results * Improve Excel widget export to respect applied filters and apply number formatting (for internal testing) * Fix display name support in generated data models ## \[2.29.0] - 2026-06-10 ### Added * Add calculated attributes (calculated dimensions) support: * Add `attributeFactory.customFormula` factory for creating formula-based calculated attributes * Extend existing visualizations and widgets to accept `CalculatedColumn` in data options * Add `formatNumber`, `formatDate`, `getDefaultDateFormat`, and `formatDataSet` formatting utilities * Add persistence support for `styleOptions` and `customOptions` properties in custom widget plugins (for internal testing) ### Changed * Fix missing tooltip on members in `MemberFilterTile` * Fix `CalendarHeatmapChart` day and month label formatting and timezone date offsets * Fix broken filter relations in case of having multiple filters from different data sources * `PivotTable` improvements: * Fix stale cell colors and number formatting after data options configuration update * Fix auto-height not accounting for drilldown breadcrumb height * Fix content clipping when internal padding (e.g., `spaceAround` spacing) is applied ## \[2.28.0] - 2026-05-26 ### Added * Add Excel download support for widgets (for internal testing) * Add `useTheme` hook for accessing the active theme settings ### Changed * Fix dashboard column resize when reaching `maxWidth` * Fix widget drag interaction obstructed by widget title text * Fix pivot query sorting configuration when sorting by measure with no pivot columns * Fix `IncludeAll` highlight filter causing JAQL query failure * Fix chart navigator rendering when zoom range falls outside data bounds * Fix pivot widget loader vertical centering inside explicit-height containers ## \[2.27.0] - 2026-05-13 ### Added * Add `create-plugin` command to `sdk-cli` (for internal testing) * Extend `WidgetDTO` round-trip translation coverage for duplicate widget support ### Changed * Fix `Table` error when switching column aggregation * Fix SSO authentication race condition on concurrent requests * Fix incorrect filter creation for charts with two date-level categories of the same dimension * Fix widget-level filters not applied for aggregated measures in JAQL * Fix missing dashboard palette in dashboard model in Angular and Vue * Fix `PivotTable` sorting checkbox not toggling on click * Fix step line chart when switched back to basic or spline subtype * Fix title replacement in filter tile when cross filtering * Fix drill down context menu edge cases ## \[2.26.0] - 2026-04-28 ### Added * Add `useGetDataSourceDimensions` hook to `sdk-ui-angular` and `sdk-ui-vue` * Add `enableSilentPreAuth` support to Vue `SisenseContextProvider` ### Changed * Fix Autozoom navigator appearance when using multiple date granularities on the X-axis, use saved zoom state * Fix "No Results" message not showing for box & whisker chart * Fix URL query parameter persistence for `sisenseUrl` during SSO redirect * Fix Fusion dashboard persistence when using filter relations * Fix date formatting and tooltip for dual X-axis charts to be aligned with Fusion ## \[2.25.0] - 2026-04-14 ### Added * Add plugins cross-framework support for internal testing * Add troubleshooting article for SSO in Safari browser ### Changed * Change default date sort to descending in member filter tile to match Sisense Fusion * Fix infinite render in table component when using filter relations * Fix dashboard filter persistence race conditions * Upgrade `dompurify` to 3.3.2 in `sdk-ui` and `sdk-pivot-ui` ## \[2.24.0] - 2026-03-31 ### Added * Add maximum redirect limit for SSO authorization * Add CSV download support for widgets (internal testing) ### Changed * Make custom formula context optional in `sdk-data` * Improved style isolation to avoid unintended overrides * Accessibility improvements: * Increase the minimum touch target size for interactive buttons * Improve form label accessibility * Improve `aria-label` usage * Add alternative text for the "No results" image * Improve color contrast for `NoResultsOverlay` ## \[2.23.0] - 2026-03-17 ### Added * Add widget renaming (title change) for internal testing * Add aggregations in data browser for internal testing * Add `CalculatedMeasure` support in measure filter functions ### Changed * Fix error related to `iconSize` prop forwarding * Fix dashboard filter relation logic when widget has overlapping filters * Fix scatter chart discrepancy caused by invalid data options * Fix double toggle on the checkbox in multi-selection filters * Improve forecast error handling related to formulas ## \[2.22.0] - 2026-03-03 ### Added * Add "Duplicate widget" functionality to the dashboard (internal testing) * Add lock/unlock menu for filter tiles (internal testing) * Add basic plugins infrastructure for internal testing * Extend the AI `Chatbot` component with quota limits notification ### Changed * Fix bracket handling in custom formula * Fix menu positioning for elements located near the right edge of the page * Fix pivot sorting logic for formulas ## \[2.21.0] - 2026-02-17 ### Added * Enable cross-filtering for `CustomWidget` in dashboard * Add support for Angular `FormulaService.getSharedFormula` * Add drag and drop filters reordering on filters panel (internal testing) * Add configurable widget toolbar menu (internal testing) ### Changed * Fix `sdk-ui-angular` dependency installation freeze in npm * Fix shared formulas issues in `DashboardById` * Improve aggregate functions validation in analytics composer and `nlqTranslator` custom formulas ## \[2.20.0] - 2026-02-03 ### Added * Add Angular v21 support * Add `LoadingOverlay` component for React * Add translation files for all Fusion languages as submodules for React, Angular, and Vue ### Changed * Fix `TableStyleOptions.rowsPerPage` configuration for table widgets loaded from Fusion * Fix `RelativeDateFilterTile` and `DateRangeFilterTile` behavior in the dashboard filters panel * Fix forecast and trend visibility when series are hidden via the legend * Fix language and locale configuration loaded from Fusion * Improve translations and error messages across packages ## \[2.19.0] - 2026-01-20 ### Changed * Optimize NLQ chart: performance upgrade, minor improvements * Improve dashboard layout adjustment upon tabber widget deletion * Improve configuration for loading indicator behaviour * Improve dashboard layout editing history * Fix issues related to 'Other' section in `PieChart` component * Fix `PivotTable` subtotals formatting issues * Fix inconsistent dashboard toolbar icon theming ## \[2.18.1] - 2026-01-09 ### Changed * Fix a build issue in the `@sisense/sdk-ui-angular` package when used in environments with restricted build setups ## \[2.18.0] - 2026-01-06 ### Added * Add lazy loading for `MembersFilterTile` list * Add `useJtdWidget` equivalent for Angular (`createJtdWidget`) and Vue (`useJtdWidget`) * Add support for specifying `tabInterval` and `tabsSize` in pixels for `TabberButtonsWidget` * Add `onDataPointClick` and `onDataPointContextMenu` callbacks for `PivotTable` components * Handle legacy Tabber configuration if used by existing Fusion widgets * `PivotTable` improvements: * Add support for the `autoHeight` style option inside the `Dashboard` * Add drilldown support ### Changed * Fix drilldown functionality for widgets where the same category is added multiple times * Fix continuous timeline translation from Fusion widgets * Fix timezone shift in quarters formatting * Improve row limit message in Pivot ## \[2.17.0] - 2025-12-22 ### Added * Add `widgetModelTranslator.toWidgetProps` transformer to be able to use widget models in the `Widget` component. * Extend DataPoint with `entries` - additional `dataOptions` context of the interacted DataPoint. * Add `useGetDataSourceDimensions` hook for loading the datamodel at runtime. ### Changed * Split `sdk-pivot-client` into 2 separate packages - `sdk-pivot-query-client` (pure data layer) and `sdk-pivot-ui` (ui components) * Move component `PivotTable` from beta to General Availability (GA) * Fix overlap of cross-filtering and JTD in Pivot * Load Fusion palette by default when using WAT (Web Access Token) authentication * Fix redundant JAQL requests in Jump to Dashboard functionality * Accept ISO date strings without timezone in NLQ (Natural Language Query) translator * Fix issue with opening "add filter popup" ## \[2.16.1] - 2025-12-12 ### Changed * Fix pivot web socket connection when the Sisense instance is configured with a proxy url ## \[2.16.0] - 2025-12-09 ### Added * Add `StreamgraphChart` component for React, Angular, and Vue * Add `semiCircle` boolean to style options for `PieChart` * Add cross-filtering support for `PivotTable` interactions * Add `alwaysShowResultsPerPage` to `PivotTable` for 'Rows per page' to be visible on single-page results * Add `imageColumns` style option to translate image cells in PivotTable ### Changed * Fix DataSourceFieldsBrowser error when a column is named `name` * Fix pivot sorting popup interactions triggering cell click handlers * Fix pivot tooltip wrapper to avoid Angular/Vue bridge errors when showing result limit alert icons * Adjust table/pivot default padding for consistent layout * Fix polar and scatter rendering inconsistencies after drilldown selection, stabilize highlight handling * Ensure live datasource type detection in widget translator, require address only for non-live sources * Fix filter panel horizontal scroll when vertical scrollbar appears * Apply JAQL sort instructions to custom formula measures during creation * Improve filter editor theming with hyperlink hover color and button theme settings * Enhance analytics composer translator to handle exclude filters and preserve original column names ## \[2.15.0] - 2025-11-25 ### Added * Add `PivotTableWidget` component for Vue * Add `useExecutePivotQuery` composable for Vue * Add `isAutoContentWidth` pivot style option to automatically adjust column widths to fit the component size * Add `AppConfig.chartConfig.tabular.htmlContent` configuration to allow html content in `Table` and `PivotTable` components * Add troubleshooting guide for dependency conflict resolution (includes MUI example) * Extend `seriesLabels` for `SunburstChart` ### Changed * Fix missing data limits notification in `PivotTable` component * Fix `TableStyleOptions.columns.width` option in table widget translation layer * Fix missing updates of handler props in Highcharts-based chart components * Prevent categorical chart misconfiguration by restricting its data options ## \[2.14.0] - 2025-11-11 ### Added * Add `onChange` property for `Dashboard` and `DashboardById` * Add support for continuous subtype for `CalendarHeatmapChart` * Add possibility to change column count in `Dashboard` edit mode * Add support for `isHtml` data option in `PivotTable` * Extend `seriesLabels` for `TreemapChart` and `FunnelChart` ### Changed * Fix `PivotTable` to use column headings from `name`, if provided via dataOptions * Fix `PivotTable` scroll issue ## \[2.13.0] - 2025-10-28 ### Added * Add `measureTopRanking` and `measureBottomRanking` filters * Add `tabbers` configuration to `Dashboard` and `DashboardById` configs * Add check if user is allowed to use edit mode in `DashboardById` * Extend charts styling options: * Add `GradientColor` to color options * Extend `seriesLabels` for `PieChart` and `ScatterChart` ### Changed * Fixed shared formulas references resolution in `useGetDashboardModels` hook * Fixed paging initialization in `PivotTable` for React 19 ## \[2.12.0] - 2025-10-14 ### Added * Add pagination configuration for `CalendarHeatmapChart` * Add filter panel toggle button in dashboard toolbar to show/hide filter panel * Add styling support for total labels in stacked charts (Column, Bar, Area) * Add `shadow` property to `LegendOptions` for controlling shadow effects on chart legends ### Changed * Improve Tabber widget integration with editable dashboard layouts * Improve `MemberFilterTile` to display values which do not exist in the dataset (when applied externally) * Enhance NLQ translator with comprehensive function processing, validation, and error handling * Improve analytics composer code generation to exclude default values from generated style and data options * Fix hover interaction issue on Pie chart legend items * Fix forbidden (403) palette requests preventing charts from rendering with WAT authentication ## \[2.11.0] - 2025-09-30 ### Added * Add `CalendarHeatmapChart` component for React, Angular, and Vue * Extend widget, dashboard components and hooks to support calendar-heatmap chart type * Add code-first `Jump to Dashboard` configuration support, `applyJtdConfig` and `applyJtdConfigs` helper utilities * Add `useJtdWidget` hook to enable `Jump to Dashboard` capabilities for a specific widget * Extend charts styling options: * Add `seriesLabels` prop for data point labels styling * Add `series` prop for better control over group padding (column and bar charts) ### Changed * Improve `Jump to Dashboard`: fix multiple bugs * Improve `useExecuteQueryByWidgetId` hook: add `ungroup` to query for table widget * Rename `Legend` type to `LegendOptions` ## \[2.10.0] - 2025-09-16 ### Added * Add `SisenseContextService.setConfig` method for runtime configuration in Angular * Add calendar-heatmap chart support for internal testing * Improve `Jump to Dashboard`: add jump from pivot widget support * Extend charts line styling options * Extend charts legend styling options ### Changed * Fix redundant JAQL queries in the dashboard cross-filtering behavior * Fix Treemap chart error when only category is provided in `dataOptions` ## \[2.9.0] - 2025-09-02 ### Added * Add custom cell formatter for pivot tables (for internal testing) ### Changed * Improve NLQ to JAQL translation and NLQ Query JSON mapping * Fix issues related to empty data values: prevent N/A from being formatted ## \[2.8.0] - 2025-08-19 ### Added * Add click and context menu support for pivot tables (for internal testing) * Extend `CustomWidget` styleOptions with size properties * Extend theme settings for dividers in dashboard toolbar and filters panel ### Changed * Fix missing formatting for "total" value labels in column chart * Update internal dependencies: React, React DOM, MUI Data Grid and other ## \[2.7.0] - 2025-08-05 ### Added * Add `customPrompt` property for `Chatbot` component * Add `customPrompt` property for `useGetQueryRecommendations` hook * Add header to the `TextWidget` component ### Changed * Improve translation logic for ranking filter JAQL generated by NLQ * Fix missing header in `CustomWidget` when an error occurs * Fix race condition in the `ThemeService.updateThemeSettings` method in Angular * Fix runtime error in `AreamapChart` when used in Webpack-based apps * Fix missing `CustomWidgetProvider` issue in JTD in Angular and Vue * Fix broken types in Angular and Vue ## \[2.6.0] - 2025-07-22 ### Added * Add alternative SSO host support for internal testing ### Changed * Improve charts resizing functionality * Improve default date granularity, set to `Years` when not provided * Fix multiple bugs in JTD (Jump to Dashboard) ## \[2.5.0] - 2025-07-08 ### Added * Add Angular v20 support * Add `name` property for `StyledColumn` and `StyledMeasureColumn` ### Changed * Improve editable dashboard layout: add default min and max cell size, fix drag handle * Improve `Jump to Dashboard`: filter priority, multiselect, widget title, documentation * Improve cascading filters: child members are now restricted by parent filter * Improve trend/forecast: for multiple functions, report errors per function and display valid results * Fix `useGetDashboardModels`: do not return dashboards without oid * Fix filter issues related to `Include All` state * Fix incorrect date labels due to daylight saving timezone shift ## \[2.4.1] - 2025-06-25 ### Changed * Improve SSO Router check to include cases where proxyUrl is used ## \[2.4.0] - 2025-06-24 ### Added * Add an option to delete widget in edit dashboard layout mode * Add custom widget registration flow for Angular and Vue * add `CustomWidget` component support * allow providing framework specific components * add guides for React, Angular and Vue * Add `FilterEditor` and `FiltersPanel` component support for Angular and Vue * Add `useGetFilterMembers` hook support for Angular and Vue * Add `useExecuteCustomWidgetQuery` hook support for Angular and Vue ### Changed * Improve editable dashboard layout: add theming, extend configs, support distributing cells equally in a row * Extend `IndicatorChart` and `TextWidget` components with `onDataPointClick` callback ## \[2.3.1] - 2025-06-17 ### Changed * Improve SSO authentication to be compatible with SSO Router plugin ## \[2.3.0] - 2025-06-11 ### Added * Add compatibility support for React v19 * Add `line/step` chart subtype * Add container customization support for `PluginWidget` * Add `widgetsPanel.editMode` configuration to enable editable dashboard layout (alpha) * Add limited `Jump To Dashboard` add-on support (alpha) ### Changed * Fix `TabberWidget` error handling for unsupported old add-on versions * Refactor `WidgetById` component, apply dashboard-level palette to it * Improve AI chat: add Markdown support in messages * Enable UMD build output for `@sisense/sdk-ui` package ## \[2.2.0] - 2025-05-27 ### Added * Add editable dashboard layout for internal testing: * Add drag-and-drop and resizing capabilities for widgets * Add layout history management UI in dashboard toolbar * Support persistence of dashboard layout to Fusion ### Changed * Extend the `onError` handler in `SisenseContextProviderProps` to support custom error box visualization * Fix a rendering issue that may occur from runtime errors happening while `showRuntimeErrors` is disabled in `SisenseContextProviderProps` * Migrate CSDK packages from version ranges to fixed versions to prevent internal dependency mismatches ## \[2.1.0] - 2025-05-13 ### Added * Add `titleFontSize` property to `WidgetThemeSettings.header` for configuring widget header font size ### Changed * Apply timezone from the date configuration correctly * Enable `TabberWidget` by default for better user experience * Fix `PivotTableWidget` height when `isAutoHeight` option is enabled * Prevent horizontal scrollbar from appearing on `Table` with auto column width * Fix bar chart labels overlap issue ## \[2.0.0] - 2025-04-30 ### Added * Add Angular and Vue support for `useComposedDashboard` hook * Add `Widget` component support for Angular and Vue * Add `FilterTile` component for Angular and Vue * Move the following features to General Availability (GA): * `DashboardById` persistency * `useComposedDashboard` hook * `Widget` component * AI components and hooks * `AreaRangeChart` component * `FilterTile` component * `SisenseContextProviderProps.enableSilentPreAuth` * `Chart.onDataReady` * `ExecuteQueryProps.ungroup` * `AppConfig.translationConfig` * Apply `composeCode` to `measureFactory` and `filterFactory` * Implement embed code for `Dashboard` ### Changed * **Breaking:** The minimum supported version of Angular is now v17 * **Breaking:** The minimum supported version of React is now v17 * **Breaking:** Remove deprecated methods from `WidgetModel` API * **Breaking:** Remove deprecated `DashboardWidget` component * **Breaking:** Separate CSDK `WidgetType` and `FusionWidgetType` * **Breaking:** Remove deprecated `PivotGrandTotals.title` prop * **Breaking:** Remove other deprecated props and methods * **Breaking:** Improve types for Vue and Angular components > *See [migration guide](./guides/migration-guide-2.0.0.md) for more details.* * Fix widgets not showing when switching dashboards when using tabber * Fixed border issue for filter tiles * Return only valid color format conditions * Improve pivot rendering performance * Improved handling of keys in `seriesToColorMap` * Align Vue component props validation with corresponding types * Improve component handler types in Angular * Highlight selected days correctly during multi-selection in date selector ## \[1.34.0] - 2025-04-15 ### Added * Add cascading filters editing support by extending `FilterTile` and `FilterEditorPopover` components * Add `executeCsvQuery` method to `queryService` for Angular * Add `useExecuteCsvQuery` composable for Vue * Add filters creation and editing possibilities into `Dashboard` and `DashboardById` components in Angular and Vue for internal testing ### Changed * Improve `FilterEditorPopover`: fix incorrect members for datetime "day" granularity filter, add missing default "from" value for numeric filter, clear previous condition value for textual filter, allow updating filter with deactivated members only * Improve `AddFilterPopover`: restrict filter creation on an attribute if one already exists in the dashboard, add caching for datasource fields loading, handle missing datasource cases, fix search field focus outline * Improve `Table`: fix non-functional `StyledColumn.sortType` sorting configuration * Fix broken forecast and trend for measures with `count` aggregation over textual attributes * Improve testing: cover filter model logic in `sdk-data` package with unit tests ## \[1.33.0] - 2025-04-01 ### Added * Add filters creation and editing possibilities into `Dashboard`, `DashboardById` and `FiltersPanel` components * Add `PivotTableWidgetComponent` for Angular * Add `executePivotQuery` method to `queryService` in the Angular package * Add `widgetModelTranslator`, `dashboardModelTranslator` and `dashboardHelpers` for Angular and Vue * Add `config` property in the `Dashboard` and `DashboardById` components * Add responsive widget layout support to the `Dashboard` and `DashboardById` * Add AI and `Chatbot` functionality support for Vue ### Changed * Improve `FilterEditorPopover`: add deactivated members and unsupported filters handling, add theming support * Improve `AddFilterPopover`: add theming support, add search and lazy loading * Improve docs: update broken links * Improve accessibility: add noticeable outline to focusable elements * Fix dashboard layout for cells with no height or numeric hight * Move the `persist` property of `DashboardById` into its config * Improve tests: add filter edit and filter creation visual tests, update dark theme screenshots ## \[1.32.0] - 2025-03-18 ### Added * Add AI and `Chatbot` functionality into Angular * Add support for columns and rows in "Break By" * Add search for `FilterEditorPopover` members lists for internal testing * Add datetime fields support in `FilterEditorPopover` component for internal testing * Add Tabber widget support for internal testing * Add basic `GroupedItemsBrowser` and `DimensionsBrowser` components for internal testing ### Changed * Update the `MembersFilterTile` UI to reflect the `enableMultiSelection` filter option * Fixed triggering of the component/hook init tracking event when the app initializes * Improve Widget Embed Code: update code templates * Improve charts: align text widget spacing with other widgets and unify tooltip formatting * Improve accessibility: add labels to links that open in a new tab for assistive technology * Improve testing: update visual tests to match new Fusion theme color, update unit test for Table chart ## \[1.31.0] - 2025-03-04 ### Added * Add numeric filters support in `FilterEditorPopover` for internal testing * Add lazy loading for `FilterEditorPopover` members lists for internal testing ### Changed * **Breaking:** Make `chartRecommendations` in `NlqResponseData` internal * Update `FilterEditorPopover` selects to use `Popper` instead of `Popover` * Change prefix for CSS variables in Tailwind to avoid conflicts (`--tw` → `--csdk-tw`) * Fix chart re-rendering when a trend/forecast fails after a props change * Improve charts: fix `Pivot` pagination panel visibility after changing the results per page, fix invalid axis points related to navigator * Improve Widget Embed Code: add `StyledColumn` and `StyledMeasureColumn` support, fix missing filters prop * Improve testing: add `Chart` component mocking with user interactions, add more pie chart cases to visual tests ## \[1.30.0] - 2025-02-18 ### Added * Add pivot cell coloring and conditional styling * Add `dataReady` property support for Angular charts and widgets * Add an option to control the verbosity of the NLG summarization * Add theming support on `FilterEditorPopover` component for internal testing ### Changed * Improve charts: fix missing formatting for pivot, number formatting for trend/forecast features, enhance pivot performance * Improve types of `CriteriaFilterTile` component's props * Deprecate `PivotGrandTotals.title` prop * Improve testing: added pivot visual-regression tests, cover textual filter editing with unit tests * Improve code-templates in analitycs-composer * Improve AI `Chatbot` with context details * Fix compatibility with old React versions ## \[1.29.0] - 2025-02-04 ### Added * Add total and percentage value labels support in `ColumnChart`, `BarChart` and `AreaChart` * Add filter relations support to `BoxplotChart` * Add prop types for Angular components * Add `FilterEditorPopover` component for internal testing ### Changed * Extend `useGetQueryRecommendations` hook to support `enabled` flag * Fix `useExecuteQueryByWidgetId` hook and `ExecuteQueryByWidgetId` component to support pivot query with rows only * Fix error handling in forecast and trend when chart has no data * Fix missing title in `WidgetById` component for Angular and Vue * Improve dashboard: align scatter chart cross-filtering behavior with Fusion, minor style improvements * Improve `PivotTable`: improve formatting of grand-total and sub-total header cells * Improve theming: apply theme fonts to dashboard filters panel and filter tiles * Improve AI `Chatbot`: add new error messages, pass through error codes from the Sisense REST API * Extend Widget Embed Code to populate code representation for dimensions and measures * Move filter utilities to `sdk-data` package ## \[1.28.0] - 2025-01-21 ### Added * Add React hook for nlq `useGetNlqResult` (beta) * Implement React hook for retrieving filter members, `useGetFilterMembers` (beta) * Add `filterFactory.cascading()` to create a Cascading Filter instance ### Changed * **Breaking:** Rename beta hook `useGetNlgQueryResult` and `GetNlgQueryResult` to `useGetNlgInsights` and `GetNlgInsights`, respectively * Extend hook `useGetQueryRecommendations` (beta) to return `WidgetProps` additionally * Refactor `MemberFilterTile` to use hook `useGetFilterMembers` internally * Move React component `FilterTile` from internal to beta * Refactor `CascadingFilterTileProps.filter` from class `CascadingFilter` to interface `Filter` * Improve `PivotTable`: fix `rowsPerPage` to work with `isAutoHeight` option * Improve charts: add number format config extraction for count aggregations, extend the `Popover` mask to fill the full page * Improve testing: polyfill `document.fonts` for unit tests environment * Improve Widget Embed Code: improve extra imports ## \[1.27.1] - 2025-01-14 ### Changed * Fix internal `Filter.isScope` by default for correct filters comparison * Add tooltip to pivot headers in case of truncated text * Replace shared components in `sdk-pivot-client` by components `sdk-shared-ui` ## \[1.27.0] - 2025-01-07 ### Added * Add AI module `SdkAiModule` to Angular. This module will soon contain Chatbot. * Add `onBeforeRender` callback to Indicator chart * Add component `FilterRelationsTile` for internal use by `Dashboard` and `DashboardById` components ### Changed * Improve change detection for complex calculated measures * Improve charts and theming: hide errors related to insights in NLQ chart, remove redundant zero value label for cartesian charts with 2 categories, move number abbreviations to translation files, improve waiting of fonts loading * Improve filter tiles: add Edit button and `onEdit` callback, add empty `FilterEditorPopover` component for internal testing, refactor common filter tile display ## \[1.26.0] - 2024-12-23 ### Added * Add shared formulas support * Display "No results" for charts without defined dimensions * Extend Widget Embed Code to support pivot widget type ### Changed * Extract shared UI components from `sdk-pivot-client` to `sdk-shared-ui` * Restrict chatbot data topics to only those from the current tenant * Add dashboard filters to the chatbot insights requests * Resolve issue with `scrollerLocation` for disabled navigator ## \[1.25.0] - 2024-12-09 ### Added * Support persistence of filters for embedded Fusion dashboards using flag `DashboardByIdProps.persist` (alpha) * Add `FilterRelations` support for dashboards * Add package `@sisense/sdk-shared-ui` ### Changed * Extend `filterFactory` functions to support the `config` param * Support alternative API calls for dashboard and widgets fetch with WAT * Use translation language for date locale * Extend Widget Embed Code to support code snippets for execute query * Fix date format in `MemberFilterTile` * Handle losing widgets' inner state on the update of `DashboardProps` * Improve testing: visual tests with new Sisense theme, tests for execute query * Improve pivot table: correct the display of `rowsPerPage`, adjust the last row sorting, handle `isAutoHeight` in Dashboard layout and "No Results" case * Improve AI `Chatbot`: allow disabling query recommendations, show all data models queryable, apply filters to `NlqChartWidget` ## \[1.24.0] - 2024-11-25 ### Added * Add internal `sdk-shared-ui` library * Implement `HierarchyService.getHierarchyModels` in Angular * Implement composable `useGetHierarchyModels` in Vue * Implement `RelativeDateFilterTileComponent` in Angular ### Changed * Make `FiltersPanel` collapsable initially in the `Dashboard` component via API * Support delete button on the filter tiles * Enable copying text in `Chatbox` * Improve error handling and `ErrorBoundary` * Add callback `onDataReady` to `TableProps` and `NlqChartWidgetProps` for internal testing * Handle date offset in `RelativeDateFilterTile` correctly * Improve Widget Embed Code in Fusion: unsupported chart type * Improve charts: `TreemapChart` tooltip with translations, display of labels on `PieChart` * Improve advanced charts: display of errors in widget header ## \[1.23.0] - 2024-11-12 ### Added * Add component `NlqChartWidget` for internal testing * Add `fromChartWidgetProps()` and `toWidgetDto()` to `widgetModelTranslator` for internal testing * Implement `typedoc-plugin-diff-packages` to check feature parity across UI frameworks ### Changed * Deprecate component `DashboardWidget` – use component `WidgetById` instead * Implement new `DrilldownWidget.drilldownPaths` in Angular and Vue * Extend `MembersFilter` and `MemberFilterTile` to support single and multi selection * Refactor `analytics-composer/ModelTranslator` to `widgetComposer` and `dashboardComposer` * Refactor component `ChartMessage` (internal) to use `widgetComposer.toWidgetProps` * Fix wrong drilldown menu items on a dashboard * Fix error boxes showing control in `ErrorBoundary` * Improve pivot tables: fix pivot url without trailing slash, add the 'csrf' validation event and trigger the 'register' event in the correct sequence * Improve CI pipeline: move build artifacts to cache and add more nx adaptation * Update code templates for Widget Embed Code in Fusion ## \[1.22.0] - 2024-10-28 ### Added * Add hook `useComposedDashboard` (alpha) for flexible dashboard composition in React * Add hook `useDashboardTheme` for internal testing * Support persistence of dashboard for internal testing * Add callback `onDataReady` to `ChartProps` for internal testing * Add custom translations loader for internal testing * Extend CLI `get-data-model` command to include attribute's data source into resulting data model * Add CommonJS builds to the packages of `sdk-common`, `sdk-modeling`, `sdk-query-client`, `sdk-rest-client`, `sdk-tracking`, and `sdk-preact` to support Jest compatibility ### Changed * Remove internal `enableTracking` property in `SisenseContextProviderProps` * Make `ErrorBox` not show by default * Fix empty pivot due to incorrect socket namespace for custom tenant * Use absolute y-values for pie charts * Align “select/unselect” cross-filtering behavior with Fusion * Migrate `ChartWidget` to use a new internal `useWithDrilldown` hook ## \[1.21.0] - 2024-10-15 ### Added * Add utility methods for manipulating filters of `DashboardProps` * Implement component `CriteriaFilerTile` in Angular * Implement component `RelativeDateFilterTile` in Vue ### Changed * Show filter attribute title in unsupported filter tiles * Make filter panel collapsible in `DashboardById` and `Dashboard` components * Fix error caused by CSS named colors in `ThemeProvider` * Enable forecast and trend in Fusion widgets ## \[1.20.0] - 2024-10-01 ### Added * Add `widgetModelTranslator` for translating between a widget model and related component props * Add `dashboardModelTranslator` for translating between a dashboard model and related component props * Add hook `useExecutePluginQuery` (alpha) for use in plugin components * Implement custom context menu and sub-menu for dashboard cross-filtering and drilldown * Add internal change detection props and hook to coordinate cross filtering and drilldown * Support drilldown hierarchies (including predefined date hierarchies) for `ChartWidget`, `DrilldownWidget` for internal testing * Add hook `useGetHierarchyModels` that retrieves existing hierarchy models from Fusion * Add plugin `highcharts-rounded-corners` for Highcharts (internal charting library) ### Changed * **Breaking:** Restructure `DashboardProps` for beta release: `widgets` to using `WidgetProps[]`, instead of `WidgetModel[]`, `layout` to `layoutOptions`, `widgetFilterOptions` to `widgetOptions` * Deprecate `get*Props()` on `WidgetModel` – use utility functions of `widgetModelTranslator` instead * Move components `DashboardById` and `Dashboard` to beta for React, Angular, and Vue * Support dashboards of multiple data sources * Handle Fusion date formats from locale * Extend data point entries with `displayValue` * Consolidate interface for custom chart plugins * Improve filters: translation of `doesn't equal` filter, update of `CriteriaFilterTile`, formula in ranked filter * Replace `fetch-intercept` with an isolated in-house implementation * Extend CLI `get-data-model` to support perspectives * Improve charts: "No Results" overlay added to all charts, data options validation for trend or forecast, polar chart stacking and value labels disabling * Improve pivot tables: container size, additional visual tests ## \[1.19.0] - 2024-09-17 ### Added * Support loading of fonts from Fusion * Support dashboard rendering of text widgets and chart plugins for internal testing ### Changed * Fix missing spaces in headings for `MemberFilterTile`, `Table`, and `PivotTable` * Extend `DataPoint` types with metadata * Fix rendering of charts without values to match Fusion * Fix pivot table error due to invalid datetime formatting * Improve type guards for narrowing filter types ## \[1.18.1] - 2024-09-04 ### Added * Disable forecast and trend in Fusion widgets temporarily to troubleshoot authorization-related issues ## \[1.18.0] - 2024-09-03 ### Added * Add auto zoom feature to `DashboardWidget` ### Changed * Improve `SisenseContextProvider`: support of Fusion authentication * Extend `measureFactory.customFormula` to support filters * Improve `PivotTable`: proper handling of web socket readiness * Fix `DashboardWidget` with filter relations and highlights * Improve tooltips for forecast and trend * Improve charts: palette colors of `BoxplotChart`, refactoring `ThemeSettings.chart.panelBackgroundColor`, making `color` column optional in `AreamapChart`, support of thousands separator from old `numberFormat` config, axis labels for stacked percent charts * Improve infrastructure: visual tests of dashboard assets of diffent widget types, replacement of CommonJS dependencies (e.g., lodash) ## \[1.17.1] - 2024-08-22 ### Changed * Improve error handling of WAT authentication * Fix an issue in `Table` so user-provided data are sorted in their entirety, instead of per page * Apply widget description as `accessibility.description` for `ChartWidget` ## \[1.17.0] - 2024-08-20 ### Added * Move components `DashboardById` and `Dashboard` to internal alpha for React, Angular, and Vue * Support external usage tracking callback configured through `trackingConfig.onTrackingEvent` of the `AppConfig` * Refactor `ChartWidget` to reuse `DrilldownWidget` internally * Support drill down for scatter chart widgets ### Changed * Deprecate internal `enableTracking` property in `SisenseContextProviderProps` – use `trackingConfig.enabled` of the `AppConfig` instead * Extend `ThemeSettings` to support animation-related config * Improve dashboard rendering: locked filters in cross filtering, resetting levels of `CascadingFilterTile`, highlight of all categories in cartesian charts, dashboard theme setting, matching theme for widget header info panel * Refactor component `Table` to reduce computations and re-renders * Fix issues of charts: legend position of funnel chart, number formatting for indicator's secondary value * Improve `SisenseContextProvider` in React: support of pending `token` or `wat` for delayed authentication and custom error handling * Improve testing: disabling animation for e2e tests ## \[1.16.0] - 2024-08-06 ### Added * Extend cartesian charts to support trends and forecast for internal testing * Extend `ThemeSettings` to support widget theme settings * Support widget design styling on fetched dashboards * Support dashboard color palette * Extend `useExecuteQueryByWidgetId` hook to support pivot tables * Add embed code logic in `@sisense/sdk-ui/analytics-composer` namespace for internal testing ### Changed * Improve query validation logic for query hooks and components * Improve dashboard rendering: conversion of cascading filters between dashboard level and widget level, supporting collapsibility of `CascadingFilter` levels, fixing filter tile borders, fixing "Include All" highlights causing interference with filters * Improve charts: styling of scatter charts including data labels and legends, fixing lazy loading of table's page count, * Fix issues of pivot table: endless rendering due to updated style options, the theme of pagination panel, "No Results" overlay, pivot sorting and redundant pivot queries * Improve testing: visual-regression tests infra and stability, adding tests of different `Indicator` use cases, tests for `useTableData` ## \[1.15.1] - 2024-07-15 ### Changed * Fix an issue with `Include All` members filter ## \[1.15.0] - 2024-07-15 ### Added * Extend component `MemberFilterTile` to support excluded members ### Changed * Make improvements to dashboard rendering: fixing UI issues of `DateRangeFilter`, improve fallback jaql filter * Improve support for Common JS in `sdk-data` package ## \[1.14.0] - 2024-07-10 ### Added * Implement additional components and hooks for dashboard rendering (internal testing): background filters, locked filters * Implement components `DashboardById` and `Dashboard` in Angular and Vue for internal testing * Add visual regression testing infrastructure and basic tests ### Changed * Support additional datetime levels for Live models: 'seconds' and 'minutes' * Make improvements to charts and pivot table: tooltips of `AreaRangeChart`, default line thickness to bold for `LineChart`, hidden pagination panel for single page result of `PivotTable`, handling of data options update for `TableChart` * Make improvements to dashboard rendering: supporting `CustomFilter` in `CascadingFilterTile` * Make improvements for `Chatbot` component: list of data topics * Improve performance by lowering priority of tracking API calls * Handle properly empty returns of network calls ## \[1.13.0] - 2024-06-26 ### Added * Implement additional components and hooks for dashboard rendering (internal testing): component `CustomFilterTile`, component `CascadingFilterTile`, hook `useCommonFilters` * Make component `LoadingOverlay` available for internal usage * Implement component `AreaRangeChart` (beta) for Angular and Vue ### Changed * Extend `AreaRangeChart` to support smooth line * Change query cache key to work for all jaql elements * Make improvements to charts: fixing broken charts when switching chart type, clearing point state on selection, parsing of ISO date strings with or without timezone offsets, fixing numeric values as string (highcharts error), tooltip of measure name for range charts * Improve the translation of filter JAQL to `Filter` objects: exclude member filter, top/bottom ranking on measure, translation of deactivated members for `MembersFilter` * Make improvements to dashboard rendering: numeric members in `MemberFilterTile`, dynamic resizing of `FiltersPanel`, theming for `DashboardById` * Make improvements for `Chatbot` component: viewer role, scroll to bottom, input box autofocus, input length limit, hide history config ## \[1.12.0] - 2024-06-11 ### Added * Add `DashboardModel` class and implement `getDashboardProps` hook for internal testing * Add `Dashboard` and `DashboardById` components for internal testing * Extend `DashboardModel` to support cascading filters * Export `useLastNlqResponse` hook for extracting NLQ (Natural Language Query) response * Add tiled version of `DateRangeFilterTile` * Add support for Common JS in `sdk-data` and `sdk-ui` packages ### Changed * Make Chatbot tooltip style and data topics customizable * Make minor tweaks and UI improvements for `Chatbot` component * Enable Angular v18 support for `sdk-ui-angular` package ## \[1.11.0] - 2024-05-28 ### Added * Add React component `AreaRangeChart` (beta) * Extend component `Chart` and `ChartWidget` to support chart type `table` * Add highlight filters support for the `PivotTable` and `DashboardWidget` components, as well as for the useGetWidgetModel hook. * Add React component `FiltersPanel` for internal testing * Add generic `useFetch` Vue composable to call any Sisense REST endpoint ### Changed * Mark `headersColor`, `alternatingColumnsColor`, and `alternatingRowsColor` as `@deprecated` in `TableStyleOptions` – use `header.color`, `columns.alternatingColor`, and `rows.alternatingColor` instead * Support pie chart of multiple values and no category * Support boolean flag `ungroup` for JAQL queries with no aggregation * Make UI improvements: error messages for unsupported functionality in `BoxplotChart` and chart redraw on highlights deselect * Make improvements to AI chat to code (internal) * Move the `@sisense/sdk-ui-vue` package from beta to General Availability (GA) * Move components `AreamapChart`, `ScattermapChart`, and `BoxplotChart` from beta to General Availability (GA) * Move component `PivotTable` and hook `useExecutePivotQuery` from alpha to beta * Move AI components and hooks from private beta to beta ## \[1.10.1] - 2024-05-10 ### Changed * Fix an issue with CLI command `get-data-model` caused by React upgrade ## \[1.10.0] - 2024-05-09 ### Added * Implement `WidgetService.getWidgetModel()` in `@sisense/sdk-angular` ### Changed * Adjust `@mui` and `@emotion` packages in `@sisense/sdk-ui` to work with React 17 * Refactor `Chart` to simplify steps of adding new chart types * Upgrade `@sisense/sisense-charts` to 5.1.1 * Make improvements to the AI components and hooks (private beta): toggleable insights ## \[1.9.0] - 2024-05-02 ### Added * Add pivot table support to `DashboardWidget` and `WidgetModel` * Extend `PivotTable` to support additional style options * Add internal `ContentPanel` component for rendering a layout of widgets * Add extra factory functions for measure filters: `measureEquals`, `measureGreaterThan`, and `measureLessThan` ### Changed * Reduce the bundle size of `@sisense/sdk-ui` * Extend CLI command `get-data-model` to include field descriptions in the generated data model file. *Note: User account of role 'Data Designer' and above is required to include field descriptions* * Upgrade `sisense-charts` to prevent jQuery patching by Highcharts * Fix pivot types to prevent build errors in Angular 17 * Fix missing values in drilldown breadcrumbs of categorical charts * Improve the translation of filter JAQL to `Filter` objects * Make improvements to the AI components and hooks (private beta): theme settings, style customizations, insight customization, chatbot header, and dropup for recent queries/suggestions ## \[1.8.0] - 2024-04-15 ### Added * Add pivot sorting interface for component `PivotTable` and hook `useExecutePivotQuery` ### Changed * Fix boxplot outliers factory functions to prevent loading of redundant data points * Make improvements to the AI components and hooks (private beta) * Improve translation of AI chats to charts and code for internal testing. ## \[1.7.2] - 2024-04-09 ### Changed * Fix `includeWidgets` option in `useGetDashboardModel` for non-admin users * Support theme settings in `ErrorBoundary` UI component * Make improvements to the AI components and hooks (private beta) ## \[1.7.1] - 2024-04-03 ### Changed * Extend `appConfig` with boolean flag `accessibilityConfig.enabled` to toggle accessibility support in Highcharts ## \[1.7.0] - 2024-04-03 ### Added * Support caching of query execution (alpha) * Extend the `PivotTable` component (alpha) to support UI sorting, date and number formatting, and dynamic sizing * Add generic `useFetch` React hook to call any Sisense REST endpoint * Add Typedoc plugin `@sisense/typedoc-plugin-markdown` (forked from `tgreyuk/typedoc-plugin-markdown` version `4.0.0-next.20`) ### Changed * Enable accessibility support in Highcharts * Extend `appConfig` (in Sisense context) to support the `queryLimit` property * Fix the issue with Indicator chart not using theme colors * Fix issues with `PieChart`: highlights and convolution animation * Make improvements to the AI components and hooks (private beta) * Move the `@sisense/sdk-ui-angular` package from beta to General Availability (GA) ## \[1.6.0] - 2024-03-20 ### Added * Support simple pivot tables and gracefully handle unsupported widgets in hooks `useGetDashboardModel`, `useGetDashboardModels`, `useExecuteQueryByWidgetId` and component `DashboardWidget` * Implement translation of AI chats to charts and code for internal testing. ### Changed * Update CLI command `get-data-model` to support data model whose table names starting with a number * Remove global scrollbar CSS in `PivotTable` * Simplify the handling of `N/A` values in charts * Remove redundant info in the tooltip of combo chart * Make improvements to the AI components and hooks (private beta) ## \[1.5.0] - 2024-03-05 ### Changed * Improve the AI components and hooks (private beta) * Extend CLI command `get-data-model` to include additional metadata about Live data models for JAQL optimization. *Note: If you are using Live models, you need to re-run `get-data-model` to update the data model representation files.* * Optimize the `useExecuteQuery` hook by removing unnecessary render * Improve loading indicator on chart re-fetch triggered by aggregation change * Fix number formatting in `DashboardWidget` and `useGetWidgetModel` * Make small fixes in components `Table` (sorting icons) and `IndicatorChart` (rendering of `N/A` and `0` values) ## \[1.4.1] - 2024-02-23 ### Changed * Limit max zoom for `AreamapChart` ## \[1.4.0] - 2024-02-22 ### Added * Implement additional components and hooks in `@sisense/sdk-ui-vue` package for public beta testing * Add component `PivotTable` (alpha) for React, Angular, and Vue ### Changed * **Breaking:** Refactor `ScattermapChartDataOptions.geo` (beta) to use `StyledColumn`, instead of `ScattermapColumn` (removed). Prop `ScattermapColumn.level` has been replaced with `StyledColumn.geoLevel` * Support HTML content in component `Table` * Support theme settings for `IndicatorChart` in ticker mode * Extend `StyledMeasureColumn` with `seriesStypeOptions` to support different series of different chart types * Make improvements to `Chart` (refactoring and chart labels), `AreaChart` (sticky tracking), `NumberFormatConfig` (optional props), testing infrastructure (adoption of `msw` for mocks), and exports of packages (for both CommonJS and ESM imports) * Make improvements to the AI components and hooks (private beta) ## \[1.3.0] - 2024-02-07 ### Added * Implement additional components and hooks in `@sisense/sdk-ui-vue` package for internal testing * Support filter relations (logic operators `and` and `or`) for `DashboardWidget` and `useExecuteQueryByWidgetId` ### Changed * Show loading indicator on chart data re-fetch * Extend component `IndicatorChart` to support ticker mode (prop param `forceTickerView`) regardless of the display size * Extend component `MemberFilterTile` to add indication of inactive members * Support `onDataPointClick` prop for `AreamapChart` * Refactor to reuse `WidgetModel` in `DashboardWidget` and `useExecuteQueryByWidgetId` * Make minor improvements to chart navigator, i18n translations, and SSO flow. ## \[1.2.0] - 2024-01-24 ### Added * Add React hook `useExecutePivotQuery` (alpha) to execute a pivot data query and return the result in both table and tree structures * Implement additional components and hooks in `@sisense/sdk-ui-vue` package for internal testing ### Changed * Re-export common types of `sdk-ui` from `sdk-ui-angular` * Support coordinates via user-provided data for `ScattermapChart` * Make improvements to the AI `Chatbot` component including format of chat messages, the question recommendations, and the mapping from NLQ response to chart's axes * Improve the SSO flow by checking the redirect completion, skipping the fetch of color palette, and adding null check for the `window` object ## \[1.1.0] - 2024-01-10 ### Added * Add component `AreamapChart` and support the `areamap` chart type in components `Chart`, `ChartWidget`, and `DashboardWidget` for beta testing * Mark `@sisense/sdk-ui-angular` package as ready for public beta testing * Implement additional components and hooks in `@sisense/sdk-ui-vue` package for internal testing ### Changed * **Breaking:** Rename `ScattermapChartDataOptions.locations` to `ScattermapChartDataOptions.geo` for `ScattermapChart` (beta) * Make minor improvements to chart legend position type, xAxis gridlines, and filter relations. ## \[1.0.0] - 2023-12-27 ### Added * Publish `@sisense/sdk-ui-vue` and related dependencies to NPM registry for internal testing. * Add component `ScattermapChart` and support the `scattermap` chart type in components `Chart`, `ChartWidget`, and `DashboardWidget` for beta testing * Add component `BoxplotChart` and support the `boxplot` chart type in components `Chart`, `ChartWidget`, and `DashboardWidget` for beta testing * Support filter relations (logic operators `and` and `or`) for beta testing * Add UI component `RelativeDateFilterTile` ### Changed * **Breaking:** Refactor `ExecuteQuery` and `ExecuteQueryByWidgetId` to return `QueryState` and `QueryByWidgetIdState`, respectively * **Breaking:** Rename type alias `StyleOptions` to `ChartStyleOptions` * **Breaking:** Combine prop `widgetStyleOptions` into `styleOptions` for `ChartWidget` and `DashboardWidget` * **Breaking:** Rename type `IndicatorDataOptions` to `IndicatorChartDataOptions` * **Breaking:** Rename namespace `measures` to `measureFactory` and namespace `filters` to `filterFactory` > *See [migration guide](./guides/migration-guide-1.0.0.md) for more details.* ## \[0.16.0] - 2023-12-12 ### Added * Add React hook `useExecuteCsvQuery` to execute a data query and return the result in CSV format * Add React hook `useGetWidgetModel` to retrieve a dashboard widget from the Sisense instance ### Changed * Fix `ChartWidget` rendering issue when updating filters * Adjust the SSO authentication flow to not show error while waiting for SSO redirect * Fix named export error in `@sisense/sdk-cli` * Adjust the range of axes when `treatNullAsZero` is enabled for time series * Support cross filtering when clicking on data points in charts * Correct `modelType` of the `trend()` measure function to match the values expected by the backend API * Add translations for messages in `@sisense/sdk-rest-client` and `@sisense/sdk-data` * Extend components `MemberFilterTile` and `DateRangeFilterTile` to show UI errors in case of JAQL query failures * Extend `CriteriaFilterTile` and `CriteriaFilterMenu` to support ranking criteria filter options * Make minor UI improvements to highcharts legends, drilldown breadcrumbs, chart markers, and transition animation between chart types ## \[0.15.0] - 2023-11-30 ### Added * Add AI `Chatbot` component and related logic in `@sisense/sdk-ui/ai` namespace for internal testing * Support fully Angular in `@sisense/sdk-ui-angular` package * Add loading indicators for charts and tables * Implement `CriteriaFilterMenu` component for vertical double-input and horizontal use cases * Extend `IndicatorChart` with the `ticker` mode * Add `useGetSharedFormula` hook to retrieve shared formulas * Add support for custom formulas in code * Add Authentication user guide ### Changed * Support Angular v17 in `@sisense/sdk-ui-angular` * Extend `widgetStyleOptions` with ability to render custom chart header in widget * Support text inputs in criteria filters * Support dependent filters ## \[0.14.0] - 2023-11-14 ### Added * Add component `CriteriaFilterTile` for vertical single input use case. ### Changed * Support dashboard filters by boolean flag `includeDashboardFilters` in component `DashboardWidget`, component `ExecuteQueryByWidgetId`, and hook `useExecuteQueryByWidgetId` * Extend `fitlersMergeStrategy` to support highlight filters * Extend hooks `useExecuteQuery` and `useExecuteQueryByWidgetId` to re-execute on `onBeforeQuery` changes * Fix issues related to date formatting and continuous timeline * Show the No Result image for scatter chart without data * Support highlights for scatter chart and pie chart * Implement colors by series in sunburst chart * Implement usage tracking of public hooks * Extract usage tracking logic into a separate package `@sisense/sdk-tracking` ## \[0.13.0] - 2023-11-02 ### Added * Publish `@sisense/sdk-ui-angular` and related dependencies to NPM registry for internal testing. * Add component `SunburstChart` and support the `sunburst` chart type in components `Chart`, `ChartWidget`, and `DashboardWidget` * Add troubleshooting guides for common issues ### Changed * Mark `drilldownOptions` as `@deprecated` in `ChartWidgetProps` – use `DrilldownWidget` instead * Support `onBeforeExecute` callback in `ExecuteQuery`, `ExecuteQueryByWidgetId`, `useExecuteQuery`, and `useExecuteQueryByWidgetId` to allow modifying the JAQL query before it is executed * Support highlight filters of selected points on `ChartWidget` ## \[0.12.1] - 2023-10-26 ### Changed * Increase `maxAllowedMembers` in `BasicMemberFilterTile` from 1000 to 2000 * Fix build of `sdk-ui-angular` by adding missed devDependencies ## \[0.12.0] - 2023-10-24 ### Added * Add `i18n` module based on the `i18next` package to support internationalization * Add React hooks `useGetDashboardModel` and `useGetDashboardModels` to retrieve dashboards from the Sisense instance ### Changed * Fix invalid URL constructed for SSO authenticator * Enable y2-axis (right axis) in style options by default for Cartesian charts. It is visible only when there is a value assigned to it * Adjust REST client methods to return `undefined` when the status code is `204 (No Content)` or `304 (Not Modified)` or when the response body is empty * Switch default value of `filtersMergeStrategy` from `widgetFirst` to `codeFirst` in component `ExecuteQueryByWidgetId`, hook `useExecuteQueryByWidgetId`, and component `DashboardWidget` * Limit the allowed number of categories and values in the `dataOptions` of Categorical charts (Pie, Funnel, and Treemap) ## \[0.11.3] - 2023-10-16 ### Changed * Switch GitHub CI to publish to NPM instead of GitHub Packages * Allow override of breadcrumb position in component `DrilldownWidget` ## \[0.11.2] - 2023-10-12 ### Changed * Add props `count` and `offset` to `ExecuteQuery`, `useExecuteQuery`, `ExecuteQueryByWidgetId`, and `useExecuteQueryByWidgetId` to support pagination * Upgrade `postcss` to from 8.4.22 to 8.4.31 to address a vulnerability * Handle use case that SSO enabled in `SisenseContextProvider` but not enabled in the Sisense instance * Replace component `HighchartsWrapper` with the official `HighchartsReact` wrapper component from `highcharts-react-official` package * Improve styles of tooltips, `MemberFilterTile`, and `DateRangeFilterTile` * Fix refresh of table in `DashboardWidget` * Turn off tailwindcss Preflight (CSS normalization) and add explicit styles instead * Fix issues with subtypes of Pie chart * Return loading state from `useExecuteQuery` when params change * Re-organize files in the `packages/sdk-ui/src/components` directory ## \[0.11.1] - 2023-10-03 ### Changed * Fix an issue that hook `useExecuteQuery` does not re-run in some cases when input prop `filters` are updated * Add `ScaterDataPoint` and event handlers for it to support additional data point structures * Support rubber band selection for scatter chart ## \[0.11.0] - 2023-09-28 ### Added * Add component `TreemapChart` and support the `treemap` chart type in components `Chart`, `ChartWidget`, and `DashboardWidget` * Add component `DrilldownWidget`, which allows adding drilldown functionality to any type of chart ### Changed * Refactor `HttpClient` to return raw response – in addition to JSON * Support internationalization for numbers and improve tooltip consistency * Make `dataSource` optional in `ChartWidgetProps` and `TableWidgetProps` * Extend component `ExecuteQueryByWidgetId` and hook `useExecuteQueryByWidgetId` to support `filters`, `highlights`, and `filtersMergeStrategy` * Extend hook `useExecuteQuery` to support boolean flag `enabled` * Extend component `DashboardWidget` to support `filtersMergeStrategy` * Move `markers`, `navigator`, `xAxis`, `yAxis`, and `yAxis2` out of `BaseStyleOptions` and into `BaseAxisStyleOptions` * Bump `sisense-charts` version after fixing chart freeze on navigator update * Improve styling of the Drilldown Breadcrumbs ## \[0.10.0] - 2023-09-15 ### Added * Support React hook `useExecuteQueryByWidgetId` and component `ExecuteQueryByWidgetId` to execute a data query extracted from an existing widget in the Sisense instance. ### Changed * **Breaking:** Remove `username` and `password` from `SisenseContextProviderProps` * Fix axis min/max configuration * Match number format in the `DashboardWidget` component * Make `HighchartsOptions` importable from `@sisense/sdk-ui` * Update supported react/react-dom versions in `peerDependencies`: `^16.14.0`, `^17.0.0`, or `^18.0.0` * Rename directories and files to consistent kebab-case ## \[0.9.0] - 2023-09-05 ### Added * Support `useExecuteQuery` hook to execute a data query. This approach, which offers an alternative to `ExecuteQuery` component, is similar to React Query's `useQuery` hook. * Add CLI command `get-api-token` to generate an API token that can be used in `SisenseContextProviderProps` * Extend `StyleOptions` with `width` and `height` props for controlling the size of a UI component such as `Chart` and `ChartWidget` ### Changed * Mark `username` and `password` as `@deprecated` in `SisenseContextProviderProps`. This authentication method will be removed in future releases of Compose SDK. This change does not affect the username/password authentication supported by the CLI tool. * Add prefix `csdk-` to all Tailwind CSS classes to avoid conflicts with user-defined classes * Refactor common logic behind data-driven UI components into a higher-order component, `asSisenseComponent` * Improve validation of data options for `Table` component * Reorganize API reference (on developer.sisense.com) by splitting API items into individual files and group individual files by modules and types. ## \[0.8.0] - 2023-08-15 ### Added * Add measures `trend` and `forecast` for advanced analytics. To use these measures, Sisense version of `L2023.6.0` or greater is required. * Support data model representation in JavaScript by specifying `.js` output file in CLI commands – in addition to TypeScript (`.ts` output file) * Detect and apply theme settings as defined in Web Access Token's payload – the `thm` claim ### Changed * Display No Results overlay, instead of an error box, when there are no results to visualize * Apply theme settings to component `DateRangeFilterTile` * Migrate from `jest` to `vitest` for unit tests * Produce only ESM bundle for `@sisense/sdk-ui` and target ES6 instead of default ES20221 ## \[0.7.4] - 2023-08-05 ### Fixed * Fix CLI usage tracking for Node.js 16 ## \[0.7.3] - 2023-08-03 ### Changed * Support usage tracking of CLI commands * Support error tracking of UI components and CLI commands ## \[0.7.2] - 2023-08-01 ### Changed * Update `README.md` and `quickstart.md` for beta release ## \[0.7.1] - 2023-07-29 ### Changed * Clean up internal code references for beta release ## \[0.7.0] - 2023-07-29 ### Changed * Support GitHub CI ## \[0.6.0] - 2023-07-28 ### Added * Support usage tracking of all UI components ### Changed * **Breaking:** Rename component `TableChart` to `Table`. Related props are also renamed accordingly. * **Breaking:** Rename component `Widget` to `ChartWidget`. Related props are also renamed accordingly. * Refactor `DateRangeFilterTile` to use `react-datepicker` instead of `@mui/x-date-pickers`, `@mui/x-date-pickers-pro` ## \[0.5.1] - 2023-07-25 ### Added * Add `LICENSE.md` ## \[0.4.0] - 2023-07-25 ### Added * Support usage tracking of component `Widget` * Support usage tracking of REST API calls to a Sisense instance ### Changed * Support partial assignment of the `StyleOptions` and `ThemeSettings` properties * Fix issues with Web Access Token authentication * Bundle `@sisense/sisense-charts`, which is a React wrapper of `highcharts`, with `@sisense/sdk-ui` * Bundle `@sisense/task-manager` with `@sisense/sdk-query-client` ## \[0.3.0] - 2023-07-13 ### Changed * Bump `highcharts` from 6.x to 10.x ## \[0.2.0] - 2023-07-11 ### Added * Support `Widget` header styles * Support aggregation in `TableChart` ## \[0.1.0] - 2023-07-06 *Initial release.* --- --- url: 'https://developer.sisense.com/guides/sdk/getting-started/index.md' --- # Getting Started Here you'll find guides that will help you get started with Compose SDK. --- --- url: >- https://developer.sisense.com/guides/sdk/getting-started/authentication-security.md --- # Authentication & Security There are some authentication and security concerns you need to address in order to start using Compose SDK in an application. ## Authentication To retrieve data using Compose SDK you need to authenticate your application against a Sisense instance. There are several ways you can authenticate your application: * [Single Sign On (SSO)](#single-sign-on) * [Web Access Token (WAT)](#web-access-token) * [API Token](#api-token) ### Single Sign On Single Sign On (SSO) allows the users of your application to authenticate with Sisense using an external identity provider. #### Set up SSO Set up your Sisense instance to authenticate users with SSO using one of the following: * [JSON Web Token (JWT)](https://docs.sisense.com/main/SisenseLinux/single-sign-on-using-json-web-token.htm?tocpath=Security%7CImplementing%20Single%20Sign-On%7C_____3) * [Security Assertion Markup Language 2.0 (SAML)](https://docs.sisense.com/main/SisenseLinux/single-sign-on-using-security-assertion-markup-language-20.htm?tocpath=Security%7CImplementing%20Single%20Sign-On%7C_____2) * [OpenID Connect](https://docs.sisense.com/main/SisenseLinux/single-sign-on-using-openid-connect.htm?tocpath=Security%7CImplementing%20Single%20Sign-On%7C_____4) ##### Using the SSO Router Addon with JWT If your Sisense instance is configured to use the [SSO Router addon](https://www.sisense.com/marketplace/add-on/sso-router/) in order to support dynamic routing to various JWT endpoints, please use Compose SDK 2.6.0 or later. For more information on configuring SSO Router check [this community post](https://community.sisense.com/forum/add-ons-plug-ins-28/topic/sso-router-plugin-usage-with-examples-4467/), or contact Sisense support. ::: tip Note If you're experiencing difficulties or unexpected behavior when using SSO related to the `return_to` parameter, the cause may be related to system configuration. To resolve the issue please contact [support](https://www.sisense.com/support/) in order to validate your Fusion configuration settings. ::: #### Authenticate with SSO Once you’ve set up SSO access, you can use it to authenticate within your application: * For React apps use the `ssoEnabled` property of the `` component: ```ts ``` * For Angular apps use the `ssoEnabled` property of the `SisenseContextConfig` object: ```ts export const SISENSE_CONTEXT_CONFIG: SisenseContextConfig = { url: "https://sisense-instance-url" ssoEnabled: true }; ``` * For Vue apps use the `ssoEnabled` property of the `` component: ```ts ``` ### Web Access Token Sisense Web Access Tokens (WATs) impersonate specific Sisense users. Typically, in a production environment you create a Sisense user specifically for using Compose SDK. You grant that user the permissions you want to expose in your application and use a WAT that impersonates that user. ::: tip Note When using a Structured Token (By Value), you can enforce row-level security using an `acl` claim. All other claims are not supported. ::: #### Create a WAT Before creating a WAT, you need to [create a token configuration](https://docs.sisense.com/main/SisenseLinux/using-web-access-token.htm?tocpath=Security%7CSecuring%20Users%7C_____4#CreatingaTokenConfiguration) to generate a token secret and key ID. Once you have a token secret and key ID, you can generate a WAT to use in your application in one of the following ways: * [Go to Web Access Tokens in the Sisense UI](https://docs.sisense.com/main/SisenseLinux/using-web-access-token.htm?tocpath=Security%7CSecuring%20Users%7C_____4#OptionsforCreatingWebAccessTokens) * Send a request to the [wat/generate](https://developer.sisense.com/guides/restApi/v1/?platform=linux\&spec=L2025.2#/web-access-tokens/generateWebAccessToken) endpoint of the Sisense REST API * [Use self-hosted token generation](https://docs.sisense.com/main/SisenseLinux/using-web-access-token.htm?tocpath=Security%7CSecuring%20Users%7C_____4#OptionsforCreatingWebAccessTokens) #### Authenticate with a WAT Once you’ve created a WAT, you can use it to authenticate within your application: * For React apps use the `wat` property of the `` component: ```ts ``` * For Angular apps use the `wat` property of the `SisenseContextConfig` object: ```ts export const SISENSE_CONTEXT_CONFIG: SisenseContextConfig = { url: "https://sisense-instance-url" wat: "eykZjkFhMGYzYmJl…" }; ``` * For Vue apps use the `wat` property of the `` component: ```ts ``` ### API Token Sisense API Tokens are issued per user. Typically, you create a Sisense user specifically for using Compose SDK. You grant that user the permissions you want to expose in your application and use that user's API Token. :::warning Be sure to use API tokens in a secure manner. Typically, API tokens are not a good choice for production environments. ::: #### Create an API Token You can get an API Token to use in your application in one of the following ways: * [Go to a user profile in the Sisense UI](https://developer.sisense.com/guides/restApi/using-rest-api.html#getting-the-api-token-from-user-profiles) * Send a request to the [authentication/login](https://developer.sisense.com/guides/restApi/v1/?platform=linux\&spec=L2025.2#/authentication/login) endpoint of the Sisense REST API * Run the following command using the Compose SDK CLI tool: ```sh npx @sisense/sdk-cli@latest get-api-token --url --username ``` Notes: * Be sure to replace `` with the URL to your Sisense instance and `` with the username of the user you want to create the API token for. * For Windows, use double quotes around the URL and username arguments. For Mac/Linux, only use double quotes for arguments that contain white space. #### Authenticate with an API token Once you’ve obtained an API token, you can use it to authenticate within your application: * For React apps use the `token` property of the `` component: ```ts ``` * For Angular apps use the `token` property of the `SisenseContextConfig` object: ```ts export const SISENSE_CONTEXT_CONFIG: SisenseContextConfig = { url: "https://sisense-instance-url" token: "eRykZjVxkFdhMaGYzYmqJl..." }; ``` * For Vue apps use the `token` property of the `` component: ```ts ``` ## Cross-Origin Resource Sharing (CORS) By default, the browser same-origin policy prevents client-side web applications located in one domain from obtaining data from a different domain. That means an application you build with Compose SDK can't get data from your Sisense instance without some initial setup. To get around this problem, you enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) for specific origins for which you want to allow resource sharing. Doing so instructs the Sisense server to respond to requests from your application with a header that tells the browser your application can use the data returned from Sisense even though it comes from a different domain. #### Set up CORS Set up CORS on your Sisense instance using one of the following: * [Add your application's domain to the **CORS Allowed Origins** in the Sisense UI](https://docs.sisense.com/main/SisenseLinux/cross-origin-resource-sharing.htm?Highlight=cors#EnablingCORS) * Send a request to the [settings/system](https://developer.sisense.com/guides/restApi/v1/?platform=linux\&spec=L2025.2#/settings/setSystemSettings) endpoint of the Sisense REST API and include your application's domain in the `allowedOrigins` array: ```json "cors": { "enabled": true, "allowedOrigins": [ "https://your-application-url" ] } ``` ::: tip Notes * **Do not** include the trailing slash (`/`) when adding a domain to the **CORS Allowed Origins**. * Save your settings changes after adding your domain. ::: ## Third-Party Cookies Most modern browsers block third-party cookies. This affects cookie-based authentication such as SSO. Therefore, the best practice is one of the following: * Use the same domain for the different apps and put it behind a specific path. This prevents Sisense cookies from being third-party cookies. For example: `companyA.com/analytics`. * Leverage the Web Access Tokens (WAT) feature for authentication. Note that WAT requires special licensing. * Allow third-party cookies via your browser settings. See this [doc](https://docs.sisense.com/main/SisenseLinux/3rd-party-cookies.htm) for detailed instructions. :::warning The Cookies Having Independent Partitioned State ([CHIPS](https://developers.google.com/privacy-sandbox/cookies/chips)) solution is not compatible with Compose SDK. ::: --- --- url: 'https://developer.sisense.com/guides/sdk/getting-started/quickstart-angular.md' --- # Compose SDK with Angular: Quickstart Guide Follow this guide to get started developing applications with Compose SDK. > **Note:** > This guide is for [ Angular](./quickstart-angular.md). We also have a Quickstart Guide for [ React](./quickstart.md) and [ Vue](./quickstart-vue.md). ## Prerequisites Compose SDK contains a set of components needed to interface with your Sisense instance. The following prerequisites are needed in order to use the SDK: 1. Familiarity with [front-end web development](https://developer.mozilla.org/en-US/docs/Learn/Front-end_web_developer), including Node.js, JavaScript/TypeScript, and Angular. 2. [Node.js](https://nodejs.org/en) version **16.0.0** or higher. 3. [Angular](https://angular.io) version **17** or higher. 4. A Node package manager such as [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [Yarn](https://yarnpkg.com/getting-started/install). 5. Access to a [Sisense](https://sisense.com) instance with a queryable data source (for example, Sample Healthcare). 6. Angular application **with TypeScript**. You can use an existing application, or if you don't have one, you can follow the [tutorial](https://angular.io/start) to create one. > **Package manager:** > In Angular, `npm` is the default choice, and this guide will also adopt `npm`. > > You can opt to switch to `Yarn` by following the instructions in a [blog post](https://blog.angular-university.io/getting-started-with-angular-setup-a-development-environment-with-yarn-the-angular-cli-setup-an-ide/) from Angular University. ## Quickstart Application Setup For this quickstart guide, we'll create a new Angular project using the [command line tool](https://angular.io/quick-start#create-a-new-angular-app-from-the-command-line). If you're using an existing project, skip to [Installing the SDK packages](#installing-the-sdk-packages). 1. Create or navigate to the directory in which you want to create your Angular app. 2. Run this command to create your new Angular app. ```sh npm init @angular compose-sdk-app ``` 3. When prompted, choose to add Angular routing, then press Enter to accept the default option. This creates a new Angular app in the `compose-sdk-app` directory. 4. Run this command to navigate to the newly created directory. ```sh cd compose-sdk-app ``` 5. Install the dependencies. ```sh npm install ``` 6. To run the application, use: ```sh npm start ``` ## Installing the SDK Packages Compose SDK for Angular contains three packages for public use: * [@sisense/sdk-ui-angular](https://www.npmjs.com/package/@sisense/sdk-ui-angular): Angular components and services for rendering charts and executing queries against a Sisense instance. * [@sisense/sdk-data](https://www.npmjs.com/package/@sisense/sdk-data): Implementations of dimensional modeling elements including dimensions, attributes, measures, and filters. * [@sisense/sdk-cli](https://www.npmjs.com/package/@sisense/sdk-cli): A command-line tool for generating a TypeScript representation of a Sisense data model. The Compose SDK packages are deployed via public NPM Registry. To install `@sisense/sdk-ui-angular` and `@sisense/sdk-data` for your app: ```sh npm i @sisense/sdk-ui-angular @sisense/sdk-data ``` Package `@sisense/sdk-cli` is not needed to run your app. It will be installed on the fly as you execute CLI commands using [npx](https://docs.npmjs.com/cli/v10/commands/npx). ## Sisense Authentication and Security In order to retrieve data, you need to authenticate your application with your Sisense instance and set up CORS. ### Authentication There are a number of different ways you can authenticate your application. To learn more, see [Authentication and Security](./authentication-security.md#authentication). Here, we'll use an API Token that we retrieve using the Compose SDK tool. To do so, run the `get-api-token` command: ```sh npx @sisense/sdk-cli@latest get-api-token --url --username ``` Hold on to the API Token. You'll need it later when adding Compose SDK code to your application. ### CORS Settings There are also a number of different ways you can set up CORS. To learn more, see [Authentication and Security](./authentication-security.md#cross-origin-resource-sharing-cors). Here we'll use the Sisense UI. To do so, in your Sisense instance, go to **Admin > Security & Access > Security Settings > General** and add your application's domain to the **CORS Allowed Origins** list. ## Adding Sisense to Your Application This section describes how to add Compose SDK to your application to render charts from data in your Sisense instance. ### Generating a Data Model Representation To visualize data in your application using Compose SDK, first make sure you have a data model in your Sisense instance. Then, create a TypeScript representation of it in your project. This is done using the CLI command which automatically generates it, or you can create it manually using the same syntax. Once you have a TypeScript representation of your data model, you define measures, dimensions and filters and easily create sophisticated queries. There is no need to specify complex `JOINS` relationships or `GROUP BYS` that you do when using SQL and other query languages because the Sisense semantic query engine will do that for you. Run the following command to create a `sample-healthcare.ts` file in directory `src/` of the application. The file contains a TypeScript representation of the Sample Healthcare data model. ```sh npx @sisense/sdk-cli@latest get-data-model --username "" --output src/sample-healthcare.ts --dataSource "Sample Healthcare" --url ``` Enter your password to complete the command and generate the data model representation. > **Note:** > You can use other authentication methods such as WAT (`--wat ""`), or API token (`--token ""`) when generating the data model representation. The resulting file, which is created in the `src/` directory, should look something like below: ```ts import type { Dimension, DateDimension, Attribute } from '@sisense/sdk-data'; import { createAttribute, createDateDimension, createDimension } from '@sisense/sdk-data'; export const DataSource = 'Sample Healthcare'; interface AdmissionsDimension extends Dimension { Cost_of_admission: Attribute; Death: Attribute; Diagnosis_ID: Attribute; Doctor_ID: Attribute; HAI: Attribute; ID: Attribute; Patient_ID: Attribute; Room_ID: Attribute; SSI: Attribute; Surgical_Procedure: Attribute; TimeofStay: Attribute; Admission_Time: DateDimension; Discharge_Time: DateDimension; } export const Admissions = createDimension({ name: 'Admissions', Cost_of_admission: createAttribute({ name: 'Cost_of_admission', type: 'numeric-attribute', expression: '[Admissions.Cost_of_admission]', }), ... ``` This works for any data model, including models you create. Just replace `"Sample Healthcare"` with the name of your data model. ## Embedding a Chart in your Application In this section, you will add a new component and modify the main app module to embed a chart visualizing data from the Sample Healthcare data source. > **Note:** > The following assumptions are made about your application: > > * The `sample-healthcare.ts` file generated earlier resides in `src/`. > * The URL to your application (e.g. http://localhost:4200) is already added as an entry to the CORS Allowed Origins section on your Sisense instance. ### Connecting to a Sisense Instance Add a Sisense provider that contains all relevant information about the Sisense instance and ensures it is available to all Compose SDK components. The authentication method used to access your Sisense instance is defined in this provider. The following example shows how to add a provider to `src/app/app.config.ts`. ```typescript import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; import { SISENSE_CONTEXT_CONFIG_TOKEN, SisenseContextConfig } from '@sisense/sdk-ui-angular'; import { routes } from './app.routes'; export const SISENSE_CONTEXT_CONFIG: SisenseContextConfig = { url: '', // replace with the URL of your Sisense instance token: '', // replace with the API token of your user account }; export const appConfig: ApplicationConfig = { providers: [ { provide: SISENSE_CONTEXT_CONFIG_TOKEN, useValue: SISENSE_CONTEXT_CONFIG }, provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)] }; ``` > **Note:** > The above example uses the API token (also called *bearer authentication*) to connect to a Sisense instance. To generate an API token for your Sisense user account, see the Sisense Instance Authentication section above. The provider also supports other authentication mechanisms including WAT and SSO. ### Adding a component and routing To render a chart in your application that queries your data model, you need to create a new component that uses the data utilities along with your previously generated data model file. Here, we'll add a new **Analytics** component with the help of the `ng` CLI tool. You can do it manually. For more information, see the [guide](https://angular.io/guide/component-overview) from Angular. Run the following in your terminal from the project directory: ```sh npx ng generate component analytics ``` This should result in a new folder with three files inside: Next, configure the routing and point the main page to the `Analytics` component. The `app.routes.ts` file should look like this now: ```typescript import { AnalyticsComponent } from './analytics/analytics.component'; import { Routes } from '@angular/router'; export const routes: Routes = [ {path: '', component: AnalyticsComponent} ]; ``` Replace the contents of the `app.component.html` file with: ```typescript ``` ### Adding a chart Use the `dataOptions` property (`ChartProps` interface) to assign table columns or attributes from your data model to the categories and values of a chart. This is similar to the **Data** panel in the **Sisense Widget Editor**, where you can drag and drop columns to the **Categories**, **Values**, and **Break By** fields. For example, if you wanted to render a line chart with `Doctors' Specialty` on the X-axis and an average aggregation of `Time of Stay` on the Y-axis, your `dataOptions` object would look like: ```ts // chartType={'line'} { category: [DM.Doctors.Specialty], value: [measureFactory.average(DM.Admissions.TimeofStay)], breakBy: [], } ``` > **Note:** > Use `measureFactory.average()` from the example above to specify the `average` type aggregation on the `TimeofStay` category. This `measureFactory` utility is exported from the `@sisense/sdk-data` library and supports other aggregation types. See the [`measureFactory`](../modules/sdk-data/factories/namespace.measureFactory/index.md) documentation for more information. The following is a complete example of a rendered chart in an application. ```ts // src/app/analytics/analytics.component.ts import { Component } from '@angular/core'; import { measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-healthcare'; import { SdkUiModule } from '@sisense/sdk-ui-angular'; @Component({ selector: 'app-analytics', standalone: true, imports: [SdkUiModule], templateUrl: './analytics.component.html', styleUrl: './analytics.component.scss' }) export class AnalyticsComponent { chart = { chartType: 'line' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Doctors.Specialty], value: [measureFactory.average(DM.Admissions.TimeofStay, 'Average time of stay')], breakBy: [], }, styleOptions: { legend: { enabled: true, position: 'bottom', }, }, }; logArguments(...args: any[]) { console.log(args); } } ``` ```ts // src/app/analytics/analytics.component.html

My Sisense Compose SDK Chart

``` At this point, check your application in the browser if it's already running or run `npm start` to run your application and view it in a browser. Your first Compose SDK chart with Angular should look something like this: ![Line chart rendered by the Angular component](../img/angular-quickstart-chart-example.png) See the [SISENSE\_CONTEXT\_CONFIG\_TOKEN](../modules/sdk-ui-angular/contexts/variable.SISENSE_CONTEXT_CONFIG_TOKEN.md) and [ChartComponent](../modules/sdk-ui-angular/charts/class.ChartComponent.md) docs for more details on supported props. ## Next Steps The sample application in this quickstart guide is designed to give you a basis for what you can do with Compose SDK. Build on the code sample by using other components from Compose SDK to add Sisense analytical experiences to your applications. Check out our demo application for Compose SDK with [Angular](https://csdk-angular.sisense.com). --- --- url: 'https://developer.sisense.com/guides/sdk/getting-started/quickstart-vue.md' --- # Compose SDK with Vue: Quickstart Guide Follow this guide to get started developing applications with Compose SDK. > **Note:** > This guide is for [ Vue](./quickstart-vue.md). We also have a Quickstart Guide for [ React](./quickstart.md) and [ Angular](./quickstart-angular.md). ## Prerequisites Compose SDK contains a set of components needed to interface with your Sisense instance. The following prerequisites are needed in order to use the SDK: 1. Familiarity with [front-end web development](https://developer.mozilla.org/en-US/docs/Learn/Front-end_web_developer), including Node.js, JavaScript/TypeScript, and Vue. 2. [Node.js](https://nodejs.org/en) version **18.0.0** or higher. 3. [Vue](https://vuejs.org) version **3.3.0** or higher. 4. A Node package manager such as [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [Yarn](https://yarnpkg.com/getting-started/install). 5. Access to a [Sisense](https://sisense.com) instance with a queryable data source (for example, Sample Retail). 6. Vue application **with TypeScript**. You can use an existing application, or if you don't have one, you can follow the [tutorial](https://vuejs.org/guide/quick-start) to create one. ## Quickstart Application Setup For this quickstart guide, we'll create a new Vue project using the [command line tool](https://vuejs.org/guide/quick-start#creating-a-vue-application). If you're using an existing project, skip to [Installing the SDK packages](#installing-the-sdk-packages). 1. Create or navigate to the directory in which you want to create your Vue app. 2. Run this command to create your new Vue app. For npm: ```sh npm create vue@latest ``` For Yarn: ```sh yarn create vue@latest ``` 3. When prompted, choose to add Typescript, JSX support and Vue routing. This creates a new Vue app in the `compose-sdk-app` directory. 4. Run this command to navigate to the newly created directory. ```sh cd compose-sdk-app ``` 5. Install the dependencies. For npm: ```sh npm install ``` For Yarn: ```sh yarn ``` 6. To run the application, use: For npm: ```sh npm run dev ``` For Yarn: ```sh yarn dev ``` ## Installing the SDK Packages Compose SDK for Vue contains three packages for public use: * [@sisense/sdk-ui-vue](https://www.npmjs.com/package/@sisense/sdk-ui-vue): Vue components and hooks for rendering charts and executing queries against a Sisense instance. * [@sisense/sdk-data](https://www.npmjs.com/package/@sisense/sdk-data): Implementations of dimensional modeling elements including dimensions, attributes, measures, and filters. * [@sisense/sdk-cli](https://www.npmjs.com/package/@sisense/sdk-cli): A command-line tool for generating a TypeScript representation of a Sisense data model. The Compose SDK packages are deployed via public NPM Registry. To install `@sisense/sdk-ui-vue` and `@sisense/sdk-data` for your app: For npm: ```sh npm i @sisense/sdk-ui-vue @sisense/sdk-data ``` For Yarn: ```sh yarn add @sisense/sdk-ui-vue @sisense/sdk-data ``` Package `@sisense/sdk-cli` is not needed to run your app. It will be installed on the fly as you execute CLI commands using [npx](https://docs.npmjs.com/cli/v10/commands/npx). ## Sisense Authentication and Security In order to retrieve data, you need to authenticate your application with your Sisense instance and set up CORS. ### Authentication There are a number of different ways you can authenticate your application. To learn more, see [Authentication and Security](./authentication-security.md#authentication). Here, we'll use an API Token that we retrieve using the Compose SDK tool. To do so, run the `get-api-token` command: ```sh npx @sisense/sdk-cli@latest get-api-token --url --username ``` Hold on to the API Token. You'll need it later when adding Compose SDK code to your application. ### CORS Settings There are also a number of different ways you can set up CORS. To learn more, see [Authentication and Security](./authentication-security.md#cross-origin-resource-sharing-cors). Here we'll use the Sisense UI. To do so, in your Sisense instance, go to **Admin > Security & Access > Security Settings > General** and add your application's domain to the **CORS Allowed Origins** list. ## Adding Sisense to Your Application This section describes how to add Compose SDK to your application to render charts from data in your Sisense instance. ### Generating a Data Model Representation To visualize data in your application using Compose SDK, first make sure you have a data model in your Sisense instance. Then, create a TypeScript representation of it in your project. This is done using the CLI command which automatically generates it, or you can create it manually using the same syntax. Once you have a TypeScript representation of your data model, you define measures, dimensions and filters and easily create sophisticated queries. There is no need to specify complex `JOINS` relationships or `GROUP BYS` that you do when using SQL and other query languages because the Sisense semantic query engine will do that for you. Run the following command to create a `sample-retail.ts` file in directory `src/` of the application. The file contains a TypeScript representation of the Sample Retail data model. ```sh npx @sisense/sdk-cli@latest get-data-model --username "" --output src/sample-retail.ts --dataSource "Sample Retail" --url ``` Enter your password to complete the command and generate the data model representation. > **Note:** > You can use other authentication methods such as WAT (`--wat ""`), or API token (`--token ""`) when generating the data model representation. The resulting file, which is created in the `src/` directory, should look something like below: ```ts import type { Dimension, DateDimension, Attribute } from '@sisense/sdk-data'; import { createAttribute, createDateDimension, createDimension } from '@sisense/sdk-data'; export const DataSource = 'Sample Retail'; interface DimCountriesDimension extends Dimension { CountryName: Attribute; Region: Attribute; } export const DimCountries = createDimension({ name: 'DimCountries', CountryName: createAttribute({ name: 'CountryName', type: 'text-attribute', expression: '[DimCountries.CountryName]', }), Region: createAttribute({ name: 'Region', type: 'text-attribute', expression: '[DimCountries.Region]', }), }) as DimCountriesDimension; ... ``` This works for any data model, including models you create. Just replace `"Sample Retail"` with the name of your data model. ## Embedding a Chart in your Application In this section, you will add a new component and modify the main app to embed a chart visualizing data from the Sample Retail data source. > **Note:** > The following assumptions are made about your application: > > * The `src/App.vue` file is the main Vue component. > * The `sample-retail.ts` file generated earlier resides in `src/`. > * The URL to your application (e.g. http://localhost:5173) is already added as an entry to the CORS Allowed Origins section on your Sisense instance. ### Connecting to a Sisense Instance The `SisenseContextProvider` component contains all relevant information about the Sisense instance and ensures it is available to all nested Compose SDK components. In other words, this is a wrapper for your application so that all the components are able to access the data. The authentication method used to access your Sisense instance is also defined in this component. The following example shows how to add `SisenseContextProvider` to `src/App.vue`. Make sure that all the other SDK components you want to use are nested inside the `SisenseContextProvider` component. ```ts // src/App.vue ``` > **Note:** > The above example uses the API token (also called *bearer authentication*) to connect to a Sisense instance. To generate an API token for your Sisense user account, see the Sisense Instance Authentication section above. The `SisenseContextProvider` also supports other authentication mechanisms including WAT and SSO. ### Adding a chart To render a chart in your application that queries your data model, use the `Chart` component, the `measureFactory` and `filterFactory` utilities, and your previously generated data model file. Use the `dataOptions` property (`ChartProps` interface) to assign table columns or attributes from your data model to the categories and values of a chart. This is similar to the **Data** panel in the **Sisense Widget Editor**, where you can drag and drop columns to the **Categories**, **Values**, and **Break By** fields. For example, if you wanted to render a column chart with `Category Name` on the X-axis and an average aggregation of `Unit Price Discount` on the Y-axis, your `dataOptions` object would look like: ```ts // chartType={'column'} { category: [DM.DimProducts.CategoryName], value: [measureFactory.average(DM.Fact_Sale_orders.UnitPriceDiscount)], breakBy: [], } ``` > **Note:** > Use `measureFactory.average()` from the example above to specify the `average` type aggregation on the `UnitPriceDiscount` category. This `measureFactory` utility is exported from the `@sisense/sdk-data` library and supports other aggregation types. See the [`measureFactory`](../modules/sdk-data/factories/namespace.measureFactory/index.md) documentation for more information. The following is a complete example of a rendered chart in an application. ```ts // src/App.vue ``` At this point, check your application in the browser if it's already running or run your application as described in [Quickstart Application Setup](#quickstart-application-setup). Your first Compose SDK chart with Vue should look something like this: ![Line chart rendered by the Vue component](../img/vue-quickstart-chart-example.png) See the [SisenseContextProvider](../modules/sdk-ui-vue/contexts/class.SisenseContextProvider.md) and [Chart](../modules/sdk-ui-vue/charts/class.Chart.md) docs for more details on supported props. ## Next Steps The sample application in this quickstart guide is designed to give you a basis for what you can do with Compose SDK. Build on the code sample by using other components from Compose SDK to add Sisense analytical experiences to your applications. --- --- url: 'https://developer.sisense.com/guides/sdk/getting-started/quickstart.md' --- # Compose SDK with React: Quickstart Guide Follow this guide to get started developing applications with Compose SDK. > **Note:** > This guide is for [ React](./quickstart.md). We also have a Quickstart Guide for [ Angular](./quickstart-angular.md) > and [ Vue](./quickstart-vue.md). ## Prerequisites Compose SDK contains a set of React components needed to interface with your Sisense instance. The following prerequisites are needed in order to use the SDK: 1. Familiarity with [front-end web development](https://developer.mozilla.org/en-US/docs/Learn/Front-end_web_developer), including Node.js, JavaScript/TypeScript, and React. 2. [Node.js](https://nodejs.org/en) version **16** or higher. 3. [React](https://react.dev) version **17**, **18** or **19**. 4. A Node package manager such as [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) or [Yarn](https://yarnpkg.com/getting-started/install). 5. Access to a [Sisense](https://sisense.com) instance with a queryable data source (for example, Sample ECommerce). 6. React application **with TypeScript**. You can use your existing application, or if you do not have one, you can follow the [Vite tutorial](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) to create one. ## Quickstart Application Setup For this quickstart guide, we will use the `Vite` project. If you want to use your own application, skip to [Installing the SDK packages](#installing-the-sdk-packages). > **Note:** > > When creating your Vite project, select the React framework and TypeScript. Follow the instructions on the [Scaffolding Your First Vite Project](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) page. Navigate to your project and install the dependencies. For npm: ```sh npm install ``` For Yarn: ```sh yarn ``` To run the Vite application, use: For npm: ```sh npm run dev ``` For Yarn: ```sh yarn dev ``` ## Installing the SDK Packages Compose SDK contains three packages for public use: * [@sisense/sdk-ui](https://www.npmjs.com/package/@sisense/sdk-ui): React components and hooks for rendering charts and executing queries against a Sisense instance. * [@sisense/sdk-data](https://www.npmjs.com/package/@sisense/sdk-data): Implementations of dimensional modeling elements including dimensions, attributes, measures, and filters. * [@sisense/sdk-cli](https://www.npmjs.com/package/@sisense/sdk-cli): A command-line tool for generating TypeScript representation of a Sisense data model. The Compose SDK packages are deployed via public NPM Registry. To install `@sisense/sdk-ui` and `@sisense/sdk-data` for your app: For npm: ```sh npm i @sisense/sdk-ui @sisense/sdk-data ``` For Yarn: ```sh yarn add @sisense/sdk-ui @sisense/sdk-data ``` Package `@sisense/sdk-cli` is not needed to run your app. It will be installed on the fly as you execute CLI commands using [npx](https://docs.npmjs.com/cli/v10/commands/npx). ## Sisense Authentication and Security In order to retrieve data, you need to authenticate your application with your Sisense instance and set up CORS. ### Authentication There are a number of different ways you can authenticate your application. To learn more, see [Authentication and Security](./authentication-security.md#authentication). Here, we'll use an API Token that we retrieve using the Compose SDK tool. To do so, run the `get-api-token` command: ```sh npx @sisense/sdk-cli@latest get-api-token --url --username ``` Hold on to the API Token. You'll need it later when adding Compose SDK code to your application. ### CORS Settings There are also a number of different ways you can set up CORS. To learn more, see [Authentication and Security](./authentication-security.md#cross-origin-resource-sharing-cors). Here we'll use the Sisense UI. To do so, in your Sisense instance, go to **Admin > Security & Access > Security Settings > General** and add your application's domain to the **CORS Allowed Origins** list. ## Adding Sisense to Your Application This section describes how to add Compose SDK to your application to render charts from data in your Sisense instance. ### Generating a Data Model Representation To visualize data in your application using Compose SDK, first make sure you have a data model in your Sisense instance. Then, create a TypeScript representation of it in your project. This is done using the CLI command which automatically generates it, or you can create it manually using the same syntax. Once you have a TypeScript representation of your data model, you define measures, dimensions and filters and easily create sophisticated queries. There is no need to specify complex `JOINS` relationships or `GROUP BYS` that you do when using SQL and other query languages because the Sisense semantic query engine will do that for you. Run the following command to create a `sample-ecommerce.ts` file in directory `src/` of the application. The file contains a TypeScript representation of the Sample ECommerce data model. ```sh npx @sisense/sdk-cli@latest get-data-model --username "" --output src/sample-ecommerce.ts --dataSource "Sample ECommerce" --url ``` Enter your password to complete the command and generate the data model representation. > **Note:** > You can use other authentication methods such as WAT (`--wat ""`), or API token (`--token ""`) when generating the data model representation. The resulting file, which is created in the `src/` directory, should look something like below: ```ts import type { Dimension, DateDimension, Attribute } from '@sisense/sdk-data'; import { createAttribute, createDateDimension, createDimension } from '@sisense/sdk-data'; export const DataSource = 'Sample ECommerce'; interface BrandDimension extends Dimension { Brand: Attribute; BrandID: Attribute; } export const Brand = createDimension({ name: 'Brand', Brand: createAttribute({ name: 'Brand', type: 'text-attribute', expression: '[Brand.Brand]', }), BrandID: createAttribute({ name: 'BrandID', type: 'numeric-attribute', expression: '[Brand.Brand ID]', }), }) as BrandDimension; ``` This works for any data model, including models you create. Just replace `"Sample ECommerce"` with the name of your data model. ## Embedding a Chart in your Application In this section, you will modify the main `app` component to embed a chart visualizing data from the Sample ECommerce data source. Use the two components, `SisenseContextProvider` and `Chart`, from `@sisense/sdk-ui` along with the `measureFactory` and `filterFactory` utilities from `@sisense/sdk-data`. > **Note:** > The following assumptions are made about your application: > > * The `src/App.tsx` file is the main React component. > * The `sample-ecommerce.ts` file generated earlier resides in `src/`. > * The URL to your application (e.g. http://localhost:5173) is already added as an entry to the CORS Allowed Origins section on your Sisense instance. If not, you can do so on your Sisense instance by going to *Admin*, then *Security Settings*. ### Connecting to a Sisense Instance The `SisenseContextProvider` component contains all relevant information about the Sisense instance and ensures it is available to all nested Compose SDK components. In other words, this is a wrapper for your application so that all the components are able to access the data. The authentication method used to access your Sisense instance is also defined in this component. The following example shows how to add `SisenseContextProvider` to `src/App.tsx`. Make sure that all the other SDK components you want to use are nested inside the `SisenseContextProvider` component. ```ts // src/App.tsx import { SisenseContextProvider } from '@sisense/sdk-ui'; function App() { return ( <> ); } export default App; ``` > **Note:** > The above example uses the API token (also called *bearer authentication*) to connect to a Sisense instance. To generate an API token for your Sisense user account, see the Sisense Instance Authentication section above. The `SisenseContextProvider` also supports other authentication mechanisms including WAT and SSO. ### Adding a chart To render a chart in your application that queries your data model, use the `Chart` component, the `measureFactory` and `filterFactory` utilities, and your previously generated data model file. Use the `dataOptions` property (`ChartProps` interface) to assign table columns or attributes from your data model to the categories and values of a chart. This is similar to the **Data** panel in the **Sisense Widget Editor**, where you can drag and drop columns to the **Categories**, **Values**, and **Break By** fields. For example, if you wanted to render a line chart with `Age Range` on the X-axis and a sum aggregation of `Revenue` on the Y-axis, your `dataOptions` object would look like: ```ts // chartType={'line'} { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], breakBy: [], } ``` > **Note:** > Use `measureFactory.sum()` from the example above to specify the `sum` type aggregation on the `Revenue` category. This `measureFactory` utility is exported from the `@sisense/sdk-data` library and supports other aggregation types. See the [`measureFactory`](../modules/sdk-data/factories/namespace.measureFactory/index.md) documentation for more information. The following is a complete example of a rendered chart in an application. ```ts // src/App.tsx import { Chart, SisenseContextProvider } from '@sisense/sdk-ui'; import * as DM from './sample-ecommerce'; import { measureFactory } from '@sisense/sdk-data'; function App() { return ( <> { console.log('clicked', point, nativeEvent); }} /> ); } export default App; ``` Your chart should look like this: ![Line chart rendered by the Chart component](../img/chart-data-source-example-2.png) See the [SisenseContextProvider](../modules/sdk-ui/contexts/function.SisenseContextProvider.md) and [Chart](../modules/sdk-ui/charts/function.Chart.md) docs for more details on supported props. ## Next Steps The sample application in this quickstart guide is designed to give you a basis for what you can do with Compose SDK. Build on the code sample by using other components from Compose SDK to add Sisense analytical experiences to your applications. For some ideas and examples, check out: * [Compose SDK Playground](https://www.sisense.com/developers/playground/) * [Demo application for Compose SDK with React](https://csdk-react.sisense.com) * [Chart Tutorial](../tutorials/tutorial-charts/index.md) --- --- url: 'https://developer.sisense.com/guides/sdk/guides/index.md' --- # Guides Here you'll find guides that will help you learn and work with Compose SDK. --- --- url: 'https://developer.sisense.com/guides/sdk/guides/ai-features/index.md' --- # Generative AI powered by Sisense Intelligence Compose SDK contains generative AI (GenAI) components and hooks/services that enable the following possibilities: * **Deliver in-app analytics chat**: Enable business users to uncover data insights easily, by asking questions in a conversational interface using the `` component. See how to [get started using the chatbot](./quickstart.md#chatbot) in your code. * **Suggest recommended questions**: Encourage exploration of the data landscape with AI-generated recommended queries, either directly within the chatbot or as a standalone feature using the `useGetQueryRecommendations()` hook in React and Vue, or the `AiService.getQueryRecommendations` in Angular. See how to [get started using query recommendations](./quickstart.md#query-recommendations) in your code. * **Bring insights to life with data storytelling**: Enhance collaboration and add context to your data with auto-generated, natural language insights using the `` component (`GetNlgInsightsComponent` in Angular) or the `useGetNlgInsights()` hook (`AiService.getNlgInsights` in Angular). See how to [get started using natural language insights](./quickstart.md#natural-language-generation-nlg-insights) in your code. Visit the following API References to learn more about usage and examples: * Generative for [React](../../modules/sdk-ui/generative-ai/) * Generative for [Angular](../../modules/sdk-ui-angular/generative-ai/) * Generative for [Vue](../../modules/sdk-ui-vue/generative-ai/) --- --- url: 'https://developer.sisense.com/guides/sdk/guides/ai-features/quickstart.md' --- # Generative AI with React: Quickstart Guide This guide offers examples for getting started with: * [AI Chatbot](#chatbot) * [Natural language generation (NLG) for Insights](#natural-language-generation-nlg-insights) * [Natural language query (NLQ)](#natural-language-query-nlq) * [Query recommendations](#query-recommendations) ## Prerequisites This guide assumes you already have a React project working with Compose SDK. If you don't already have a working project, follow the [Compose SDK Quickstart](../../getting-started) before continuing here. The additional prerequsities for Generative AI are listed below: * `@sisense/sdk-ui` version `2.0.0` or higher * Sisense Fusion version L2025.2 or higher, with Generative AI and LLM enabled per the [Sisense Documentation](https://docs.sisense.com/main/SisenseLinux/genai.htm) ## Project Setup To use AI features in Compose SDK, all AI related components or hooks imported from `@sisense/sdk-ui/ai` must be wrapped with an `AiContextProvider` component within your application code. For example: ```ts import App from './App.tsx'; import { SisenseContextProvider } from '@sisense/sdk-ui'; import { AiContextProvider } from '@sisense/sdk-ui/ai'; const sisenseContextProps = { /* Sisense configuration */ }; // ... ``` ## Chatbot Here are some examples of how to work with the [``](../../modules/sdk-ui/generative-ai/function.Chatbot.md) component. ### Default Chatbot To display a chatbot with the default settings, simply add the [``](../../modules/sdk-ui/generative-ai/function.Chatbot.md) component to your code without specifying any props. ```ts import { Chatbot } from '@sisense/sdk-ui/ai'; // ... ``` ### Custom Configuration You can also configure the Chatbot with custom options, including size, behavior and look and feel. #### Change Size To change the size of the displayed Chatbot, provide values for the `width` and `height` properties (props). For more information refer to [ChatbotProps](../../modules/sdk-ui/interfaces/interface.ChatbotProps.md) ```ts ``` #### Change Behavior To change the Chatbot's default behavior or text content, provide an object to the `config` property. For more information refer to [ChatConfig](../../modules/sdk-ui/interfaces/interface.ChatConfig.md) ```ts ``` #### Change Look and Feel To change the look and feel of the chatbot, wrap the component in a [``](../../modules/sdk-ui/contexts/function.ThemeProvider.md) and specify properties under the `aiChat` field. For more information refer to [AiChatThemeSettings](../../modules/sdk-ui/interfaces/interface.AiChatThemeSettings.md) ```ts ``` ## Natural Language Generation (NLG Insights) Natural language textual insights generated from the data results of the provided query parameters. There are different options for generating NLG insights using a Compose SDK query: * Use the [`useGetNlgInsights()`](../../modules/sdk-ui/generative-ai/function.useGetNlgInsights.md) hook as an API to return a plain text response, and render it how you like using your own code / component. * Use the [``](../../modules/sdk-ui/generative-ai/function.GetNlgInsights.md) component to get display the generated text response in a styled container. ### useNlgInsights Hook To use the [`useGetNlgInsights()`](../../modules/sdk-ui/generative-ai/function.useGetNlgInsights.md) hook, call the hook with the query information and handle the returned result. ```ts import { useGetNlgInsights } from '@sisense/sdk-ui/ai'; // ... const { data, isLoading } = useGetNlgInsights({ dataSource: DM.DataSource, dimensions: [DM.Commerce.Date.Years], measures: [measureFactory.sum(DM.Commerce.Revenue)], verbosity: 'Low' }); if (isLoading) { return
Loading...
; } return

{data}

; ``` ### GetNlgInsights Component To use the [``](../../modules/sdk-ui/generative-ai/function.GetNlgInsights.md) component, add it to your code with the query information. ```ts import { GetNlgInsights } from '@sisense/sdk-ui/ai'; // ... ``` ## Natural Language Query (NLQ) Generate properties for a [``](../../modules/sdk-ui/dashboards/function.Widget.md) by asking a question in natural language, with the `useGetNlqResult` hook. Provide the question and datamodel name, and receive [`WidgetProps`](../../modules/sdk-ui/type-aliases/type-alias.WidgetProps.md) as a response, then render the result in a [``](../../modules/sdk-ui/dashboards/function.Widget.md) component. ```ts import { useGetNlqResult } from '@sisense/sdk-ui/ai'; // ... const { data, isLoading} = useGetNlqResult({ dataSource: 'Sample ECommerce', query: 'total sales by month', }); if (isLoading) { return
Loading result
; } return ( <> {data && } ); ``` ## Query Recommendations Query recommendations are AI-generated queries that you can run on your data model. The provides query recommendations as a standlone capability outside of the conversational analytics flow provided by the [``](../../modules/sdk-ui/generative-ai/function.Chatbot.md) component. This enables query recommendation functionality to be delivered in a customized user experience. To do so, use the [`useGetQueryRecommendations()`](../../modules/sdk-ui/generative-ai/function.useGetQueryRecommendations.md) hook by providing a data model title for the query recommendations and, optionally, the number of recommendations you want to generate. The hook returns `data` as an array of [`QueryRecommendation`](../../modules/sdk-ui/interfaces/interface.QueryRecommendation.md) entities. These include properties such as: * The `nlqPrompt` which is the textual representation of the question to ask, to show to the end user * `widgetProps` that can be passed to a [``](../../modules/sdk-ui/dashboards/function.Widget.md) component to render the results of the generated question. * other properties e.g `detailedDescription` In this example, we simply show the list of suggested questions. In practice, the other propeties are then useful if/when a user selects one of the generated questions. ```ts import { useGetQueryRecommendations, QueryRecommendation } from '@sisense/sdk-ui/ai'; // ... const { data, isLoading } = useGetQueryRecommendations({ contextTitle: "Sample ECommerce", count: 5 }); if (isLoading) { return
Loading recommendations..
; } return (
    {data.map((item: QueryRecommendation, index) => (
  • {item.nlqPrompt}
  • ))}
); ``` In this example, both the generated question and the answer (widget) are shown at the same time. ```ts import { useGetQueryRecommendations, QueryRecommendation } from '@sisense/sdk-ui/ai'; // ... const { data, isLoading } = useGetQueryRecommendations({ contextTitle: "Sample ECommerce", count: 5 }); if (isLoading) { return
Loading recommendations..
; } return (
    {data.map((item: QueryRecommendation, index) => ( item.widgetProps &&
  • {item.nlqPrompt}
  • ))}
); ``` --- --- url: 'https://developer.sisense.com/guides/sdk/guides/charts/index.md' --- # Chart Types When working with Compose SDK, there are several different ways you can visualize data using charts: * Compose SDK Charts - Charts found in the `sdk-ui*` modules built specifically for Compose SDK * Sisense Fusion Widgets - Charts that are already defined as widgets within a Sisense Fusion dashboard * External Charts - Charts from 3rd party charting libraries, such as [D3](https://d3js.org/), [Material UI](https://mui.com/x/react-charts/), [nivo](https://nivo.rocks/), and others You can choose to use one type of chart or mix and match to fit your specific needs. ## Compose SDK Charts Compose SDK charts are the components found in the `sdk-ui` modules of Compose SDK. These charts can be used with data from a Sisense instance or 3rd party data. These components provide an easy way to build data visualizations directly against a Sisense data model or other data source. They also allow for customizing the data that is presented, how that data is styled, and how your users can interact with that data. Compose charts are currently available as React, Angular, and Vue components. Compose SDK charts should be your default choice for any project where you’ll be creating new charts, especially with data from a Sisense instance. To learn more about Compose SDK charts, see [Compose SDK Charts](./guide-compose-sdk-charts.md). ## Sisense Fusion Widgets Sisense Fusion widgets are charts rendered from dashboard widgets that already exist in your Sisense Fusion environment. You can reuse most charts you already defined using Sisense Fusion and optionally customize them using Compose SDK. Note: Sisense Fusion widgets can also be used to update existing projects that embed Sisense Fusion widgets using Sisense.JS. Using Compose SDK instead of Sisense.JS usually leads to improved performance, lower latency, and simpler code. Use Fusion charts when you already have the charts you need in your Sisense instance. To learn more about Fusion charts, see [Sisense Fusion Widgets](./guide-fusion-widgets.md). ## External Charts Using the query APIs of Compose SDK, you can use the data you retrieve from your Sisense environment with just about any JavaScript charting library. Use Compose SDK to query Sisense for the data you need for your charts. Then use that data to populate charts from a 3rd party charting library. Use external charting libraries if you are already familiar with them and want to continue using them or if they offer functionality not currently available in Compose SDK charts. To learn more about External Charts, see [External Charts](./guide-external-charts.md). --- --- url: >- https://developer.sisense.com/guides/sdk/guides/charts/guide-compose-sdk-charts.md --- # Compose SDK Charts The chart components found in the `sdk-ui` modules of Compose SDK are a great way to display analytics data with minimal effort. They can be used out of the box with data retrieved from a Sisense instance, or with the correct structure, data retrieved from anywhere else. The charts allow you to customize the data that is presented, how that data is styled, and how your users can interact with that data. The components provide properties for the most common customization options and also allow you to manipulate the underlying chart configuration before rendering if necessary. Compose SDK charts are currently available as React and Angular components. There are several different types of components you can use to display charts. * Specific chart components - Each chart type has a component specifically for that chart type. For example, to display a pie chart, you can use the `` component. These components offer the most granular customization levels since their properties are tailored to the specific type of chart you’re working with. * Chart - A universal `` component that can be used to display charts of different types. You specify which type of chart you want to display using the component’s `chartType` property. This component can be used to easily switch between chart types or to show a series of charts that are all different types. * ChartWidget - A universal `` wrapped in a widget container. You can use the widget wrapping to add a title, widget styling, and more. Although there are some differences between the different chart types, their basic usage is mostly similar. Each chart has the same base properties for working with a chart’s data, chart options, and callbacks. In addition to these base properties, some charts will have additional properties specific to their chart types. The examples in this guide will use the generic `` component, but you can apply the code found in the examples to other chart types as well. The examples will also mostly use data from a Sisense instance, but the code in the examples can be adapted to work with data from other sources as well. ## Data Properties Charts contain the following data properties for working with the data the chart displays: * `dataSet` - Data or reference to the data the chart displays * `filters` - Filters to apply to the chart data * `highlights` - Highlights that highlight data that pass certain criteria ### dataSet The `dataSet` property defines a chart’s data. The data you use for your chart can either be data queried from a data source in a Sisense instance or any explicit data. If no data is specified, the chart uses data from the `defaultDataSource` (specified in the `` for React projects and in the `SisenseContextConfig` object for Angular projects). The way the data is applied to a chart is defined by the chart’s `dataOptions` as explained below. #### Sisense data When using data from a Sisense instance, set the `dataSet` property’s value to the name of the Sisense data source. Typically, you retrieve the data source name from a data model you create using the `get-data-model` command of the Compose SDK CLI. Under the hood, a chart executes a query to connect to the data source and load the data as specified in the `dataOptions`, `filters`, and `highlights` as described below. You can also perform the query explicitly (using the `` component or the `useExecuteQuery()` hook in React or the `QueryService` in Angular) and use the returned data as a chart’s `dataSet` value. Running a query explicitly allows you to use the query results for a number of purposes, to populate multiple charts for example, instead of tying the query to a single chart. For example, the following code snippets set a chart’s dataset using data from the Sample ECommerce data model: ##### React ##### Angular ```ts // Component behavior in .component.ts import { measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; //... chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, }, ``` ```html ``` #### Explicit data In addition to data from Sisense, you can use any other data with Compose SDK charts. This can be static data that you provide or data from a 3rd party. The Compose SDK charts expect data in a specific tabular format. The data must be an object containing an array of `Column` objects and a two-dimensional array of row data. The `Column` objects have `name` and `type` properties. The row data can be made up of strings and numbers for raw data or `Cell` objects for data that includes some formatting information in addition to the raw data. Once you have your data formatted properly, you can use that data by setting the data object as the value of the `dataSet` property. For example, the following code snippets set a chart’s dataset using static data: ##### Sample data ```ts const sampleData = { columns: [ { name: 'Years', type: 'date' }, { name: 'Quantity', type: 'number' }, { name: 'Units', type: 'number' }, ], rows: [ ['2019', 5500, 1500], ['2020', 4471, 7000], ['2021', 1812, 5000], ['2022', 5001, 6000], ['2023', 2045, 4000], ], }; ``` ##### React ##### Angular ```ts // Component behavior in .component.ts chart = { chartType: 'column' as const, dataSet: sampleData, dataOptions: { category: [{ name: 'Years', type: 'date' }], value: [{ name: 'Quantity', type: 'number' }], }, }, ``` ```html ``` ### filters The `filters` property defines filters to apply to a chart’s data. You can create filters using filtering functions or connect your filtering to filter UI components. You can use filters on a single chart or use the same filter to filter multiple charts at once. #### Filter functions The `sdk-data` module contains factory functions to create text, number, and date filters on specified attributes. Call one or more of these functions to create filters that you then use to set the value of a chart's `filter` property. Use this filtering option when you know what you want to filter on when writing your code or you want to create a dynamic filter without using Compose SDK filtering UI components. For example, the following code snippets filter a chart’s dataset to only include data where the cost is greater than 1000. ##### React ##### Angular ```ts // Component behavior in .component.ts import { filterFactory, measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; //... chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, filters: [filterFactory.lessThan(DM.Commerce.Cost, 1000)], }; ``` ```html ``` #### Filter components The `sdk-ui` modules contain UI components for creating user-defined filters. You can use the filters created by these components to filter one of more charts. Add one or more of these components to create filters that you then use to set the value of a chart's `filter` property. Use this filtering option when you want to use pre-built components to allow your users to set filters. For example, the following code snippets filter a chart’s dataset based on the condition dimension. ##### React ##### Angular ```ts // Component behavior in .component.ts import { Filter, filterFactory, measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; //... DM = DM; conditionFilter = filterFactory.members(DM.Commerce.Condition, []); onMembersFilterChange({ filter }: { filter: Filter | null }) { if (!filter) return void console.log(filter); this.conditionFilter = filter; } chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, filters: [this.conditionFilter], }; ``` ```html ``` ### highlights Highlights work in a similar fashion to filters. But, whereas filters filter the data to only show the subset of the data that matches the filter, highlights show all the data, but call attention to the data that matches the filter. Not all filters will work as highlights though. The filter dimension must match those defined in the `dataOptions` of the chart (see the [Chart Properties](#chart-properties) section below). Just like filters, you can create highlights using filtering functions or connect your filtering to filter components. You can also use filters and highlights together to first filter the data that is displayed in a chart and then highlight some of that data. #### Filter functions for highlighting See above to learn about filtering functions. Use the filters returned by those functions to set a chart’s `highlights` property. For example, the following code snippets highlight certain age ranges in a chart. ##### React ##### Angular ```ts // Component behavior in .component.ts import { filterFactory, measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; //... chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, highlights: [ filterFactory.members(DM.Commerce.AgeRange, ['25-34', '35-44', '45-54']), ], }, ``` ```html ``` #### Filter components for highlighting See above to learn about filter components. Use the filters created by those components to set a chart’s `highlights` property. For example, the following code snippets highlight a chart’s data based on age range, with some default highlighting already set when the chart is loaded. ##### React ##### Angular ```ts // Component behavior in .component.ts import { Filter, filterFactory, measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; //... DM = DM; ageRangeFilter = filterFactory.members(DM.Commerce.AgeRange, []); onMembersFilterChange({ filter }: { filter: Filter | null }) { if (!filter) return void; this.ageRangeFilter = filter; } chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, highlights: [this.ageRangeFilter], }; ``` ```html ``` ## Chart Properties Charts contain the following chart properties for working with the data and style options: * `dataOptions` - Configuration for querying aggregate data and assigning data to a chart * `styleOptions` - Configuration options that define the style of chart elements. ### dataOptions A chart’s data options configure how the data in a chart is aggregated and how the data is applied to a chart. There are different configurations for different types of charts. Some types of configurations are: * Cartesian * Categorical * Scatter * Indicator Let’s take a look at the data options for Cartesian charts. After understanding how those work, you should have no problem using the other types of data options as well. Cartesian charts can include multiple values on both the X and Y axes, as well as a break-down by categories. The cartesian data options contain the following properties: * `category` * `value` * `breakBy` * `seriesToColorMap` In the examples below, we’ll show the data options that replace the placeholder in the following chart code. ##### React ```ts import { Chart } from '@sisense/sdk-ui'; import * as DM from '../sample-ecommerce'; import { measureFactory } from '@sisense/sdk-data'; //... ; ``` ##### Angular ```ts // Component behavior in .component.ts import { measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { /* data options go here */ }, }; ``` ```html ``` #### category and value The `category` and `value` properties determine how the axes of a chart are set up. Typically, a `category` is a dimension in your data model. These are entities such as dates, people, or location. This information doesn’t change that often. Typically, a `value` is a fact in your data model. These contain quantitative and numerical data such as transactions, inventory, or performance data. This information changes often and is generally the data you want to analyze using a chart. Often, you will run some sort of aggregation or other measure function to aggregate, summarize, and accumulate values. Let’s take a look at some examples of charts using the `category` and `value` properties. This is the simplest example where we have a single `category` and a single `value`. This chart shows the sum of revenue for a number of age ranges. ##### React ##### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue')], }, ``` *** This example adds an additional `value` to the chart . This chart shows the sum of cost alongside the sum of revenue for a number of age ranges. ##### React ##### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange], value: [ measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue'), measureFactory.sum(DM.Commerce.Cost, 'Sum of Cost'), ], }, ``` *** This example is similar to the one above in that it uses two `value` measures, but it changes the type of chart used in the second `value` and adds a right-side axis. This chart shows the sum of revenue and quantity for a number of age ranges. ##### React ###### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange], value: [ measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue'), { column: measureFactory.sum(DM.Commerce.Quantity, 'Sum of Quantity'), chartType: 'line', showOnRightAxis: true, }, ], }, ``` *** This example adds an additional `category` to a chart instead of an additional `value`. Notice how there are now labels across both the bottom and top of the Y-axis. This chart shows the sum of revenue for a number of condition types and age ranges. In this case it is probably preferable to use a `breakBy` instead of adding a second category, as explained below. ##### React ##### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange, DM.Commerce.Condition], value: [measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue')], }, ``` *** This example has two `category` attributes and two `value` measures. This chart shows the sum of revenue alongside the sum of cost for a number of condition types and age ranges. ##### React ##### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange, DM.Commerce.Condition], value: [ measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue'), measureFactory.sum(DM.Commerce.Cost, 'Sum of Cost'), ], }, ``` #### breakBy and seriesToColorMap The `breakBy` property, optionally determines how categories are broken down into subcategories. You can also use the `seriesToColorMap` property to customize the color of the broken down subcategories. Let’s take a look at some examples of charts using the `breakBy` and `seriesToColorMap` properties. This example has a single `category` and a single `value`, but the categories are broken down by an additional attribute. This chart shows the sum of revenue for a number of condition types and age ranges. ##### React ##### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue')], breakBy: [DM.Commerce.Condition], }, ``` *** This example shows the same data as the previous example, but the subcategories are colored using the colors defined in the series color map. ##### React ##### Angular ```ts dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue, 'Sum of Revenue')], breakBy: [DM.Commerce.Condition], seriesToColorMap: { New: '#7CB518', Refurbished: '#F3DE2C', Used: '#FB6107', Unspecified: '#FBB02D', }, }, ``` ### styleOptions A chart’s style options configure the styling of the chart’s elements. There are many different types of style configurations. The type of configuration you use for a specific chart depends on the chart type. Each of the configuration types has a different set of properties that are tailored to the types of charts they apply to. Note that you can achieve additional styling of your charts using the ``. For example, the following code snippets limit the number of slices in the pie chart and remove some of the labeling using style options. ##### React ##### Angular ```ts // Component behavior in .component.ts import { filterFactory, measureFactory } from '@sisense/sdk-data'; import * as DM from '../../sample-ecommerce'; //... chart = { chartType: 'pie' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, styleOptions: { convolution: { enabled: true, independentSlicesCount: 4, selectedConvolutionType: 'bySlicesCount', }, labels: { categories: false, }, width: 550, height: 400, }, }; ``` ```html ``` ## Callbacks Charts contain callback properties for defining functions that are called when certain events occur. Most charts have the following callback properties: * `onBeforeRender` * `onDataPointClick` * `onDataPointContextMenu` * `onDataPointsSelected` These callbacks allow you to perform actions to change a chart’s behavior or to react in some way to events that happen on a chart. The `onBeforeRender` callback allows you to customize the underlying chart element before it is rendered to your users. The callback receives an object representing the Highcharts options of the underlying chart element. Use the options object to change options values and then return the modified options object. The returned options are then used when rendering your chart. The `onData*` callbacks allow you to react to user interactions with your chart. The callbacks receive information about the data point the user is interacting with as well as an event object for the native event that occurred. You can use that information to respond to the event in any way you want. For example, the following code snippets remove the tooltip that shows by default when you hover over data points in the chart and replace it with an element that shows the data point information when the data point is clicked. ##### React ##### Angular ```ts // Component behavior in .component.ts import { Component } from '@angular/core'; import { measureFactory } from '@sisense/sdk-data'; import { DataPoint, HighchartsOptions } from '@sisense/sdk-ui'; import * as DM from '../../sample-ecommerce'; type PointInfo = { range: string; value: string } | null; //... pointInfo: PointInfo; chart = { chartType: 'column' as const, dataSet: DM.DataSource, dataOptions: { category: [DM.Commerce.AgeRange], value: [measureFactory.sum(DM.Commerce.Revenue)], }, }; onBeforeRender(options: HighchartsOptions) { if (options.tooltip) options.tooltip.enabled = false; return options; } onDataPointClick(...args: any[]) { const clickedPoint: DataPoint = args[0].point; this.pointInfo = { range: clickedPoint.categoryDisplayValue!, value: this.formatNumber(clickedPoint.value), }; } ``` ```html
Range: {{ pointInfo.range }} | Value {{ pointInfo.value }}
``` --- --- url: >- https://developer.sisense.com/guides/sdk/guides/charts/guide-external-charts.md --- # External Charts You can use the data you retrieve from your Sisense instance with just about any JavaScript charting library. Use Compose SDK to query your Sisense instance for the data you need for your charts. Then use that data to populate charts from a 3rd party charting library. In this guide we’ll use [Plotly.js](https://plotly.com/javascript/) charts, but the same principles apply to using any other charting library. In order to display your Sisense data in a 3rd party chart, you need to: * Query you Sisense instance for the data you want * Transform the data you receive from Sisense to the format required by the charting library you’re using * Apply the formatted data to the 3rd party chart. Let’s see how you would perform these steps to create this chart that shows the total cost and total revenue for a number of age ranges. ![Plotly chart](../../img/chart-guides/plotly.png "Plotly chart") ## Query The first step you need to perform to use Sisense data in a 3rd party chart is to query the data. There are a number of ways you can do this with Compose SDK. The two main ways are: * Use `executeQuery()` (as a hook in React or as the `QueryService` method in Angular) * Use the `` component (React only) In this guide, we’ll take the first approach of using `executeQuery()`. So we simply call `executeQuery()` and pass it the information we want to query from our data model. In this snippet, we’re querying the Sample ECommerce model to get total cost and total revenue categorized by age range. ##### React ##### Angular ```ts import * as DM from '../../sample-ecommerce'; import { measureFactory } from '@sisense/sdk-data'; import { QueryService } from '@sisense/sdk-ui-angular'; //... constructor(private queryService: QueryService) {} async ngOnInit(): Promise { const { data } = await this.queryService.executeQuery({ dataSource: DM.DataSource, dimensions: [DM.Commerce.AgeRange], measures: [ measureFactory.sum(DM.Commerce.Cost, 'Total Cost'), measureFactory.sum(DM.Commerce.Revenue, 'Total Revenue'), ], }); //.. } ``` ## Transform Now that we have the data from Sisense, we need to transform it to the format required by our 3rd party charting library. The code you need to write in this step will differ depending on what charting library you use. For our Plotly chart, we need to take the data retrieved from Sisense, which is represented as a two-dimensional array of row data, and transpose it to an object containing 3 arrays, one for each column of our data. We need to take this data from Sisense, organized as a two-dimensional array of row data, where each row is an object containing an age range and the corresponding cost and revenue totals: ```ts data = [ [ { data: '0-18', text: '0-18', blur: false }, { data: 4319951.642637288, text: '4319951.64263729', blur: false }, { data: 1527753.0939548016, text: '1527753.0939548', blur: false }, ], [ { data: '19-24', text: '19-24', blur: false }, { data: 8656480.951007009, text: '8656480.95100701', blur: false }, { data: 3859902.864543805, text: '3859902.8645438', blur: false }, ], [ { data: '25-34', text: '25-34', blur: false }, { data: 21185350.45013156, text: '21185350.4501316', blur: false }, { data: 4877853.600113869, text: '4877853.60011387', blur: false }, ], //... ]; ``` And turn in into this data, organized as three arrays, one for the age ranges, one for the corresponding total cost values, and one for the corresponding total revenue values: ```ts x1 = ['0-18', '19-24', '25-34', '35-44', '45-54', '55-64', '65+']; x2 = [ 4319951.642637288, 8656480.951007009, 21185350.45013156, //... ]; x3 = [ 1527753.0939548016, 3859902.864543805, 4877853.600113869, //... ]; ``` We can do that fairly easily with this code: ```ts const x1: string[] = []; const y1: number[] = []; const y2: number[] = []; data?.rows.forEach((row) => { x1.push(row[0].data); y1.push(row[1].data); y2.push(row[2].data); }); ``` Next, we need to take that data and create two “traces”, one for the total cost and another for the total revenue. ```ts const trace1: Plotly.Data = { x: x1, y: y1, type: 'bar', name: 'Total Cost', }; const trace2: Plotly.Data = { x: x1, y: y2, type: 'bar', name: 'Total Revenue', }; ``` Then, we can configure the layout of the chart. ```ts const layout = { title: 'Total Cost and Revenue by Age Ranges', xaxis: { title: 'Age Range' }, yaxis: { title: 'Cost and Revenue ($)' }, width: 900, height: 500, }; ``` That concludes our data transformation. We just need to package it up in a variable that we’ll use to set the Plotly chart’s data in the next step. ```ts const plotData = [trace1, trace2]; ``` ## Apply Finally, we can apply our transformed data to our 3rd party chart. In our case, we simply add a Plotly `` component with the data we transformed and the layout configuration we created. ##### React ```ts //... import Plot from 'react-plotly.js'; //.. return ; //... ``` ##### Angular ```ts // Component behavior in .component.ts //.. this.graph = { data: plotData, layout: layout, }; //... ``` ```html ``` ## Full Code When we put the steps together, the code for populating our 3rd party chart with data from Sisense looks like this: ##### React ```ts import { useExecuteQuery } from '@sisense/sdk-ui'; import * as DM from '../sample-ecommerce'; import { measureFactory } from '@sisense/sdk-data'; import Plot from 'react-plotly.js'; function MyPlotlyChart() { // Query const { data, isLoading, isError } = useExecuteQuery({ dataSource: DM.DataSource, dimensions: [DM.Commerce.AgeRange], measures: [measureFactory.sum(DM.Commerce.Cost, 'Total Cost'), measureFactory.sum(DM.Commerce.Revenue, 'Total Revenue')], }); if (isLoading) { return
Loading...
; } if (isError) { return
Error
; } // Transform const x1: number[] = []; const y1: number[] = []; const y2: number[] = []; data?.rows.forEach((row) => { x1.push(row[0].data); y1.push(row[1].data); y2.push(row[2].data); }); const trace1: Plotly.Data = { x: x1, y: y1, type: 'bar', name: 'Total Cost', }; const trace2: Plotly.Data = { x: x1, y: y2, type: 'bar', name: 'Total Revenue', }; const layout = { title: 'Total Cost and Revenue by Age Ranges', xaxis: { title: 'Age Range' }, yaxis: { title: 'Cost and Revenue ($)' }, width: 900, height: 500, }; const plotData = [trace1, trace2]; // Apply return ; } export default MyPlotlyChart; ``` ##### Angular ```ts import { Component } from '@angular/core'; import * as DM from '../../sample-ecommerce'; import { measureFactory } from '@sisense/sdk-data'; import { QueryService } from '@sisense/sdk-ui-angular'; import { PlotData } from 'plotly.js-dist-min'; @Component({ selector: 'app-analytics', templateUrl: './analytics.component.html', styleUrls: ['./analytics.component.css'], }) export class AnalyticsComponent { graph: { data: Partial[]; layout: {} } = { data: [], layout: {} }; constructor(private queryService: QueryService) {} async ngOnInit(): Promise { const { data } = await this.queryService.executeQuery({ dataSource: DM.DataSource, dimensions: [DM.Commerce.AgeRange], measures: [ measureFactory.sum(DM.Commerce.Cost, 'Total Cost'), measureFactory.sum(DM.Commerce.Revenue, 'Total Revenue'), ], }); const x1: number[] = []; const y1: number[] = []; const y2: number[] = []; data?.rows.forEach((row) => { x1.push(row[0].data); y1.push(row[1].data); y2.push(row[2].data); }); const trace1: Plotly.Data = { x: x1, y: y1, type: 'bar', name: 'Total Cost', }; const trace2: Plotly.Data = { x: x1, y: y2, type: 'bar', name: 'Total Revenue', }; const layout = { title: 'Total Cost and Revenue by Age Ranges', xaxis: { title: 'Age Range' }, yaxis: { title: 'Cost and Revenue ($)' }, width: 900, height: 500, }; const plotData = [trace1, trace2]; this.graph = { data: plotData, layout: layout, }; } } ``` ## Learn More To learn more about using 3rd party charts with Compose SDK, including using [Material UI](https://mui.com/x/react-charts/) with React, see [Take control of your data visualizations: Connecting to third-party libraries with Compose SDK](https://www.sisense.com/blog/take-control-of-your-data-visualizations/). --- --- url: 'https://developer.sisense.com/guides/sdk/guides/charts/guide-fusion-widgets.md' --- # Sisense Fusion Widgets Sisense Fusion widgets are charts from dashboard widgets that already exist in your Sisense instance. You can reuse the charts you already have and customize them using Compose SDK. You display charts from your existing Sisense instance using the `` component in React/Vue projects or `WidgetByIdComponent` in Angular projects. Note that you can also get the data from a dashboard widget and use it in a Compose SDK chart using the `useExecuteQueryByWidgetId()` hook in React/Vue projects or the `executeQueryByWidgetId()` query service method in Angular projects. ## WidgetById Properties Many of the properties of dashboard widget component’s properties are the same as the properties for other Compose SDK charts. To learn more about those properties, see [Compose SDK charts](./guide-compose-sdk-charts.md). There are also some properties which are specific to `WidgetById` components. ### dashboardOid and widgetOid In addition to any other chart properties you want to use with a `WidgetById`, you need to specify the `dashboardOid` and `widgetOid`, which identify which widget from your Sisense instance is displayed in the `WidgetById`. You can get the `dashboardOid` and `widgetOid` from the widget’s embed code in Sisense instance or using the Sisense REST API. You can also use the the `useGetDashboardModel` and `useGetDashboardModels` hooks in React/Vue or the `DashboardService` functions with the same names in Angular, to get `dashboardOid` and `widgetOid` values for a dashboard and its widgets. For example, the following code snippets get a chart or charts from a Sisense dashboard: #### React Hardcoded dashboard and widget IDs Retrieve widget IDs using hook #### Angular ```ts // Hardcoded dashboard and widget IDs // Component behavior in .component.ts chart = { dashboardOid: '65536353a90176002a68e5aa', widgetOid: '6553637ea90176002a68e5ac', }; ``` ```html ``` ### Widget Properties Since the WidgetById component includes the widget wrapper over a chart, it has properties for customizing the widget, including: * `title` - Widget title * `description` - Widget description * `styleOptions` - Configuration options that define the style of the widget #### Additional Properties In addition to the standard chart properties, widget properties, and properties to identify which widget to display, `WidgetById` also has properties that allow you to define the interplay between the widget as it is set up in your Sisense instance and customizations you apply in code. These properties include: * `includeDashboardFilters` - Whether to include dashboard filters and highlights that apply to the original widget in your Sisense instance * `filtersMergeStrategy` - How to reconcile dashboard filters and highlights that apply to the original widget in your Sisense instance and filters and highlights set in code --- --- url: 'https://developer.sisense.com/guides/sdk/guides/cli.md' --- # CLI Use the Compose SDK CLI to work with your Compose SDK project. The CLI has the following commands: * [`get-data-model`](#get-data-model): Creates a [TypeScript representation of a data model](./data-model.md) * [`get-api-token`](#get-api-token): Gets an [API token for authentication](../getting-started/authentication-security.md#api-token) ## get-data-model The `get-data-model` command creates a [TypeScript representation of a data model](./data-model.md). Use either a username/password, API token, or WAT token to authenticate when running this command. ### Options * `dataSource` - (`string`): The name of the data model to create a TypeScript representation of * `output` - (`string` | `undefined`): The `*.ts` file to write the data model file to * `password` - (`string` | `undefined`): Password when using username/password authentication (if omitted when using a username/password to authenticate, the CLI will prompt you to enter your password) * `token` - (`string` | `undefined`): API token when using API token authentication * `url` - (`string`): URL of the Sisense instance that contains your data model * `username` - (`string` | `undefined`): Username when using username/password authentication * `wat` - (`string` | `undefined`): WAT token when using WAT authentication ### Example This example command creates a data model file for the Sample ECommerce data model using username/password authentication. After running this command, the CLI will prompt you for your password. (Be sure to replace `` and `` with your actual username and Sisense instance URL). ```sh npx @sisense/sdk-cli@latest get-data-model --dataSource "Sample ECommerce" --url --output src/sample-ecommerce.ts --username ``` ## get-api-token The `get-api-token` command gets an [API token for authentication](../getting-started/authentication-security.md#api-token). Use a username/password to authenticate when running this command. ### Options * `password` - (`string` | `undefined`): Password for authentication (if omitted, the CLI will prompt you to enter your password) * `url` - (`string`): URL of the Sisense instance that your user exists in * `username` - (`string`): Username for authentication ### Example This example gets an API Token using username/password authentication. After running this command, the CLI will prompt you for your password. (Be sure to replace `` and `` with your actual username and Sisense instance URL). ```sh npx @sisense/sdk-cli@latest get-api-token --username --url ``` --- --- url: 'https://developer.sisense.com/guides/sdk/guides/client-query-caching.md' --- # Client Query Caching (Alpha) Compose SDK provides a client-side caching mechanism that enhances chart rendering performance and reduces network requests for data queries. If the results of an identical query already exist in the client-side cache, those results will be used automatically instead of making the same query request again to the Fusion API. ## Enabling Client Caching To enable client-side caching, set the `AppConfig.queryCacheConfig.enabled` property to `true` in your `SisenseContextProvider`. ```tsx ``` This setting enables the caching mechanism globally for all JAQL queries made through the SDK.\ The cache can store up to 250 distinct JAQL queries. Once this limit is reached, the oldest entries are purged to make room for new ones.\ You can manually clear the cache by obtaining a `CacheClient` instance and calling its `clear` method, which removes all cached queries: ```tsx const cacheClient = useQueryCache(); cacheClient.clear(); ``` **Note:** While `queryCacheConfig` is supported in all frameworks (React, Angular, Vue), the ability to clear the cache via `code` is currently only supported in React. This current limitation is the reason the feature is currently in `alpha` status. Refreshing the page in the browser also refreshes the cache (see below). ## Query Caching Clarifications This client-side caching mechanism is distinct from Sisense Fusion's server-side caching. * It does not influence how JAQL requests are handled by Fusion * It does not affect the server side caching within Fusion * It cannot affect the response times for any JAQL requests sent to Fusion Instead, it stores the results of JAQL queries in memory for quicker access without repeated server requests from the browser. * It does not store data permanently. Reloading the page will clear the cache. * It does not share cache across different browser tabs, or between different users. * It is designed to improve performance and user experience for data that is accessed frequently or multiple times in the UI. **Example:** A user frequently switches between tabs or sections of a UI which contain Compose SDK dashboards, widgets or other components that query data. Client-side caching will help avoid reloading the same data over the network each time a dashboard or widget is rendered, providing a more responsive user experience. --- --- url: 'https://developer.sisense.com/guides/sdk/guides/custom-widgets/index.md' --- # Custom Widgets Here you'll find guides that will help you get started with the custom widgets in Compose SDK. --- --- url: >- https://developer.sisense.com/guides/sdk/guides/custom-widgets/custom-widgets-angular.md --- # Custom Widgets > **Note**: > This guide is for [ Angular](../../getting-started/quickstart-angular.md). For other frameworks, see the [ React](custom-widgets-react.md) and [ Vue](custom-widgets-vue.md) guides. This guide explains how to define your own custom widget component and register it in your application code, so that it will be automatically rendered (based on the corresponding widget type) when using the `DashboardById` component. Custom widgets in Compose SDK can be used to replace Fusion plugins when displaying dashboards. **Note:** It is assumed that the application is [already configured correctly](../../getting-started/quickstart-angular.md) for use with Compose SDK. ## Sample dashboard The `histogramwidget` plugin is included with Sisense Fusion, so we'll be using it as our example. We'll start by creating a dashboard in Fusion, containing a single `histogramwidget` widget with `Sample ECommerce` as its data source. ![Dashboard in Fusion](../../img/plugins-guide/dashboard-in-fusion.png "Dashboard in Fusion") ## Displaying the dashboard in your application To display a dashboard using Compose SDK, we need the `oid` for the relevant dashboard. The simplest way to find this, is to copy the value from the end of the URL when viewing the dashboard in Fusion, e.g. `/app/main/dashboards/{dashboardOid}`. The dashboard can be easily displayed using the `csdk-dashboard-by-id` component, passing this value into the `dashboardOid` input. ```html ``` Since Compose SDK does not support the `histogramwidget` plugin out of the box, it is expected that Compose SDK will display an error in place of the histogram widget. ![Dashboard in Compose SDK (no registered custom widget)](../../img/plugins-guide/dashboard-in-csdk-unregistered.png "Dashboard in Compose SDK (no registered custom widget)") In order to resolve this, we will explore how to define a custom widget component and register it with Compose SDK, so that it knows what to do when it encounters a `histogramwidget` plugin from Fusion. ## Defining a custom widget using Compose SDK Before registering our custom widget, we first need to define a custom widget component that will replace the Fusion plugin. This component will: 1. Receive the props that Compose SDK will pass to our custom widget when rendering the `csdk-dashboard-by-id` component 2. Run a data query using those props 3. Render a visualization with the results Purely for the **simplicity** of this guide, we have chosen to define a custom widget component which renders a table of the query results. In reality, you would more likely define an Angular implementation of a histogram chart, or however else you wish to represent the Fusion plugin in your Compose SDK dashboard. This guide also aims to demonstrate the flexibility of the `registerCustomWidget` interface - as long as you provide a component that matches the shape of [`CustomWidgetComponent`](../../modules/sdk-ui-angular/type-aliases/type-alias.CustomWidgetComponent.md), Compose SDK will render that component as a replacement for the designated Fusion plugin. A note on the `dataOptions` input that is passed to our component: For those familiar with the Fusion plugin / add-on architecture, `dataOptions` is the Compose SDK equivalent of `panels` on the [WidgetMetadata](https://developer.sisense.com/guides/customJs/jsApiRef/widgetClass/widget-metadata.html) object. Compose SDK translates all widget metadata and filters to Compose SDK data structures (e.g. values inside [`dataOptions`](../../modules/sdk-ui-angular/type-aliases/type-alias.ChartDataOptions.md) are of type [`StyledColumn`](../../modules/sdk-ui-angular/interfaces/interface.StyledColumn.md) and [`StyledMeasureColumn`](../../modules/sdk-ui-angular/interfaces/interface.StyledMeasureColumn.md), the same types you'd expect for [`dataOptions`](../../modules/sdk-ui-angular/type-aliases/type-alias.ChartDataOptions.md) into the [`csdk-chart`](../../modules/sdk-ui-angular/charts/class.ChartComponent.md) component). In the custom widget component, we can use the inputs directly with the `executeCustomWidgetQuery` method from the `QueryService` which runs a data query and applies some formatting on the results (defined by the `StyledColumn` information in `dataOptions`). ```typescript import { Component, Input, OnInit } from '@angular/core'; import { QueryService } from '@sisense/sdk-ui-angular'; import type { CustomWidgetComponentProps, QueryResultData } from '@sisense/sdk-ui-angular'; import { Observable } from 'rxjs'; @Component({ selector: 'app-results-table', template: `
{{ column.name }}
{{ cell.text }}
` }) export class ResultsTableComponent implements OnInit, CustomWidgetComponentProps { @Input() title!: string; @Input() dataOptions!: any; @Input() filters!: any[]; data$!: Observable; constructor(private queryService: QueryService) {} ngOnInit() { this.data$ = this.queryService.executeCustomWidgetQuery({ title: this.title, dataOptions: this.dataOptions, filters: this.filters }); } trackByIndex(index: number): number { return index; } } ``` **Note:** Don't forget to declare your custom widget component in your Angular module If you prefer to work with the raw data without any formatting applied, you can use `extractDimensionsAndMeasures` with `executeQuery` instead. ```typescript import { extractDimensionsAndMeasures } from '@sisense/sdk-ui-angular'; ngOnInit() { const { dimensions, measures } = extractDimensionsAndMeasures(this.dataOptions); this.data$ = this.queryService.executeQuery({ dimensions, measures, filters: this.filters, }); } ``` ## Registering the custom widget with Compose SDK To register the custom widget, we need to inject the `CustomWidgetsService` and call `registerCustomWidget`. ```typescript import { Component, OnInit } from '@angular/core'; import { CustomWidgetsService } from '@sisense/sdk-ui-angular'; import { ResultsTableComponent } from './results-table.component'; @Component({ selector: 'app-root', template: ` ` }) export class AppComponent implements OnInit { constructor(private customWidgetsService: CustomWidgetsService) {} ngOnInit() { this.customWidgetsService.registerCustomWidget('histogramwidget', ResultsTableComponent); } } ``` If we refresh our application, instead of seeing the error in place of the widget as before, we should now see something like this: ![Dashboard in Compose SDK (registered custom widget)](../../img/plugins-guide/dashboard-in-csdk-registered.png "Dashboard in Compose SDK (registered custom widget)") ## Summary Here's what we accomplished: * Displayed an existing Fusion dashboard in our application by rendering a `csdk-dashboard-by-id` component * Created an Angular component that uses its inputs to execute a data query and display the results in a table * Registered that table component as a custom widget to be shown in place of the `histogramwidget` Fusion plugin when it is rendered inside of a `csdk-dashboard-by-id` component Obviously, we didn't end up with a new histogram component in Angular (yet), but hopefully the simplicity of this guide gives you the tools you need to make that, or anything else, happen! --- --- url: >- https://developer.sisense.com/guides/sdk/guides/custom-widgets/custom-widgets-react.md --- # Custom Widgets > **Note**: > This guide is for [ React](../../getting-started/quickstart.md). For other frameworks, see the [ Angular](custom-widgets-angular.md) and [ Vue](custom-widgets-vue.md) guides. This guide explains how to define your own custom widget component and register it in your application code, so that it will be automatically rendered (based on the corresponding widget type) when using the `DashboardById` component. Custom widgets in Compose SDK can be used to replace Fusion plugins when displaying dashboards. **Note:** It is assumed that the application is [already configured correctly](../../getting-started/quickstart.md) for use with Compose SDK. ## Sample dashboard The `histogramwidget` plugin is included with Sisense Fusion, so we'll be using it as our example. We'll start by creating a dashboard in Fusion, containing a single `histogramwidget` widget with `Sample ECommerce` as its data source. ![Dashboard in Fusion](../../img/plugins-guide/dashboard-in-fusion.png "Dashboard in Fusion") ## Displaying the dashboard in your application To display a dashboard using Compose SDK, we need the `oid` for the relevant dashboard. The simplest way to find this, is to copy the value from the end of the URL when viewing the dashboard in Fusion, e.g. `/app/main/dashboards/{dashboardOid}`. The dashboard can be easily displayed using the `DashboardById` component, passing this value into the `dashboardOid` prop. ```ts import { DashboardById } from '@sisense/sdk-ui'; function App() { return ( ); } export default App; ``` Since Compose SDK does not support the `histogramwidget` plugin out of the box, it is expected that Compose SDK will display an error in place of the histogram widget. ![Dashboard in Compose SDK (no registered custom widget)](../../img/plugins-guide/dashboard-in-csdk-unregistered.png "Dashboard in Compose SDK (no registered custom widget)") In order to resolve this, we will explore how to define a custom widget component and register it with Compose SDK, so that it knows what to do when it encounters a `histogramwidget` plugin from Fusion. ## Defining a custom widget using Compose SDK Before registering our custom widget, we first need to define a custom widget component that will replace the Fusion plugin. This component will: 1. Receive the props that Compose SDK will pass to our custom widget when rendering the `DashboardById` component 2. Run a data query using those props 3. Render a visualization with the results Purely for the **simplicity** of this guide, we have chosen to define a custom widget component which renders a table of the query results. In reality, you would more likely define a React implementation of a histogram chart, or however else you wish to represent the Fusion plugin in your Compose SDK dashboard. This guide also aims to demonstrate the flexibility of the `registerCustomWidget` interface - as long as you provide a functional component that matches the shape of [`CustomWidgetComponent`](../../modules/sdk-ui/type-aliases/type-alias.CustomWidgetComponent.md), Compose SDK will render that component as a replacement for the designated Fusion plugin. A note on the `dataOptions` prop that is passed to our component: For those familiar with the Fusion plugin / add-on architecture, `dataOptions` is the Compose SDK equivalent of `panels` on the [WidgetMetadata](https://developer.sisense.com/guides/customJs/jsApiRef/widgetClass/widget-metadata.html) object. Compose SDK translates all widget metadata and filters to Compose SDK data structures (e.g. values inside [`dataOptions`](../../modules/sdk-ui/type-aliases/type-alias.ChartDataOptions.md) are of type [`StyledColumn`](../../modules/sdk-ui/interfaces/interface.StyledColumn.md) and [`StyledMeasureColumn`](../../modules/sdk-ui/interfaces/interface.StyledMeasureColumn.md), the same types you'd expect for [`dataOptions`](../../modules/sdk-ui/type-aliases/type-alias.ChartDataOptions.md) into the [`Chart`](../../modules/sdk-ui/charts/function.Chart.md) component). In the custom widget component, we can use the props directly with the `useExecuteCustomWidgetQuery` hook which runs a data query and applies some formatting on the results (defined by the `StyledColumn` information in `dataOptions`). ```ts import { CustomWidgetComponent, useExecuteCustomWidgetQuery } from '@sisense/sdk-ui'; const ResultsTable: CustomWidgetComponent = (props) => { const { data } = useExecuteCustomWidgetQuery(props); if (!data) { return null; } return ( {data.columns.map((column, columnIndex) => ( ))} {data.rows.map((row, rowIndex) => ( {row.map((cell, cellIndex) => ( ))} ))}
{column.name}
{cell.text}
); }; ... ``` If you prefer to work with the raw data without any formatting applied, you can use `extractDimensionsAndMeasures` with `useExecuteQuery` instead. ```ts import { useExecuteQuery, extractDimensionsAndMeasures } from '@sisense/sdk-ui'; const { dimensions, measures } = extractDimensionsAndMeasures(props.dataOptions); const { data } = useExecuteQuery({ dimensions, measures, filters: props.filters, }); ``` ## Registering the custom widget with Compose SDK To register the custom widget, we need to call `registerCustomWidget`, which is returned from the `useCustomWidgets` hook. ```ts import { DashboardById, useCustomWidgets } from '@sisense/sdk-ui'; ... function App() { const { registerCustomWidget } = useCustomWidgets(); registerCustomWidget('histogramwidget', ResultsTable); return ; } ... ``` If we refresh our application, instead of seeing the error in place of the widget as before, we should now see something like this: ![Dashboard in Compose SDK (registered custom widget)](../../img/plugins-guide/dashboard-in-csdk-registered.png "Dashboard in Compose SDK (registered custom widget)") ## Summary Here's what we accomplished: * Displayed an existing Fusion dashboard in our application by rendering a `DashboardById` component * Created a React component that uses its props to execute a data query and display the results in a table * Registered that table component as a custom widget to be shown in place of the `histogramwidget` Fusion plugin when it is rendered inside of a `DashboardById` component Obviously, we didn't end up with a new histogram component in React (yet), but hopefully the simplicity of this guide gives you the tools you need to make that, or anything else, happen! ## Migration from previous Plugin Interface to Custom Widget Interface If you have existing code that uses the [previous Compose SDK "plugin" interface](https://developer.sisense.com/guides/sdkPrevious/v1/guides/chart-plugins.html), here's how to migrate to the new "custom widget" interface. ### API Changes | Previous (Compose SDK Plugin Interface) | New (ComposeSDK Custom Widget Interface) | |------------------------------|-------------------------------------| | `usePlugins()` | `useCustomWidgets()` | | `registerPlugin()` | `registerCustomWidget()` | | `PluginComponent` | `CustomWidgetComponent` | | `PluginComponentProps` | `CustomWidgetComponentProps` | | `useExecutePluginQuery()` | `useExecuteCustomWidgetQuery()` | | `widget.pluginType` | `widget.customWidgetType` | | `widget.widgetType -> 'plugin'` | `widget.widgetType -> 'custom'` | ### Code Migration Example **Before (Compose SDK Plugin Interface):** ```ts import { DashboardById, PluginComponent, useExecutePluginQuery, usePlugins } from '@sisense/sdk-ui'; const MyWidget: PluginComponent = (props) => { const { data } = useExecutePluginQuery(props); // ... component implementation }; function App() { const { registerPlugin } = usePlugins(); registerPlugin('my-widget', MyWidget); // 'my-widget' represents a Fusion plugin return ; } ``` **After (Compose SDK Custom Widget Interface):** ```ts import { DashboardById, CustomWidgetComponent, useExecuteCustomWidgetQuery, useCustomWidgets } from '@sisense/sdk-ui'; const MyWidget: CustomWidgetComponent = (props) => { const { data } = useExecuteCustomWidgetQuery(props); // ... component implementation }; function App() { const { registerCustomWidget } = useCustomWidgets(); registerCustomWidget('my-widget', MyWidget); // 'my-widget' represents a Fusion plugin return ; } ``` ### Migration Steps 1. **Update imports**: Change all Compose SDK plugin-related imports to their custom widget equivalents 2. **Update type annotations**: Replace `PluginComponent` with `CustomWidgetComponent` and `PluginComponentProps` with `CustomWidgetComponentProps` 3. **Update hooks**: Replace `usePlugins()` with `useCustomWidgets()` and `useExecutePluginQuery()` with `useExecuteCustomWidgetQuery()` 4. **Update registration calls**: Replace `registerPlugin()` with `registerCustomWidget()` The functionality remains the same - only the Compose SDK naming convention has changed, while adding support for [Angular](custom-widgets-angular.md) and [Vue](custom-widgets-vue.md). Custom widgets in Compose SDK still serve as replacements for Fusion plugins when displaying dashboards. --- --- url: >- https://developer.sisense.com/guides/sdk/guides/custom-widgets/custom-widgets-vue.md --- # Custom Widgets > **Note**: > This guide is for [ Vue](../../getting-started/quickstart-vue.md). For other frameworks, see the [ React](custom-widgets-react.md) and [ Angular](custom-widgets-angular.md) guides. This guide explains how to define your own custom widget component and register it in your application code, so that it will be automatically rendered (based on the corresponding widget type) when using the `DashboardById` component. Custom widgets in Compose SDK can be used to replace Fusion plugins when displaying dashboards. **Note:** It is assumed that the application is [already configured correctly](../../getting-started/quickstart-vue.md) for use with Compose SDK. ## Sample dashboard The `histogramwidget` plugin is included with Sisense Fusion, so we'll be using it as our example. We'll start by creating a dashboard in Fusion, containing a single `histogramwidget` widget with `Sample ECommerce` as its data source. ![Dashboard in Fusion](../../img/plugins-guide/dashboard-in-fusion.png "Dashboard in Fusion") ## Displaying the dashboard in your application To display a dashboard using Compose SDK, we need the `oid` for the relevant dashboard. The simplest way to find this, is to copy the value from the end of the URL when viewing the dashboard in Fusion, e.g. `/app/main/dashboards/{dashboardOid}`. The dashboard can be easily displayed using the `DashboardById` component, passing this value into the `dashboardOid` prop. ```vue ``` Since Compose SDK does not support the `histogramwidget` plugin out of the box, it is expected that Compose SDK will display an error in place of the histogram widget. ![Dashboard in Compose SDK (no registered custom widget)](../../img/plugins-guide/dashboard-in-csdk-unregistered.png "Dashboard in Compose SDK (no registered custom widget)") In order to resolve this, we will explore how to define a custom widget component and register it with Compose SDK, so that it knows what to do when it encounters a `histogramwidget` plugin from Fusion. ## Defining a custom widget using Compose SDK Before registering our custom widget, we first need to define a custom widget component that will replace the Fusion plugin. This component will: 1. Receive the props that Compose SDK will pass to our custom widget when rendering the `DashboardById` component 2. Run a data query using those props 3. Render a visualization with the results Purely for the **simplicity** of this guide, we have chosen to define a custom widget component which renders a table of the query results. In reality, you would more likely define a Vue implementation of a histogram chart, or however else you wish to represent the Fusion plugin in your Compose SDK dashboard. This guide also aims to demonstrate the flexibility of the `registerCustomWidget` interface - as long as you provide a component that matches the shape of [`CustomWidgetComponent`](../../modules/sdk-ui-vue/type-aliases/type-alias.CustomWidgetComponent.md), Compose SDK will render that component as a replacement for the designated Fusion plugin. A note on the `dataOptions` prop that is passed to our component: For those familiar with the Fusion plugin / add-on architecture, `dataOptions` is the Compose SDK equivalent of `panels` on the [WidgetMetadata](https://developer.sisense.com/guides/customJs/jsApiRef/widgetClass/widget-metadata.html) object. Compose SDK translates all widget metadata and filters to Compose SDK data structures (e.g. values inside [`dataOptions`](../../modules/sdk-ui-vue/type-aliases/type-alias.ChartDataOptions.md) are of type [`StyledColumn`](../../modules/sdk-ui-vue/interfaces/interface.StyledColumn.md) and [`StyledMeasureColumn`](../../modules/sdk-ui-vue/interfaces/interface.StyledMeasureColumn.md), the same types you'd expect for [`dataOptions`](../../modules/sdk-ui-vue/type-aliases/type-alias.ChartDataOptions.md) into the [`Chart`](../../modules/sdk-ui-vue/charts/class.Chart.md) component). In the custom widget component, we can use the props directly with the `useExecuteCustomWidgetQuery` composable which runs a data query and applies some formatting on the results (defined by the `StyledColumn` information in `dataOptions`). ```vue ``` If you prefer to work with the raw data without any formatting applied, you can use `extractDimensionsAndMeasures` with `useExecuteQuery` instead. ```vue ``` ## Registering the custom widget with Compose SDK To register the custom widget, we need to use the `useCustomWidgets` composable and call `registerCustomWidget`. ```vue ``` If we refresh our application, instead of seeing the error in place of the widget as before, we should now see something like this: ![Dashboard in Compose SDK (registered custom widget)](../../img/plugins-guide/dashboard-in-csdk-registered.png "Dashboard in Compose SDK (registered custom widget)") ## Summary Here's what we accomplished: * Displayed an existing Fusion dashboard in our application by rendering a `DashboardById` component * Created a Vue component that uses its props to execute a data query and display the results in a table * Registered that table component as a custom widget to be shown in place of the `histogramwidget` Fusion plugin when it is rendered inside of a `DashboardById` component Obviously, we didn't end up with a new histogram component in Vue (yet), but hopefully the simplicity of this guide gives you the tools you need to make that, or anything else, happen! --- --- url: 'https://developer.sisense.com/guides/sdk/guides/dashboards/index.md' --- # Embedded Dashboards Alternative to iFrame and Embed SDK, Compose SDK now allows you (1) to embed an existing Fusion dashboard into your application, (2) to customize the dashboard to your specific needs, or (3) to compose a dashboard fully in code. These capabilities are available in React, Angular, and Vue. --- --- url: >- https://developer.sisense.com/guides/sdk/guides/dashboards/guide-1-embed-fusion-dashboard.md --- # 1 | Embed Fusion Dashboard ## Component `DashboardById` To embed a Fusion dashboard into your application as-is, you can use component `DashboardById` available from the `sdk-ui-*` package. This method is the quickest and simplest, but it comes with limited customization options for the dashboard. The following code examples and screenshots use the Sample ECommerce dashboard identified by OID pre-existing in a Sisense instance. ### React ```ts import { DashboardById } from '@sisense/sdk-ui'; const CodeExample = () => { return ( ); }; export default CodeExample; ``` ### Angular ```ts import { Component } from '@angular/core'; @Component({ selector: 'code-example', template: `
`, }) export class CodeExampleComponent { dashboardOid = 'your-dashboard-oid'; } ``` ### Vue ```ts ``` ![Embedded Sample ECommerce Dashboard](../../img/dashboard-guides/fusion-dashboard-light-theme.png "Embedded Sample ECommerce Dashboard") ::: tip Note Follow [this guide](../custom-widgets/index.md) to learn how to define your own custom widget component, and have it rendered in place of a corresponding Fusion widget plugin when using the `DashboardById` component. ::: ## Simple customization While `DashboardById` does not allow customizations, you can still use a `ThemeProvider` (React and Vue) or `ThemeService` (Angular) to apply a consistent look and feel to the dashboard elements including toolbar, widgets panel, and filters panel. The following React code example renders the dashboard in dark mode: ```ts import { DashboardById, ThemeProvider, getDefaultThemeSettings } from '@sisense/sdk-ui'; const CodeExample = () => { const darkTheme = getDefaultThemeSettings(true); return ( ); }; export default CodeExample; ``` ![Embedded Sample ECommerce Dashboard in Dark Mode](../../img/dashboard-guides/fusion-dashboard-dark-theme.png "Embedded Sample ECommerce Dashboard in Dark Mode") ## Next Up In this section you learned how to embed a Fusion dashboard using component `DashboardById`. In the next section, you'll see how to customize the elements of the embedded Fusion dashboard. Go to the [next lesson](./guide-2-customize-fusion-dashboard.md). --- --- url: >- https://developer.sisense.com/guides/sdk/guides/dashboards/guide-2-customize-fusion-dashboard.md --- # 2 | Customize Fusion Dashboard ## Generic `Dashboard` Component You can write a little more code to embed the dashboard with customizations. In the following example, we use a combination of the `useGetDashboardModel` hook and `Dashboard` component, in lieu of the `DashboardById` component. ##### React ```ts import { Dashboard, dashboardModelTranslator, useGetDashboardModel } from '@sisense/sdk-ui'; const CodeExample = () => { // DashboardModel is the data representation of a Fusion dashboard in Compose SDK const { dashboard } = useGetDashboardModel({ dashboardOid: 'your-dashboard-oid', includeFilters: true, includeWidgets: true, }); if (!dashboard) { return null; } // DashboardProps is a set of properties for the generic Dashboard component const { title, widgets, layoutOptions, filters, styleOptions, widgetsOptions } = dashboardModelTranslator.toDashboardProps(dashboard); return ( ); }; export default CodeExample; ``` ##### Angular ```ts import { Component } from '@angular/core'; import { type DashboardProps, DashboardService, dashboardModelTranslator, } from '@sisense/sdk-ui-angular'; @Component({ selector: 'code-example', template: `
`, }) export class CodeExampleComponent { dashboardProps: DashboardProps | null = null; constructor(private dashboardService: DashboardService) {} async ngOnInit(): Promise { const dashboardModel = await this.dashboardService.getDashboardModel( '66fb233ae2c222003368dac1', { includeWidgets: true, includeFilters: true }, ); this.dashboardProps = dashboardModelTranslator.toDashboardProps(dashboardModel); } } ``` ##### Vue ```ts ``` ![Embedded Sample ECommerce Dashboard](../../img/dashboard-guides/fusion-dashboard-light-theme.png "Embedded Sample ECommerce Dashboard") As shown in the code, there is a clear separation between `DashboardModel` and `DashboardProps`. In Compose SDK, `DashboardModel` is the data representation of a Fusion dashboard – in other words, metadata of a Fusion dashboard. On the other hand, `DashboardProps` is a set of properties for the generic `Dashboard` component. Following the design principle of Separation of Concerns, `DashboardProps` and `Dashboard` are no longer coupled to the `DashboardModel`. It is still very simple to translate the `DashboardModel` to `DashboardProps` using the provided utilty function, `dashboardModelTranslator.toDashboardProps`, and you have access to all elements of the dashboard for manipulation, which we will demonstrate in the next example. ## Customize Embedded Fusion Dashboard Here, we add a dashboard filter on the `Gender` dimension. We also customize the look of the charts by adding rounded corners for the bars. Basically, a dashboard and its props are composed of existing building blocks in Compose SDK including `ChartWidget` and `*FilterTile`. Any APIs supported in chart widgets like `onBeforeRender`, `onDataPointClick` are also available for manipulation in `DashboardProps.widgets`. ##### React ```ts import { Dashboard, dashboardModelTranslator, useGetDashboardModel } from '@sisense/sdk-ui'; import * as DM from './sample-ecommerce'; import { filterFactory } from '@sisense/sdk-data'; const CodeExample = () => { // DashboardModel is the data representation of a Fusion dashboard in Compose SDK const { dashboard } = useGetDashboardModel({ dashboardOid: 'your-dashboard-oid', includeFilters: true, includeWidgets: true, }); if (!dashboard) { return null; } // DashboardProps is a set of properties for the Dashboard component const { title, widgets, layoutOptions, filters, styleOptions, widgetsOptions } = dashboardModelTranslator.toDashboardProps(dashboard); // Add a filter to the dashboard filters const updatedFilters = [...filters, filterFactory.members(DM.Commerce.Gender, ['Male'])]; // Customize the look of the chart widgets that are based on Highcharts const updatedWidgets = widgets.map((widget) => ({ ...widget, onBeforeRender: (highchartsOptions: any) => { highchartsOptions.series.forEach((s: any) => { s.borderRadiusTopLeft = `10px`; s.borderRadiusTopRight = `10px`; }); return highchartsOptions; }, })); return ( ); }; export default CodeExample; ``` ##### Angular ```ts import { Component } from '@angular/core'; import { type DashboardProps, DashboardService, dashboardModelTranslator, } from '@sisense/sdk-ui-angular'; import * as DM from './sample-ecommerce'; import { filterFactory } from '@sisense/sdk-data'; @Component({ selector: 'code-example', template: `
`, }) export class CodeExampleComponent { dashboardProps: DashboardProps | null = null; constructor(private dashboardService: DashboardService) {} async ngOnInit(): Promise { const dashboardModel = await this.dashboardService.getDashboardModel( 'your-dashboard-oid', { includeWidgets: true, includeFilters: true }, ); this.dashboardProps = dashboardModelTranslator.toDashboardProps(dashboardModel); const { filters, widgets } = this.dashboardProps; // Add a filter to the dashboard filters this.dashboardProps.filters = [ ...(filters ?? []), filterFactory.members(DM.Commerce.Gender, ['Male']), ]; // Customize the look of the chart widgets that are based on Highcharts this.dashboardProps.widgets = widgets.map((widget) => ({ ...widget, onBeforeRender: (highchartsOptions: any) => { highchartsOptions.series.forEach((s: any) => { s.borderRadiusTopLeft = `10px`; s.borderRadiusTopRight = `10px`; }); return highchartsOptions; }, })); } } ``` ##### Vue ```ts ``` ![Embedded Sample ECommerce Dashboard with Customizations](../../img/dashboard-guides/fusion-dashboard-customized.png "Embedded Sample ECommerce Dashboard with Customizations") ::: tip Note Alternative to manipulating `DashboardProps.filters` directly, you can use dashboard helper functions available from each of the `sdk-ui-*` packages. ::: ## Next Up In this section you learned how to embed a Fusion dashboard and customize it to your specific needs using the `Dashboard` component. In the next section, you'll see how to compose a dashboard fully in code. Go to the [next lesson](./guide-3-compose-dashboard-in-code.md). --- --- url: >- https://developer.sisense.com/guides/sdk/guides/dashboards/guide-3-compose-dashboard-in-code.md --- # 3 | Compose Dashboard In Code It’s time to detach ourselves from Fusion dashboards. In the following examples, you'll learn how to programmatically create a dashboard based on the data fetched from Sisense using the generic `Dashboard` component — without relying on any pre-existing Fusion dashboards. ::: tip Note The examples below assume that the app is already set up to connect to the Sample ECommerce data model in a Sisense instance using `SisenseContextProvider` – see [Quickstart guides](../../getting-started/index.md). To keep the code concise, the examples are provided in React, but the same configurations can be adapted for Angular and Vue. ::: ## Create an Empty Dashboard In this example, we'll start `dashboardProps` almost empty with just title and an empty array of WidgetProps. ##### React ```ts import { Dashboard, DashboardProps, WidgetProps } from '@sisense/sdk-ui'; import { useMemo } from 'react'; const CodeExample = () => { // DashboardProps is a set of properties for the Dashboard component const dashboardProps: DashboardProps = useMemo(() => { const widgets: WidgetProps[] = []; return { title: 'Fabulous ECommerce Dashboard', widgets }; }, []); return ; }; export default CodeExample; ``` ![Empty Sample ECommerce Dashboard](../../img/dashboard-guides/generic-dashboard-empty.png "Empty Sample ECommerce Dashboard") ## Add a Chart Widget Let's add a chart widget to the list of widgets. It is a simple indicator displaying Total Revenue. ##### React ```ts import { Dashboard, DashboardProps, WidgetProps } from '@sisense/sdk-ui'; import * as DM from './sample-ecommerce'; import { useMemo } from 'react'; import { measureFactory } from '@sisense/sdk-data'; const CodeExample = () => { // DashboardProps is a set of properties for the Dashboard component const dashboardProps: DashboardProps = useMemo(() => { const widgets: WidgetProps[] = [ { id: 'widget-1', widgetType: 'chart', chartType: 'indicator', title: 'Total Revenue', dataOptions: { value: [ { column: measureFactory.sum(DM.Commerce.Revenue, 'Total Revenue').format('0,0$'), }, ], }, }, ]; return { title: 'Fabulous ECommerce Dashboard', widgets }; }, []); return ; }; export default CodeExample; ``` ![Dashboard with One Widget](../../img/dashboard-guides/generic-dashboard-one-chart-widget.png "Dashboard with One Widget") Let's take a closer look at `WidgetProps`: * `id` is needed for layout and widget options. * `widgetType` can be one of the four currently supported types: `chart`, `pivot`, `text`, and `plugin`. * `filters` is not provided as the dashboard does not have any filters yet. * `layoutOptions` helps to customize how `widgets` are laid out. If it is not provided, dashboard will use a simple vertical column layout by default. This isn't a very interesting dashboard. Let’s improve this in the next example. ## Add Dashboard Filters, Other Widgets, and Set Up Layout Below is the code for the same Sample Ecommerce dashboard created programatically. ##### React ```ts import { Dashboard, DashboardProps, IndicatorStyleOptions, LineStyleOptions, NumberFormatConfig, ScatterStyleOptions, StackableStyleOptions, WidgetProps, WidgetsPanelColumnLayout, } from '@sisense/sdk-ui'; import * as DM from './sample-ecommerce'; import { useMemo } from 'react'; import { Filter, filterFactory, measureFactory } from '@sisense/sdk-data'; const seriesToColorMap = { Female: '#00cee6', Male: '#9b9bd7', Unspecified: '#6eda55', }; export const getIndicatorStyleOptions = ( title: string, secondaryTitle = '', ): IndicatorStyleOptions => { return { indicatorComponents: { title: { shouldBeShown: true, text: title, }, secondaryTitle: { text: secondaryTitle, }, ticks: { shouldBeShown: true, }, labels: { shouldBeShown: true, }, }, subtype: 'indicator/gauge', skin: 1, }; }; const scatterStyleOptions: ScatterStyleOptions = { xAxis: { logarithmic: true, }, yAxis: { logarithmic: true, }, height: 454, }; const barStyleOptions: StackableStyleOptions = { subtype: 'bar/stacked', height: 454, }; const numberFormat: NumberFormatConfig = { name: 'Numbers', decimalScale: 2, trillion: true, billion: true, million: true, kilo: true, thousandSeparator: true, prefix: false, symbol: '$', }; const lineChartStyleOptions: LineStyleOptions = { subtype: 'line/spline', lineWidth: { width: 'bold' }, yAxis: { title: { enabled: true, text: 'SALES' }, }, y2Axis: { title: { enabled: true, text: 'QUANTITY' }, }, markers: { enabled: true, fill: 'hollow', }, height: 454, }; const CodeExample = () => { // DashboardProps is a set of properties for the Dashboard component const dashboardProps: DashboardProps = useMemo(() => { const widgets: WidgetProps[] = [ { id: 'widget-1', widgetType: 'chart', chartType: 'indicator', title: 'Total Revenue', dataOptions: { value: [ { column: DM.Measures.SumRevenue, numberFormatConfig: numberFormat, }, ], secondary: [], min: [measureFactory.constant(0)], max: [measureFactory.constant(125000000)], }, styleOptions: getIndicatorStyleOptions('Total Revenue'), }, { id: 'widget-2', widgetType: 'chart', chartType: 'indicator', title: 'Total Units Sold', dataOptions: { value: [DM.Measures.Quantity], secondary: [], min: [measureFactory.constant(0)], max: [measureFactory.constant(250000)], }, styleOptions: getIndicatorStyleOptions('Total Units Sold'), }, { id: 'widget-3', widgetType: 'chart', chartType: 'indicator', title: 'Total Sales', dataOptions: { value: [measureFactory.countDistinct(DM.Commerce.VisitID)], secondary: [], min: [measureFactory.constant(0)], max: [measureFactory.constant(100000)], }, styleOptions: getIndicatorStyleOptions('Total Sales'), }, { id: 'widget-4', widgetType: 'chart', chartType: 'indicator', title: 'Total Brands', dataOptions: { value: [measureFactory.countDistinct(DM.Brand.BrandID)], secondary: [], min: [measureFactory.constant(0)], max: [measureFactory.constant(2500)], }, styleOptions: getIndicatorStyleOptions('Total Brands'), }, { id: 'widget-5', widgetType: 'chart', chartType: 'line', title: 'REVENUE vs.UNITS SOLD', dataOptions: { category: [ { column: DM.Commerce.Date.Months, dateFormat: 'yy-MM', }, ], value: [ DM.Measures.SumRevenue, { column: DM.Measures.Quantity, showOnRightAxis: true, chartType: 'column', }, ], breakBy: [], }, styleOptions: lineChartStyleOptions, }, { id: 'widget-6', widgetType: 'chart', chartType: 'pie', title: 'GENDER BREAKDOWN', dataOptions: { category: [DM.Commerce.Gender], value: [DM.Measures.SumRevenue], }, filters: [filterFactory.members(DM.Commerce.Gender, ['Male', 'Female'])], styleOptions: scatterStyleOptions, }, { id: 'widget-7', widgetType: 'chart', chartType: 'pie', title: 'AGE RANGE BREAKDOWN', dataOptions: { category: [DM.Commerce.AgeRange], value: [DM.Measures.SumRevenue], }, filters: [filterFactory.members(DM.Commerce.Gender, ['Male', 'Female'])], styleOptions: scatterStyleOptions, }, { id: 'widget-8', widgetType: 'chart', chartType: 'scatter', title: 'TOP CATEGORIES BY REVENUE, UNITS SOLD AND GENDER', dataOptions: { x: DM.Measures.SumRevenue, y: DM.Measures.Quantity, breakByPoint: DM.Category.Category, breakByColor: DM.Commerce.Gender, size: DM.Measures.SumCost, seriesToColorMap, }, filters: [ filterFactory.members(DM.Commerce.Gender, ['Male', 'Female']), filterFactory.topRanking(DM.Category.Category, DM.Measures.SumRevenue, 10), ], styleOptions: scatterStyleOptions, }, { id: 'widget-9', widgetType: 'chart', chartType: 'bar', title: 'TOP 3 CATEGORIES BY REVENUE AND AGE', dataOptions: { category: [DM.Commerce.AgeRange], value: [DM.Measures.SumRevenue], breakBy: [DM.Category.Category], }, filters: [filterFactory.topRanking(DM.Category.Category, DM.Measures.SumRevenue, 3)], styleOptions: barStyleOptions, }, ]; const filters: Filter[] = [ filterFactory.members(DM.Commerce.Date.Years, ['2013-01-01T00:00:00']), filterFactory.members(DM.Country.Country, []), filterFactory.greaterThan(DM.Commerce.Revenue, 0), ]; const widgetsPanelLayout: WidgetsPanelColumnLayout = { columns: [ { widthPercentage: 20, rows: [ { cells: [{ widthPercentage: 100, widgetId: 'widget-1' }] }, { cells: [{ widthPercentage: 100, widgetId: 'widget-2' }] }, { cells: [{ widthPercentage: 100, widgetId: 'widget-3' }] }, { cells: [{ widthPercentage: 100, widgetId: 'widget-4' }] }, ], }, { widthPercentage: 40, rows: [ { cells: [{ widthPercentage: 100, widgetId: 'widget-5' }] }, { cells: [ { widthPercentage: 50, widgetId: 'widget-6' }, { widthPercentage: 50, widgetId: 'widget-7' }, ], }, ], }, { widthPercentage: 40, rows: [ { cells: [{ widthPercentage: 100, widgetId: 'widget-8' }] }, { cells: [{ widthPercentage: 100, widgetId: 'widget-9' }] }, ], }, ], }; return { title: 'Fabulous ECommerce Dashboard', widgets, filters, layoutOptions: { widgetsPanel: widgetsPanelLayout }, }; }, []); return ; }; export default CodeExample; ``` ![Sample ECommerce Dashboard In Code](../../img/dashboard-guides/generic-dashboard-ecommerce.png "Sample ECommerce Dashboard In Code") The dashboard is fully interactive. Cross filtering and drilldown work as expected. At first glance, this code may seem like a significant leap from the previous example. However, upon closer inspection, you'll notice there's no advanced coding or complex algorithms involved. It's simply a standard configuration of dashboard elements: 9 widgets, 3 dashboard filters, and a widget layout.\ The Compose SDK handles all the internal wiring and interactions for you. ## Learn More In this section you learned how to compose a dashboard fully in code using the `Dashboard` component. To deepen your understanding, check out [the API Doc](../../modules/index.md) and [Compose SDK Playground](https://www.sisense.com/developers/playground/?example=fusion-assets%2Ffusion-dashboard). --- --- url: 'https://developer.sisense.com/guides/sdk/guides/data-model.md' --- # Data Model When using Compose SDK with data from a Sisense instance, you can use a TypeScript representation of your data model to easily reference the entities in your model. Although it’s not strictly necessary to use a TypeScript data model representation, using one will save you time and help minimize errors in your code. TypeScript representations of Sisense data models are built using functionality provided in the `sdk-data` module of Compose SDK. Typically, these data model representations are built using the Compose SDK CLI tool. The CLI tool reads a specified data model from your Sisense instance and uses the functionality of the `sdk-data` module to build your data model using TypeScript. The end result is a TypeScript file that exports your data model’s structure. You can then import the data model from that file and use it in your code. Theoretically, you can manually create a TypeScript representation of a Sisense data model using the functions exposed in `sdk-data`, but the need to do so is exceptionally rare. You may, however, want to edit the generated model to add sort on an attribute for example. ## Generating a Data Model To generate a TypeScript representation of a Sisense data model use the Compose SDK CLI tool’s `get-data-model` command. The command takes the following parameters: * `--output`: Relative path in which to create the data model TypeScript file * `--dataSource`: Name of the data model in the Sisense instance * `--url`: URL of the Sisense instance * `--username`: (Optional) A username in the Sisense instance * `--wat`: (Optional) Web access token to use for authentication * `--token`: (Optional) An API token to use for authentication For example, you can create a TypeScript representation of the Sample ECommerce data model like this: ```sh npx @sisense/sdk-cli@latest get-data-model --username --output src/sample-ecommerce.ts --dataSource "Sample ECommerce" --url https://myinstanceurl.com ``` If prompted, enter your password to authenticate and generate the data model representation. ## Data Model Contents A TypeScript representation of a Sisense data model includes a data source name, type information and information about the dimensions and attributes of your data model. Each table in your data model is represented by an exported dimension, which itself is made up of multiple attributes, representing the fields in the table. For example, consider the Sample Ecommerce data model: ![Ecommerce data model](../img/data-model/ecommerce-model.png "Ecommerce data model") This model generates the following TypeScript representation. Note that the `Brand` dimension represents the Brand table and the `Brand` and `BrandID` attributes represent fields in the Brand table with those names. Each attribute is created using `name`, `type`, and `expression` values. ```ts import { Dimension, DateDimension, Attribute, createAttribute, createDateDimension, createDimension, } from '@sisense/sdk-data'; export const DataSource = 'Sample ECommerce'; interface BrandDimension extends Dimension { Brand: Attribute; BrandID: Attribute; } export const Brand = createDimension({ name: 'Brand', Brand: createAttribute({ name: 'Brand', type: 'text-attribute', expression: '[Brand.Brand]', }), BrandID: createAttribute({ name: 'BrandID', type: 'numeric-attribute', expression: '[Brand.Brand ID]', }), }) as BrandDimension; // Additional interface definitions and dimensions with attributes // for the Category, Commerce, and Country tables ``` ## Using a Data Model Once you’ve created a TypeScript data model representation, you can import and use it in code that refers to that data model. You can use it when creating visualizations, such as charts and tables, or when performing queries. For example here is a chart that uses an imported data model representation to define its `dataset` and `dataOptions`. ```ts import * as DM from '../sample-ecommerce'; import { measureFactory } from '@sisense/sdk-data'; //... ``` And here is an example of performing a query using a data model representation, including sorting one of the attributes: ```ts import * as DM from '../sample-ecommerce'; import { Sort, measureFactory } from '@sisense/sdk-data'; //... const { data, isLoading, isError } = useExecuteQuery({ dataSource: DM.DataSource, dimensions: [DM.Commerce.AgeRange.sort(Sort.Descending)], measures: [measureFactory.sum(DM.Commerce.Revenue)], }); ``` --- --- url: 'https://developer.sisense.com/guides/sdk/guides/drilldown/index.md' --- # Drilldown Drilling down on a chart allows your users to see more detailed data by selecting a dimension to drill down on. This enables them to examine complex datasets in a more manageable and intuitive way. With Compose SDK you can create a drilldown experience using the charts in the `sdk-ui-*` modules: ![Drilldown with Compose SDK chart](../../img/drilldown-guide/csdk-context-menu.png "Drilldown with Compose SDK chart") You can also create a drilldown experience using third-party charts: ![Drilldown with third-party chart](../../img/drilldown-guide/plotly-csdk-context-menu.png "Drilldown with third party chart") With both types of charts, you can use the built-in drilldown-related components to show drilldown context menus and drilldown breadcrumbs: ![Drilldown with Compose SDK components](../../img/drilldown-guide/csdk-components.png "Drilldown with Compose SDK components") Or you can provide your own custom context menu and breadcrumbs components: ![Drilldown with custom components](../../img/drilldown-guide/custom-components.png "Drilldown with custom components") If you're new to drilldown charts, start by learning how to create a [simple drilldown chart](./guide-1-simple-drilldown.md). --- --- url: >- https://developer.sisense.com/guides/sdk/guides/drilldown/guide-1-simple-drilldown.md --- # 1 | Simple Drilldown Chart To create a drilldown experience on a Compose SDK chart, you need to wrap the chart inside a ``. The `` component allows you to specify which dimensions can be used to drill down on and adds the following functionality to your chart: * A context menu for initiating drilldown actions * Breadcrumbs that allow for navigating the drilldown hierarchy and clearing the current drilldown selection * Filters for the drilldown operation ## Chart To demonstrate how to add a drilldown experience to a Compose SDK chart, let's start with this simple column chart: ![Column chart](../../img/drilldown-guide/plain-chart.png "Column chart") ```ts ``` ## Drilldown Widget Once you have a chart that you want to add a drilldown experience to, you need to wrap the chart in a `` and provide it with: * `initialDimension`: The initial dimension that the wrapped chart will show * `drilldownDimensions`: List of drilldown options that users can choose to drill down on The widget then provides the chart with: * Drilldown results in the form of the `drilldownDimension` and `drilldownFilters` to apply to the chart * Functions for handling the selection of data points (`onDataPointsSelected`) and the showing of the drilldown context menu (`onContextMenu`) For example, wrapping a chart in a `` may look something like this: ```ts {({ drilldownDimension, drilldownFilters, onDataPointsSelected, onContextMenu }) => ( )} ``` Here you can see that the `initialDimension` is set to be **Age Range**, as it was in the chart before adding drilldown functionality. Also, the `drilldownDimensions` is set to a list of the drilldown options. In this case, users can drill down on the **Gender**, **Condition**, and **Category** dimensions. ## Apply to Chart After wrapping the chart in a ``, you need to pass the control to the wrapper so the chart can be modified based on drilldown actions. That means you need to: * Switch out the chart's static `category` for the `drilldownDimension` from the `` * Add a `filters` property to the chart with the value being the `drilldownFilters` from the `` * Use the `onDataPointsSelected` and `onContextMenu` functions from the `` to provide the wrapper with the selected data and context menu position (typically, this is done using chart event callbacks, such as `onDataPointsSelected` and `onDataPointClick`) That should leave you with code that looks something like this: ```ts {({ drilldownFilters, drilldownDimension, onDataPointsSelected, onContextMenu }) => ( { onDataPointsSelected(points, nativeEvent); onContextMenu({ left: nativeEvent.clientX, top: nativeEvent.clientY, }); }} onDataPointClick={(point: DataPoint, nativeEvent: MouseEvent) => { onDataPointsSelected([point], nativeEvent); onContextMenu({ left: nativeEvent.clientX, top: nativeEvent.clientY, }); }} /> )} ``` ## Results At this point, you have a chart that you can drill down on. You can start the drilldown process by either clicking a a data point or selecting a number of data points. For example, if you click on the 35-44 age range, you get a context menu with the drilldown category options you set in your code. ![Drilldown context menu](../../img/drilldown-guide/csdk-context-menu.png "Drilldown context menu") If you then click on a drilldown category, such as **Condition**, the chart updates accordingly. Note the breadcrumbs above the chart that indicate the current drilldown status. ![Drill down condition](../../img/drilldown-guide/drilldown-condition.png "Drill down condition") You can then continue to drill down. For example, you can click on the **Used** column and drill down on **Category**. ![Drill down again](../../img/drilldown-guide/csdk-components.png "Drill down again") As you drill down, the breadcrumbs keep track of the actions you've performed. You can use the breadcrumbs to go back up some of your drill hierarchy or to clear all the drilling down to return to the original chart. ## Next Up In this section you learned how to create a drilldown experience using Compose SDK components. In the next section, you'll see how to customize the look and feel of a drilldown chart by providing custom context menu. Go to the [next lesson](./guide-2-custom-context-menu.md). ## Full Code For your convenience, here is the full code for the simple drilldown chart: ```ts import * as DM from '../sample-ecommerce'; import { Chart, DataPoint, DrilldownWidget } from '@sisense/sdk-ui'; import { measureFactory } from '@sisense/sdk-data'; export const DrilldownChart = () => { return ( {({ drilldownDimension, drilldownFilters, onDataPointsSelected, onContextMenu }) => ( { onDataPointsSelected(points, nativeEvent); onContextMenu({ left: nativeEvent.clientX, top: nativeEvent.clientY, }); }} onDataPointClick={(point: DataPoint, nativeEvent: MouseEvent) => { onDataPointsSelected([point], nativeEvent); onContextMenu({ left: nativeEvent.clientX, top: nativeEvent.clientY, }); }} styleOptions={{ width: 750 }} /> )} ); }; ``` --- --- url: >- https://developer.sisense.com/guides/sdk/guides/drilldown/guide-2-custom-context-menu.md --- # 2 | Custom Context Menu In this section, you'll learn how to customize the look and feel of a drilldown chart by providing a custom context menu. You can use any components of you choose as the basis for a custom context menu. In this guide, we use the [Material UI Menu component](https://mui.com/material-ui/react-menu/) as the basis for the custom context menu. ## Props A context menu has the following properties: * `position`: The position at which to display the context menu * `itemsSections`: The items for the context menu, organized by section (more on this below) * `children`: Additional content, if there is any, to be displayed at the bottom of the context menu * `closeContextMenu`: Function to run when the context menu is closed So the first step in creating a custom context menu is to create a component with these properties: ```ts export const CustomContextMenu = ({ position, itemSections, children, closeContextMenu }: ContextMenuProps) => { // Component code goes here }; ``` ## Menu Component Next, you can start to apply some of these properties in your component code: * Use `position` to determine if the context menu is open and to place it in the correct location * Use `closeContextMenu` to determine what happens when the context menu is closed * Place any children that are passed through to the component at the bottom of your context menu Using the `` component, your code would look something like this: ```ts const open = !!position; return ( {/* Menu items go here */} {children} ); ``` ## Menu Content Finally, you need to add the drilldown options to the context menu. Do that by applying the information in `itemSections` to the contents of the `` component. The `itemSections` array contains information about the current drilldown dimension and the remaining possible dimensions to drill down on. For example, consider the chart we discussed in the previous section where the `initialDimension` is **Age Range** and the `drilldownDimensions` are **Gender**, **Condition**, and **Category**. Before drilling down on the chart, the `itemSections` array contains the following: ```ts [ { sectionTitle: 'AgeRange', }, { sectionTitle: 'Drill', items: [ { caption: 'Gender' }, { caption: 'Condition' }, { caption: 'Category' } ], }, ]; ``` Then after drilling down on **Condition**, the `itemSections` array contains the following: ```ts [ { sectionTitle: 'Condition', }, { sectionTitle: 'Drill', items: [ { caption: 'Gender' }, { caption: 'Category' } ], }, ]; ``` Now that you understand the contents of the `itemSections` array, you can decide how you want to use it to populate your menu options. You can of course choose to do this however you like. Here, we ignore the current drilldown dimension and just create a menu item for each remaining drill category. Each menu item contains an icon and the caption of the drilldown dimension. When the item is clicked, the context menu is closed and the `onClick` function from the item object is called to perform the drill down. ```ts {!!itemSections && !!(itemSections as Array<{ items: Array }>)[1].items.length && itemSections?.map(({ items }) => items?.map((item) => ( { closeContextMenu(); item.onClick?.(); }} > {item.caption} )) ) } ``` If there are no remaining drilldown dimensions, you may want to create a single menu item alerting the user that they cannot drill down any further. ```ts {(!itemSections || !(itemSections as Array<{ items: Array }>)[1].items.length) && ( {'Cannot drill down any further'} )} ``` ## Apply Once you have a custom context menu component, you need to apply it to your chart. All you need to do is to add a `config` property to the `` that wraps the chart. Within the `config` object, set the `contextMenuComponent` property to the component you created. ```ts ``` ## Results At this point, your custom context menu is ready for action. When you select a data point in your chart, you should see a context menu that looks like this: ![Custom context menu](../../img/drilldown-guide/custom-context-menu.png "Custom context menu") ## Next Up In this section you learned how to create a custom context menu. In the next section, you'll continue to customize the drilldown experience by creating a custom breadcrumbs component. Go to the [next lesson](./guide-3-custom-breadcrumbs.md). ## Full Code For your convenience, here is the full code for our custom context menu component: ```ts import Menu from '@mui/material/Menu'; import ListItemText from '@mui/material/ListItemText'; import ListItemIcon from '@mui/material/ListItemIcon'; import MenuItem from '@mui/material/MenuItem'; import MoveDown from '@mui/icons-material/MoveDown'; import { ContextMenuProps } from '@sisense/sdk-ui'; export const CustomContextMenu = ({ position, itemSections, children, closeContextMenu, }: ContextMenuProps) => { const open = !!position; return ( {!!itemSections && !!(itemSections as Array<{ items: Array }>)[1].items.length && itemSections?.map(({ items }) => items?.map((item) => ( { closeContextMenu(); item.onClick?.(); }} > {item.caption} )) )} {(!itemSections || !(itemSections as Array<{ items: Array }>)[1].items.length) && ( {'Cannot drill down any further'} )} {children} ); }; ``` --- --- url: >- https://developer.sisense.com/guides/sdk/guides/drilldown/guide-3-custom-breadcrumbs.md --- # 3 | Custom Breadcrumbs In this section, you'll learn how to customize the look and feel of our drilldown chart by providing custom breadcrumbs. Similar to the custom context menu, when building a custom breadcrumbs component, you can use whatever components you choose. In this guide, we use the [Material UI Breadcrumbs component](https://mui.com/material-ui/api/breadcrumbs/) as the basis for our custom breadcrumbs. ## Props A breadcrumbs component has the following properties: * `currentDimension`: The current drilldown dimension * `filtersDisplayValues`: List of applied drilldown filters (more on this below) * `clearDrilldownSelections`: Function to run when the clear button is clicked * `sliceDrilldownSelections`: Function to run when a breadcrumb is clicked So the first step in creating custom breadcrumbs is to create a component with these properties: ```ts export const CustomBreadCrumbs = ({ currentDimension, filtersDisplayValues, clearDrilldownSelections, sliceDrilldownSelections, }: DrilldownBreadcrumbsProps) => { // Component code goes here }; ``` ## Breadcrumbs Component The breadcrumb component we build in this guide uses [Material UI Chip](https://mui.com/material-ui/api/chip/) components as the individual breadcrumbs. Again, you can choose to use another component if you want. Here, whenever breadcrumbs are shown, there will be two chips on either end of the breadcrumbs. * The first chip contains a button to clear the drilldown and return the chart to its original state * The last chip displays the current drilldown dimension For example, here you can see the clear button in the first chip and the current drilldown dimension, **Category** as the last chip: ![Special breadcrumb chips](../../img/drilldown-guide/breadcrumb-chips.png "Special breadcrumb chips") To create these chips, you can start to apply some of the properties mentioned above in your component code. * Use `filterDisplayValues` to define which breadcrumbs to show * Use `clearDrilldownSelections` to return the chart to its original state when the clear button in the first chip is clicked * Use `currentDimension` to build the last drilldown chip ```ts if (!filtersDisplayValues.length) return null; return ( } /> {/* Code for additional chips goes here */} } /> ); ``` ## Drilldown Chips Now you can fill in the remaining chips for the current drilldown hierarchy. You know how many chips to create based on the number of elements in the `filterDisplayValue` array. Each element in the array is an array itself of a drilldown level. When a single data point is selected for drilling down, the drilldown level is an array with a single element. If more than one data point is selected for drilling down, the drilldown level is an array containing all the selected points. For example, consider the chart we discussed in previous sections where the `initialDimension` is **Age range** and the `drilldownDimensions` are **Gender**, **Condition**, and **Category**. If a user initially selects the **25-34** and **35-44** age ranges, drills down on category, and the selects the **New** category to drill down by age, the `filterDisplayValue` array will look like this: ```ts [['25-34', '35-44'], ['New']]; ``` Here two similar types of chips are used to display the values in the `filtersDisplayValue` array. In both types of chips, a value from `filtersDisplayValue` is used to show which dimensions have been selected for drilling down. If the value contains multiple category selections, you can choose how to display those. Here we choose to separate them with a pipe character (`|`). The difference between the two types of chips is whether they are clickable or not. Clickable chips allow users to go back up the drilldown hierarchy. You go back up the hierarchy using the `sliceDrilldownSelections` callback. Here you can see the two types of chips. The clickable chips are blue and the others are gray. Clickable chips where it makes sense to move back up the drilldown hierarchy. Non-clickable chips are used in all other cases. ![Breadcrumb chip types](../../img/drilldown-guide/breadcrumb-chips.png "Breadcrumb chip types") To create these chips, check the location of the current chip in `filtersDisplayValues` and then build the appropriate type of chip: ```ts { filtersDisplayValues.map((displayValue, i) => { const isClickable = i < filtersDisplayValues.length - 1; return isClickable ? ( sliceDrilldownSelections(i + 1)} color="primary" icon={} /> ) : ( } /> ); }); } ``` ## Apply Now that you've created a custom breadcrumbs component, you can apply it to a chart. All you need to do is to add a property in the `` `config`. Within the `config` object, set the `breadcrumbsMenuComponent` property to the component you created. In the code below, we also choose to detach the breadcrumbs from the chart component so that we can place it wherever we want. To do so: * Set `isBreadcrumbsDetached` to `true` * Add `breadcrumbsComponent` to the destructuring of the `` return value * Place the `breadcrumbsComponent` where you want it to display For example, the following code shows the breadcrumbs component right below the chart: ```ts {({ drilldownFilters, drilldownDimension, onDataPointsSelected, onContextMenu, breadcrumbsComponent, }) => ( <> {breadcrumbsComponent} )} ); } ``` Note that you can also detach the default breadcrumbs component and place it wherever you want using the same process. ## Results At this point, your breadcrumbs component is ready for action. When you drill down on your chart, you should see a breadcrumbs component that looks like this: ![Custom breadcrumbs](../../img/drilldown-guide/custom-breadcrumbs.png "Custom breadcrumbs") ## Next Up In this section you learned how to create a breadcrumbs component. In the next section, you'll see how to create a drilldown experience on a third party chart. Go to the [next lesson](./guide-4-third-party-chart.md). ## Full Code For your convenience, here is the full code for our custom breadcrumbs component: ```ts import { Breadcrumbs, Chip } from '@mui/material'; import { DrilldownBreadcrumbsProps } from '@sisense/sdk-ui'; import CancelIcon from '@mui/icons-material/Cancel'; import MoveDown from '@mui/icons-material/MoveDown'; import { CollectionsBookmark } from '@mui/icons-material'; export const CustomBreadCrumbs = ({ currentDimension, filtersDisplayValues, clearDrilldownSelections, sliceDrilldownSelections, }: DrilldownBreadcrumbsProps) => { if (!filtersDisplayValues.length) return null; return ( } /> {filtersDisplayValues.map((displayValue, i) => { const isClickable = i < filtersDisplayValues.length - 1; return isClickable ? ( sliceDrilldownSelections(i + 1)} color="primary" icon={} /> ) : ( } /> ); })} } /> ); }; ``` --- --- url: >- https://developer.sisense.com/guides/sdk/guides/drilldown/guide-4-third-party-chart.md --- # 4 | Third Party Drilldown Chart In this section, you'll how to drill down when using a 3rd party charting library. In this guide, we'll switch out our Compose SDK chart with a chart from [Plotly.js](https://plotly.com/javascript/). ## Plotly Wrapper To get started, you need to build a wrapper around a Plotly chart component so that you can populate it with data retrieved by Compose SDK. :::tip Learn more Here, we briefly discuss this process. To learn more about using a third party chart with Compose SDK, see the [External Charts](../charts/guide-external-charts.md) section of the [Charts Guide](../charts/). ::: In addition to being able to handle the data we provide it with, the wrapper needs to be able to handle data point selection and display a context menu when needed. So the first step in creating the wrapper is to create a component with the following properties: ```ts type Props = { rawData: QueryResultData, onDataPointsSelected: DataPointsEventHandler, onContextMenu: (menuPosition: MenuPosition) => void, }; export const PlotlyBarChart: React.FC = ({ rawData, onDataPointsSelected, onContextMenu }) => { // Chart code goes here }; ``` Next, you to keep track of the data points selected for drilling down. To do so, create a state variable: ```ts const [selectedCategories, setSelectedCategories] = useState([]); ``` And you also need to store the data for the current state of the drilldown chart as well as the data converted to the format we need for the Plotly chart. To do so create some memoized variables: ```ts const data = useMemo( () => rawData.rows.map(([category, value]) => ({ category: category.data as string, value: value.data as string | number, })), [rawData.rows] ); const trace = useMemo(() => { setSelectedCategories([]); return generateTrace(data, rawData.columns[1].name); }, [data, rawData.columns]); ``` Note that the `generateTrace()` function is used to reformat the data for the Plotly chart. That function looks like this: ```ts const generateTrace = ( data: { [key: string]: Datum }[], name: string ): Partial => ({ type: 'bar', x: data.map((d) => d.category), y: data.map((d) => d.value), name: name, }); ``` Next, you need to handle clicks to open the drilldown context menu. To do so, use the `useEffect()` hook to create an event listener that uses the `onContextMenu` function from the component's props: ```ts useEffect(() => { const handleContextMenu = (event: MouseEvent) => { event.preventDefault(); if (!selectedCategories.length) return; onContextMenu({ left: event.clientX, top: event.clientY }); }; document.addEventListener('contextmenu', handleContextMenu); return () => { document.removeEventListener('contextmenu', handleContextMenu); }; }, [selectedCategories, onDataPointsSelected, onContextMenu]); ``` After that, you need to handle user selection of data points in the chart. To do so, use the `useCallback()` hook to set the `selectedCategories` state variable: ```ts const handleSelection = useCallback( (event: PlotSelectionEvent) => { if (event.points.length) { const internalSelectedCategories = event.points.map((point) => { const clickedIndex = point.pointNumber; return data[clickedIndex].category; }); setTimeout( () => setSelectedCategories(internalSelectedCategories), 250 ); setTimeout( () => onDataPointsSelected( internalSelectedCategories.map((category) => ({ value: undefined, categoryValue: category, categoryDisplayValue: category, seriesValue: undefined, })), event as unknown as MouseEvent ), 250 ); } }, [data, onDataPointsSelected] ); ``` Finally, with all that done, you can apply it to the Plotly `` component: ```ts return ( ); ``` When you put all the above code together, it looks like this: ```ts import { useCallback, useEffect, useMemo, useState } from 'react'; import { QueryResultData } from '@sisense/sdk-data'; import Plot from 'react-plotly.js'; import { Datum, PlotSelectionEvent } from 'plotly.js'; import { DataPointsEventHandler, MenuPosition } from '@sisense/sdk-ui'; type Props = { rawData: QueryResultData; onDataPointsSelected: DataPointsEventHandler; onContextMenu: (menuPosition: MenuPosition) => void; }; const generateTrace = ( data: { [key: string]: Datum }[], name: string ): Partial => ({ type: 'bar', x: data.map((d) => d.category), y: data.map((d) => d.value), name: name, }); export const PlotlyBarChart: React.FC = ({ rawData, onDataPointsSelected, onContextMenu, }) => { const [selectedCategories, setSelectedCategories] = useState([]); const data = useMemo( () => rawData.rows.map(([category, value]) => ({ category: category.data as string, value: value.data as string | number, })), [rawData.rows] ); const trace = useMemo(() => { setSelectedCategories([]); return generateTrace(data, rawData.columns[1].name); }, [data, rawData.columns]); useEffect(() => { const handleContextMenu = (event: MouseEvent) => { event.preventDefault(); if (!selectedCategories.length) return; onContextMenu({ left: event.clientX, top: event.clientY }); }; document.addEventListener('contextmenu', handleContextMenu); return () => { document.removeEventListener('contextmenu', handleContextMenu); }; }, [selectedCategories, onDataPointsSelected, onContextMenu]); const handleSelection = useCallback( (event: PlotSelectionEvent) => { if (event.points.length) { const internalSelectedCategories = event.points.map((point) => { const clickedIndex = point.pointNumber; return data[clickedIndex].category; }); setTimeout( () => setSelectedCategories(internalSelectedCategories), 250 ); setTimeout( () => onDataPointsSelected( internalSelectedCategories.map((category) => ({ value: undefined, categoryValue: category, categoryDisplayValue: category, seriesValue: undefined, })), event as unknown as MouseEvent ), 250 ); } }, [data, onDataPointsSelected] ); return ( ); }; ``` ## Apply Drilldown Widget Now that you have a Plotly chart properly wrapped, you can further wrap it in a `` component to add the drilldown functionality. To do so, you work exactly the same way as you would when wrapping a Compose SDK chart. Here, since you're already using a custom chart, we'll also use the custom context menu and breadcrumbs from the previous sections. But if you want, you can use the Compose SDK components with your third party chart instead. ```ts export const PlotlyDrilldownChart = () => { return ( {/* Query and chart code go here */} ); }; ``` Next, you need to execute a query to get the data for the third party chart. To do so, use the `` component. The properties of `` look a lot like the properties of the Compose SDK `` from the beginning of this guide since they are both doing the same things under the hood to retrieve data. Once again, in the `` we use the `drilldownDimension` and `drilldownFilters` to define which data we want to query for: ```ts {({ data }) => { if (data) { return { /* Chart code goes here */ }; } }} ``` All that's left to do now is to pass the retrieved data to your wrapped third party chart: ```ts ``` When you put all the above code together, it looks like this: ```ts import * as DM from '../sample-ecommerce'; import { DrilldownWidget, ExecuteQuery } from '@sisense/sdk-ui'; import { measureFactory } from '@sisense/sdk-data'; import { CustomContextMenu } from './CustomContextMenu'; import { CustomBreadCrumbs } from './CustomBreadCrumbs'; import { PlotlyBarChart } from './PlotlyBarChart'; export const PlotlyDrilldownChart = () => { return ( {({ drilldownFilters, drilldownDimension, onDataPointsSelected, onContextMenu, breadcrumbsComponent }) => ( {({ data }) => { if (data) { return ( <> {breadcrumbsComponent} ); } }} )} ); }; ``` ## Results At this point, your custom third party chart is ready for action. When you select data points and drill down, your chart should look something like this: ![Third party drilldown chart](../../img/drilldown-guide/third-party.png "Third party drilldown chart") --- --- url: 'https://developer.sisense.com/guides/sdk/guides/formatting.md' --- # Number & Date Formatting This guide demonstrates how to customize the formatting of numbers and dates in your [Compose SDK charts](./charts/guide-compose-sdk-charts.md). ## Number Formatting You can format numbers in your chart using the `numberFormatConfig` property of `StyledColumn` or `StyledMeasureColumn` objects. There are 3 main ways to format numbers. Specify the type of formatting using the `name` property with one of the following values: * `'Numbers'`: Format as regular numbers that are not currency or percentages * `'Currency'`: Format as currency * `'Percent'`: Format as a percentage For each of these format types, you can customize the formatting using the following properties: * `thousandsSeparator` (boolean): Whether to show a thousands separator. Defaults to `true`. * `decimalScale` (number): Number of decimal places to show. Depending on which type of formatting you use, you can also customize the formatting using the properties described below. ### Numbers Use the following properties to customize the formatting of regular numbers: * `trillion` (boolean): Whether to abbreviate numbers greater than or equal one trillion. Defaults to `true`. * `billion` (boolean): Whether to abbreviate numbers greater than or equal one billion. Defaults to `true`. * `million` (boolean): Whether to abbreviate numbers greater than or equal one million. Defaults to `true`. * `kilo` (boolean): Whether to abbreviate numbers greater than or equal one thousand. Defaults to `true`. #### Numbers Example ![Chart with Numbers formatting](../img/chart-guides/formatting-numbers.png "Chart with Numbers formatting") ```tsx ``` ### Currency Use the following properties to customize the formatting of numbers representing currency: * `prefix` (boolean): Whether to show the `symbol` before the number (`true`) or after the number (`false`). Defaults to `true`. * `symbol` (string): Symbol to show before or after the number, depending on the `prefix` value. Defaults to `'$'`. * `trillion` (boolean): Whether to abbreviate numbers greater than or equal one trillion. Defaults to `true`. * `billion` (boolean): Whether to abbreviate numbers greater than or equal one billion. Defaults to `true`. * `million` (boolean): Whether to abbreviate numbers greater than or equal one million. Defaults to `true`. * `kilo` (boolean): Whether to abbreviate numbers greater than or equal one thousand. Defaults to `true`. #### Currency Example ![Chart with currency formatting](../img/chart-guides/formatting-currency.png "Chart with currency formatting") ```tsx ``` ### Percent The percent type doesn't have any additional properties for further customization. #### Percent Example ![Chart with percent formatting](../img/chart-guides/formatting-percent.png "Chart with percent formatting") ```tsx ``` ## Date Formatting You can format dates in your chart categories using the `dateFormat` property on a [`StyledColumn`](../modules/sdk-ui/interfaces/interface.StyledColumn.md). Provide a format string using [date-fns `format` tokens](https://date-fns.org/v2.29.3/docs/format) (for example `yyyy`, `MM`, `dd`, `HH:mm:ss`, or `qqq` and `yyyy` as in the example below), not the ECMAScript Date Time String Format. ### Date Example ![Chart with date formatting](../img/chart-guides/formatting-dates.png "Chart with date formatting") ```tsx ``` --- --- url: 'https://developer.sisense.com/guides/sdk/guides/internationalization.md' --- # Internationalization with Compose SDK The Compose SDK utilizes the [i18next](https://www.i18next.com/) internationalization framework, making it straightforward to load your own translations. ## Changing the language To facilitate language changes, the `AppConfig` of `SisenseContextProvider` includes a `translationConfig` property where you can easily set your desired language. For example, to set the language to **French**, use the following code: ``` ``` ## Loading Additional Translations By default, the Compose SDK offers a limited number of translation resources. You can utilize `translationConfig` to load additional translation resources into the internationalization framework. ### Pre-built translation submodules (`@sisense/sdk-ui/translations/*`) The `@sisense/sdk-ui` package exposes pre-built translation bundles as subpath exports. Each submodule exports an array of translation objects (language, namespace, resources) ready to use in `customTranslations`. | Submodule | Code | Language | |-----------|------|----------| | `@sisense/sdk-ui/translations/en-us` | `en-US` | English (US) | | `@sisense/sdk-ui/translations/de-de` | `de-DE` | German | | `@sisense/sdk-ui/translations/es-ar` | `es-AR` | Spanish (Argentina) | | `@sisense/sdk-ui/translations/es-es` | `es-ES` | Spanish (Spain) | | `@sisense/sdk-ui/translations/fr-fr` | `fr-FR` | French | | `@sisense/sdk-ui/translations/it-it` | `it-IT` | Italian | | `@sisense/sdk-ui/translations/ja-jp` | `ja-JP` | Japanese | | `@sisense/sdk-ui/translations/ko-kr` | `ko-KR` | Korean | | `@sisense/sdk-ui/translations/nl-nl` | `nl-NL` | Dutch | | `@sisense/sdk-ui/translations/pt-br` | `pt-BR` | Portuguese (Brazil) | | `@sisense/sdk-ui/translations/ru-ru` | `ru-RU` | Russian | | `@sisense/sdk-ui/translations/tr-tr` | `tr-TR` | Turkish | | `@sisense/sdk-ui/translations/zh-cn` | `zh-CN` | Chinese (Simplified) | **Example: using a pre-built translation** ```tsx import { SisenseContextProvider } from '@sisense/sdk-ui'; import sdkUiFrench from '@sisense/sdk-ui/translations/fr-fr'; {/* Your app */} ``` **Example: combining pre-built translations with custom overrides** ```tsx import { SisenseContextProvider, TranslationDictionary } from '@sisense/sdk-ui'; import sdkUiGerman from '@sisense/sdk-ui/translations/de-de'; const myOverrides: Partial = { chartNoData: 'Keine Daten in dieser Ansicht', }; {/* Your app */} ``` Later bundles for the same language and namespace extend or override earlier ones, so custom keys will take precedence when registered after the pre-built bundle. ### Preparing Translation Resources A translation resource consists of translation keys paired with their corresponding string values. This resource is typically structured as a nested object, making it easier to manage different translations. You can register multiple languages by creating separate translation resources for each language and then adding them to your configuration. **IMPORTANT:** Do not translate parts within double curly brackets (i.e., `{{chartType}}`), as these are placeholders for dynamic values that will be matched using the provided variable names. #### TranslationDictionary type Each package with translations provides a `TranslationDictionary` type listing all keys used within that package. Use this type to ensure your custom translation includes all relevant keys. **Example: complete translation for the `sdk-ui` package** ``` import { TranslationDictionary } from '@sisense/sdk-ui'; const customTranslationResources: TranslationDictionary = { ... }; ``` Note that specifying all translation keys is not required; any keys you do not provide will default to English. Using the `TranslationDictionary` type as a Partial can help prevent typos in your custom translation. #### Example: Translation Resources Let’s create translation resources for some fields in `sdk-ui` in **French**, and one error message in `sdk-data` in **Spanish**: ``` import { TranslationDictionary } from '@sisense/sdk-ui'; const frenchTranslationResources: Partial = { errors: { invalidFilterType: 'Type de filtre invalide', }, chartNoData: 'Aucun résultat' }; ``` ``` import { TranslationDictionary } from '@sisense/sdk-data'; const spanishTranslationResources: Partial = { errors: { measure: { unsupportedType: 'Tipo de medida no compatible', }, }, }; ``` As these files may grow larger, consider storing translations in separate files and loading them as needed. JSON format is a suitable option for nested objects like these. ### Configuring Custom Translations Similar to how we set the language, we can now provide additional translation resources through the `translationConfig` Below is an example of loading both translations while setting the default language to **French**: ``` import { translationNamespace as sdkDataNamespace } from '@sisense/sdk-data'; ``` Note that we specified the namespace for **Spanish**, as this translation is meant to be loaded for the `sdk-data` package. Translation namespace values can be found in `translationNamespace` constant exported from every package that has translations. If `namespace` is not specified, the translation resource will be registered for `sdkUi` namespace that corresponds to the `sdk-ui` package. ## Advanced Configuration For more advanced internationalization configurations, the `i18n` instance is accessible through the [`useTranslation` hook](https://react.i18next.com/latest/usetranslation-hook) from the `react-i18next` package. **Note:** The `i18n` instance is initialized within the `SisenseContextProvider`. Therefore, it is important to use the `useTranslation` hook either within the `SisenseContextProvider` or in its child components. Attempting to access `i18n` outside of this context will not yield the instance utilized by the Compose SDK. #### Example: Checking Loaded Resources To verify if the **French** resource bundle is loaded for the `sdk-ui` package, you can use the following code: ``` import { useTranslation } from 'react-i18next'; import { useEffect } from 'react'; const MyComponent = () => { const { i18n } = useTranslation(); useEffect(() => { const frenchTranslationResourse = i18n.getResourceBundle('fr-FR', 'sdkUi'); console.log(`Loaded French translation ${JSON.stringify(frenchTranslationResourse)}`) }, [i18n]); return <>; } ``` For further details, please refer to the [i18next API documentation](https://www.i18next.com/overview/api). --- --- url: 'https://developer.sisense.com/guides/sdk/guides/jest-compatibility.md' --- # Setting Up Jest for Compatibility with Compose SDK ## Overview The Compose SDK packages are built with ECMAScript Modules (ESM), while Jest, by default, expects CommonJS (CJS) modules. This difference creates compatibility challenges when using Jest for testing with Compose SDK. You can learn more about the differences between ESM and CJS in the [Node.js official documentation](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs). While Jest does provide [experimental ESM support](https://jestjs.io/docs/ecmascript-modules), it remains unstable and not recommended for production use. ## Including CommonJS packages Starting with version 1.22.0, Compose SDK packages include a CommonJS build alongside the ESM build. This allows you to configure Jest to use the CJS version of Compose SDK in tests, while the main application can continue using the ESM version. ### File Structure for CJS in Compose SDK * *Primary Package* (`sdk-ui`): Includes `.cjs` files alongside `.js` files within the `dist` folder. * *Other Packages* (`sdk-common`, `sdk-data`, `sdk-modeling`, `sdk-query-client`, `sdk-rest-client`, `sdk-tracking`): Contain a `cjs` folder within the `dist` directory, which houses the CJS build files. ## Configuring Jest to Use CJS Packages To direct Jest to the appropriate CJS files, you’ll use the [`moduleNameMapper`](https://jestjs.io/docs/tutorial-react-native#modulenamemapper) configuration in your Jest config. This maps the ESM package paths to the CJS equivalents. **Note**: Compose SDK uses the ESM version of [lodash](https://lodash.com/), so it’s also necessary to map lodash explicitly to its CJS version. Here are suggested configurations for common frameworks: ### Jest `moduleNameMapper` Configuration **React** ``` "moduleNameMapper": { "^@sisense/sdk-(common|data|modeling|query-client|rest-client|tracking)(.*)$": "/node_modules/@sisense/sdk-$1/dist/cjs$2", "^@sisense/sdk-ui(.*)$": "/node_modules/@sisense/sdk-ui/dist$1", "^lodash-es(.*)$": "/node_modules/lodash$1" } ``` **Angular** ``` "moduleNameMapper": { "^@sisense/sdk-(common|data|modeling|query-client|rest-client|tracking)(.*)$": "/node_modules/@sisense/sdk-$1/dist/cjs$2", "^@sisense/sdk-ui-angular(.*)$": "/node_modules/@sisense/sdk-ui-angular/dist/fesm2020/sisense-sdk-ui-angular.mjs", "^lodash-es(.*)$": "/node_modules/lodash$1" } ``` **Vue**: ``` "moduleNameMapper": { "^@sisense/sdk-(common|data|modeling|query-client|rest-client|tracking)(.*)$": "/node_modules/@sisense/sdk-$1/dist/cjs$2", "^@sisense/sdk-ui-vue(.*)$": "/node_modules/@sisense/sdk-ui-vue/dist$1", "^lodash-es(.*)$": "/node_modules/lodash$1" } ``` ## Conclusion By mapping the necessary Compose SDK packages to their CJS builds, Jest can effectively work with Compose SDK. This setup ensures that testing compatibility is maintained without impacting the main application’s use of ESM. --- --- url: 'https://developer.sisense.com/guides/sdk/guides/migration-guide-1.0.0.md' --- # Migrating Compose SDK from 0.x.x to 1.0.0 Released in December 2023, Compose SDK (C-SDK) major version `1.0.0` introduces a number of breaking changes. If your application is still using C-SDK version less than `1.0.0`, follow this guide to migrate. > **Note**: > This guide is for React. > As of December 2023, C-SDK for other frameworks including Angular and Vue are still under internal testing. ## Renamed (1) For chart components in `@sisense/sdk-ui`, type alias `StyleOptions` has been renamed to `ChartStyleOptions`. If your app uses this type alias explicitly, do a simple search and replace in the code. (2) For indicator chart in `@sisense/sdk-ui`, interface `IndicatorDataOptions` has been renamed to `IndicatorChartDataOptions`. If your app uses this interface explicitly, do a simple search and replace in the code. (3) In `@sisense/sdk-data`, namespaces `measures` and `filters` have been renamed to `measureFactory` and `filterFactory`, respectively. Here is a code example to demonstrate the usage of `measureFactory` and `filterFactory` BEFORE ``` import { filters, measures } from '@sisense/sdk-data'; import * as DM from './sample-ecommerce'; const { data, isLoading, isError } = useExecuteQuery({ dataSource: DM.DataSource, dimensions: [DM.Commerce.AgeRange], measures: [measures.sum(DM.Commerce.Revenue)], filters: [filters.greaterThan(DM.Commerce.Revenue, 1000)], }); if (isLoading) { return
Loading...
; } if (isError) { return
Error
; } if (data) { return
{`Total Rows: ${data.rows.length}`}
; } return null; ``` AFTER ``` import { filterFactory, measureFactory } from '@sisense/sdk-data'; import * as DM from './sample-ecommerce'; const { data, isLoading, isError } = useExecuteQuery({ dataSource: DM.DataSource, dimensions: [DM.Commerce.AgeRange], measures: [measureFactory.sum(DM.Commerce.Revenue)], filters: [filterFactory.greaterThan(DM.Commerce.Revenue, 1000)], }); if (isLoading) { return
Loading...
; } if (isError) { return
Error
; } if (data) { return
{`Total Rows: ${data.rows.length}`}
; } return null; ``` ## Removed (1) Parameter `widgetStyleOptions` has been removed from `ChartWidgetProps`, `TableWidgetProps`, and `DashboardWidgetProps`. Its options are now merged into the `styleOptions` prop to streamline the above mentioned props. This means that only `styleOptions` is needed to customize the look and feel of `*Chart`, `ChartWidget`, and `DashboardWidget` components. Here is an example of `ChartWidget` with both `widgetStyleOptions` and `styleOptions` defined before and after the change: Notice that `widgetStyleOptions` has been merged to `styleOptions`. BEFORE ``` ``` AFTER ``` ``` ## Updated (1) The return value of the `ExecuteQuery` component is now consistent with that of the `useExecuteQuery` hook. Specifically, `ExecuteQuery` returns `QueryState` instead of just `data`. Here is a code example to demonstrate the usage of `ExecuteQuery` before and after the change: BEFORE ```tsx { (data) => { if (data) { console.log(data); return
{`Total Rows: ${data.rows.length}`}
; } } }
``` AFTER ```tsx { ( { data, isLoading, isError } ) => { if (isLoading) { return
Loading...
; } if (isError) { return
Error
; } if (data) { return
{`Total Rows: ${data.rows.length}`}
; } return null; } }
``` (2) Similarly, the return value of the `ExecuteQueryByWidgetId` component is now consistent with that of the `useExecuteQueryByWidgetId` hook. Specifically, `ExecuteQueryByWidgetId` returns `QueryByWidgetIdState` instead of just `data` and `query`. Here is a code example to demonstrate the usage of `ExecuteQueryByWidgetId` before and after the change: BEFORE ```tsx { (data, query) => { if (data) { return
{`Total Rows: ${data.rows.length}`}
; } } }
``` AFTER ```tsx { ({data, isLoading, isError}) => { if (isLoading) { return
Loading...
; } if (isError) { return
Error
; } if (data) { console.log(data); return
{`Total Rows: ${data.rows.length}`}
; } return null; } }
``` --- --- url: 'https://developer.sisense.com/guides/sdk/guides/migration-guide-2.0.0.md' --- # Migrating Compose SDK from 1.x.x to 2.0.0 Released in April 2025, the major version `2.0.0` of the Compose SDK introduces several breaking changes.\ If your application is still using a Compose SDK version earlier than `2.0.0`, follow this guide to migrate. ## Minimum React Version The minimum supported version of **React** is now v17.0.0. ## Minimum Angular Version The minimum supported version of **Angular** is now v17. ## Removed Deprecated Entities 1. ### `DashboardWidget` Component If your app uses `DashboardWidget` (React/Vue) or `\` (Angular) explicitly, replace them with the equivalent as described in the list below. The `DashboardWidget` component was renamed to `WidgetById` and deprecated in version `1.23.0`. It has now been removed in version `2.0.0`. **React**, **Vue** * The `DashboardWidget` component has been removed. Use the `WidgetById` component instead. * The `DashboardWidgetProps` interface has been removed. Use the `WidgetByIdProps` interface instead. * The `DashboardWidgetStyleOptions` interface has been removed. Use the `WidgetByIdStyleOptions` interface instead. **Angular** * The `` component has been removed. Use the `` component instead. * The `DashboardWidgetProps` interface has been removed. Use the `WidgetByIdProps` interface instead. * The `DashboardWidgetStyleOptions` interface has been removed. Use the `WidgetByIdStyleOptions` interface instead. 2. ### `WidgetModel` Interface In previous versions, `WidgetModel` was a class instance that included methods to convert its data into component props.\ In version `1.20.0`, these methods were moved into separate `widgetModelTranslator` utility functions and deprecated on the `WidgetModel` interface.\ In version `2.0.0`, these methods have been removed, and `WidgetModel` is now a plain object. Removed deprecated `WidgetModel` methods: * `getExecuteQueryParams` – use `widgetModelTranslator.toExecuteQueryParams` instead. * `getExecutePivotQueryParams` – use `widgetModelTranslator.toExecutePivotQueryParams` instead. * `getChartProps` – use `widgetModelTranslator.toChartProps` instead. * `getTableProps` – use `widgetModelTranslator.toTableProps` instead. * `getPivotTableProps` – use `widgetModelTranslator.toPivotTableProps` instead. * `getPivotTableWidgetProps` – use `widgetModelTranslator.toPivotTableWidgetProps` instead. * `getChartWidgetProps` – use `widgetModelTranslator.toChartWidgetProps` instead. * `getTableWidgetProps` – removed. Use `widgetModelTranslator.toChartWidgetProps` instead. * `getTextWidgetProps` – use `widgetModelTranslator.toTextWidgetProps` instead. Example: ```tsx // Before const chartProps = widgetModel.getChartProps(); // After const chartProps = widgetModelTranslator.toChartProps(widgetModel); ``` 3. ### Widget type Previously, the `WidgetModel` relied on the `Fusion` list of widget types. To improve consistency, the `WidgetModel` now uses the Compose SDK's own simplified list of widget types compatible with `Widget` component and `WidgetProps`.\ If you create/modify `WidgetModel` objects directly, you need to update the `widgetType` property to use the new type. ```ts type WidgetType = 'chart' | 'pivot' | 'text' | 'plugin'; ``` Example: ```tsx // Before const widgetModel: WidgetModel = { widgetType: 'chart/scatter', chartType: 'scatter', // ... other properties }; // After const widgetModel: WidgetModel = { widgetType: 'chart', chartType: 'scatter', // ... other properties }; ``` 4. ### `TableStyleOptions` Interface * Removed deprecated `TableStyleOptions.headersColor` – use `TableStyleOptions.header.color.enabled` instead. * Removed deprecated `TableStyleOptions.alternatingRowsColor` – use `TableStyleOptions.rows.alternatingColor.enabled` instead. * Removed deprecated `TableStyleOptions.alternatingColumnsColor` – use `TableStyleOptions.columns.alternatingColor.enabled` instead. ```ts // Before const styleOptions: TableStyleOptions = { headersColor: true, alternatingColumnsColor: true, alternatingRowsColor: true, }; // After const styleOptions: TableStyleOptions = { header: { color: { enabled: true, }, }, columns: { alternatingColor: { enabled: true, }, }, rows: { alternatingColor: { enabled: true, }, }, }; ``` 5. ### `PivotGrandTotals.title` The deprecated `PivotGrandTotals.title` property has been removed.\ To customize the title of grand totals, use the translation mechanism (see [Translation Guide](./internationalization.md) for details). Example: ```tsx const frenchTranslationResources: Partial = { pivotTable: { grandTotal: 'Total général', subTotal: '{{value}} total', }, }; ``` 6. ### Widget drilldown * `DrilldownWidgetProps.drilldownDimensions` and `DrilldownOptions.drilldownDimensions` were deprecated in version `1.20.0` and have now been removed. Use `DrilldownWidgetProps.drilldownPaths` and `DrilldownOptions.drilldownPaths` instead. ```ts // Before const drilldownOptions = { drilldownDimensions: [DM.Commerce.AgeRange, DM.Commerce.Gender, DM.Commerce.Condition], }; // After const drilldownOptions = { drilldownPaths: [DM.Commerce.AgeRange, DM.Commerce.Gender, DM.Commerce.Condition], }; ``` 7. ### Dashboard Helpers * The deprecated `modifyFilter` utility has been removed. Rename usages to `replaceFilter` instead. 8. ### `ThemeSettings` * The deprecated `themeSettings.chart.panelBackgroundColor` property has been removed. Use `themeSettings.filter.panel.backgroundColor` instead for theming filter panel. 9. ### Query Params **Angular**: * The deprecated `onBeforeQuery` property in `ExecuteQueryParams` has been removed. Use the `beforeQuery` parameter instead. *** ## Type Fixes **All frameworks**: * The deprecated `CriteriaFilterType` type has been removed. Use the regular `Filter` type instead. **Vue**: In version `2.0.0` prop types were aligned across frameworks, with the following changes: * The `dataOptions` and `chartType` props for all charts and `ChartWidget` are now required. * The `dataOptions` prop for `Table` and `PivotTable` is now required. * The `url` prop for `SisenseContextProvider` is now required. * The `widgets` prop for `Dashboard` is now required. * The `closeContextMenu` prop for `ContextMenu` is now required. * The `clearDrilldownSelections`, `currentDimension`, `filtersDisplayValues`, and `sliceDrilldownSelections` props for `DrilldownBreadcrumbs` are now required. * The `filter`, `onUpdate`, and `title` props for `CriteriaFilterTile` and `RelativeDateFilterTile` are now required. * The `attribute`, `filter`, `onChange`, and `title` props for `DateRangeFilterTile` and `MemberFilterTile` are now required. * The `onChange` prop for `FilterTile` is now required. * The `dashboardOid` prop for `DashboardById` is now required. * The `dashboardOid` and `widgetOid` props for `WidgetById` are now required. * The `dataSource` prop for `GetNlgInsights` is now required. **Angular**: * The `dataPointsSelect` prop for multiple charts has been renamed to `dataPointsSelected`. * The callbacks `dataPointClick`, `dataPointContextMenu`, `dataPointsSelect`, and `beforeRender` for specific charts are now strictly typed, referencing the values specific to each chart. --- --- url: 'https://developer.sisense.com/guides/sdk/guides/plugins/index.md' --- # Plugins Widget plugins let you build custom visualizations and register them as dashboard widgets in Compose SDK. A plugin exposes a React component that the SDK renders inside any widget slot, giving you full control over the visualization, data inputs, and style configuration. ## DevX Guides Set up your plugin development environment and explore the full toolchain reference: * **[Plugin DevX Quickstart Guide](./plugin-devx-quickstart.md)** — Create, develop, and deploy a plugin from scratch. Covers scaffolding, dev server, build, and Fusion deployment. * **[Plugin DevX Reference](./plugin-devx-reference.md)** — Complete CLI reference, project structure, testing, framework integration examples, and Fusion deployment details. * **[AI-Driven Development](./ai-driven-development.md)** — Build plugins faster with AI coding agents using the bundled context files, pre-approved permissions, and `/design-custom-widget` skill. ## Tutorial Learn the plugin API step by step: **[Widget Plugin Tutorial](../../tutorials/tutorial-widget-plugins/index.md)** — A progressive walkthrough covering visualization props, data fetching, data panel configuration, design panels, and cross-filtering. --- --- url: >- https://developer.sisense.com/guides/sdk/guides/plugins/ai-driven-development.md --- # AI-Driven Development Build Sisense widget plugins faster with any AI coding agent. Every plugin project scaffolded by `create-plugin` includes a complete AI development environment — an `AGENTS.md` context file, reference docs in `.claude/docs/`, and pre-approved tool permissions. Describe your goal in plain language and the agent implements it using the correct SDK patterns without needing to read the SDK source. Type errors are caught automatically after every file edit and fixed in the same turn. > **Prerequisite:** Scaffold a plugin project first. See the [Quick Start](./plugin-devx-quickstart.md). *** ## What's Included Every plugin project contains a `.claude/` folder with AI context and reference material: | Path | What it does | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `AGENTS.md` | Primary context file — loaded automatically by Claude Code, Cursor, Windsurf, and other AI coding agents on every session | | `.claude/docs/` | Detailed guides for visualization, data fetching, data model, data panel, design panel, and event handling | | `.claude/docs/hooks-reference.md` | All hooks and utilities from `@sisense/sdk-ui` | | `.claude/docs/types-reference.md` | All SDK types used in plugin development | | `.claude/docs/errors.md` | Common runtime, TypeScript, build, and deployment errors with fixes | | `.claude/settings.json` | *(Claude Code only)* Pre-approved tool permissions and a hook that runs TypeScript type checking after every file edit | | `.claude/skills/design-custom-widget/` | *(Claude Code only)* `/design-custom-widget` skill — explicitly triggers the full implementation flow | | `.claude/commands/deploy.md` | *(Claude Code only)* `/deploy` command — pre-flight checks then upload to Sisense Fusion | *** ## Getting Started ### 1. Open the project Open the scaffolded plugin folder in your editor or terminal. ### 2. Describe your goal Tell the AI what you want to build in plain language: ``` I want to build a bar chart showing revenue by product category with cross-filtering. ``` With Claude Code you can also invoke the `/design-custom-widget` skill directly, which triggers the same flow explicitly. Either way, the agent reads your source files automatically, then asks any remaining questions it needs (all at once): * What does the visualization show? (if not already described) * Will users cross-filter other widgets by clicking? * Do you have a preferred charting library? * Will users configure visual styling from the widget editor sidebar? From your answers, it implements everything in a single step: data inputs, library install, `Visualization.tsx`, cross-filtering, resize handling, style controls, and a final `tsc` + lint check. No follow-up commands needed. *** ## Claude Code Commands For Claude Code users, two commands are available as shortcuts: | Command | What it does | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `/design-custom-widget` | Explicitly triggers the full implementation flow — reads source files, asks clarifying questions, implements everything: data inputs, library, chart code, resize observer, style controls, type checks | | `/deploy` | Pre-flight checks (env, types, lint) then build and deploy to Sisense Fusion | These commands are optional — describing your goal in plain language works just as well. For all other tasks (adding cross-filtering, style options, data inputs, tooltips, number formatting, debugging) plain language is the only path regardless of agent. *** ## Typical Workflow The typical path from empty project to deployed plugin: 1. **Describe your visualization goal** — the agent reads your source files, asks a few questions, and implements everything in one step 2. **Extend incrementally** — describe additions in plain language: "add cross-filtering", "add a color style option", "add a second data input" 3. **Deploy** — ask the agent to deploy, or run `npm run deploy` directly With Claude Code, steps 1 and 3 can be triggered explicitly with `/design-custom-widget` and `/deploy`. *** ## Tips for Effective AI Collaboration **Include input names in your goal description.** If the default `category`/`value` names don't match your chart's semantics, say so upfront — the agent renames them as part of the implementation. Example: "a scatter plot with x and y axes and a breakBy dimension". **One goal per session.** Starting fresh? Describe the full chart and the agent implements everything. Already have a working plugin? Describe one addition at a time — "add cross-filtering", "add a color style option", "add a second dimension input" — and it follows the right guide automatically. **Ask the AI to debug when something looks wrong.** Blank widget, ignored clicks, incorrect values — tell it "debug the plugin" and it checks all 11 common root causes and reports the exact file and fix. It's faster than reading `errors.md` manually. **Keep conversations focused.** AI agents work best when each conversation has a clear goal. "Add cross-filtering" is better than "make the chart interactive and also improve the design panel and fix the deploy." One feature per conversation gives more targeted results. *** ## Further Reading * [Quick Start](./plugin-devx-quickstart.md) — scaffold, setup, and deploy from scratch * [Widget Plugins Tutorial](../00-widget-plugins-tutorial.md) — deep-dive into visualization, data fetching, data panel, design panel, and event handling * [Plugin DevX Reference](./plugin-devx-reference.md) — full CLI options, project structure, testing, framework integration --- --- url: >- https://developer.sisense.com/guides/sdk/guides/plugins/plugin-devx-quickstart.md --- # Plugin DevX — Quick Start Create, develop, and build a custom widget plugin for Compose SDK. > **Want to understand the plugin API in depth?** See the [Widget Plugins Tutorial](../../tutorials/tutorial-widget-plugins/index.md) for a progressive walkthrough of visualization, data fetching, design panels, and event handling. > > **Need the full CLI reference, all framework examples, or Fusion deployment?** See the [Plugin DevX Reference](./plugin-devx-reference.md). *** ## Prerequisites * **Node.js** >= 20.19.0 * **Compose SDK** >= 2.30.0 * **A Sisense instance:** * **For development:** a URL and API token are needed for live data, but not required to start — you can develop the UI without them. * **For deployment to Fusion:** version 2026.2.2 or later is required. *** ## 1. Create a Plugin ```bash npx @sisense/sdk-cli@latest create-plugin ``` The interactive prompt asks for a name and template: ``` ? What name would you like to give the plugin? (my-custom-plugin) > ? How would you like to start? > Empty Project Line Chart Simple Table ``` Use **Empty** to start from scratch, or **Line Chart** for a working reference implementation. You can also skip prompts with flags: ```bash npx @sisense/sdk-cli@latest create-plugin --name my-custom-chart --template line-chart ``` See the [CLI Reference](./plugin-devx-reference.md#cli-reference) for all options. *** ## 2. Set Up Environment Variables ```bash cd my-custom-chart # npm npm install # yarn yarn install cp .env.local.example .env.local ``` Edit `.env.local` with your Sisense credentials: ```bash VITE_APP_SISENSE_URL=https://your-instance.sisense.com VITE_APP_SISENSE_TOKEN=your-api-token ``` Without these values, the dev server starts but shows a "Configuration required" warning instead of live data. **Note:** To deploy the plugin to your Sisense Fusion instance, use an API token for a user with the 'Admin' role (see [Step 7](#7-deploy-to-sisense-fusion)). *** ## 3. Start the Dev Server ```bash # npm npm run dev # yarn yarn dev ``` Opens at `http://localhost:3000` with a split layout — your visualization on the left, design panel on the right: ![Plugin dev server split layout](../../img/plugins-guide/plugin-devx-dev-server.png "Plugin dev server") Changes to files in `src/` are reflected instantly via hot module replacement. *** ## 4. Edit Your Components The two files you'll spend most time in: **`src/components/Visualization.tsx`** — your visualization. Implements [`CustomVisualization`](../../modules/sdk-ui/type-aliases/type-alias.CustomVisualization.md) with [`CustomVisualizationProps`](../../modules/sdk-ui/interfaces/interface.CustomVisualizationProps.md): ```tsx import type { CustomVisualization, CustomVisualizationProps } from '@sisense/sdk-ui'; import type { DataOptions, StyleOptions } from '../types.js'; export type VisualizationProps = CustomVisualizationProps; export const Visualization: CustomVisualization = ({ dataSource, dataOptions, filters, styleOptions, }) => { return
...
; }; ``` **`src/components/DesignPanels.tsx`** — style configuration UI. Implements [`DesignPanelProps`](../../modules/sdk-ui/interfaces/interface.DesignPanelProps.md): ```tsx import type { DesignPanelProps } from '@sisense/sdk-ui'; import type { StyleOptions } from '../types.js'; export const DesignPanels = ({ styleOptions, onChange }: DesignPanelProps) => { return
{/* your configuration controls */}
; }; ``` Edit `src/dev-preview-props.ts` to provide sample data that matches your `DataOptions` type. > **Using an AI agent?** The project includes a pre-configured `.claude/` folder with reference docs readable by any AI coding agent. With Claude Code, run `/design-custom-widget` and describe your chart — the agent implements everything (data inputs, library, chart code, style controls) in one step. With other agents, describe your goal in plain language and reference the guides in `.claude/docs/` directly. See [AI-Driven Development](./ai-driven-development.md) for the full workflow. *** ## 5. Build ```bash # npm npm run build # yarn yarn build ``` Produces framework-aware outputs: | Export path | Target | Output | | ------------- | ------- | ------------------------------ | | `"."` | React | `dist/react/main.js` | | `"./vue"` | Vue | `dist/cross-framework/main.js` | | `"./angular"` | Angular | `dist/cross-framework/main.js` | *** ## 6. Register in Your App Install the plugin (published to npm or from a local path): ```bash # npm npm install my-custom-chart # yarn yarn add my-custom-chart # from local path (run build first) # npm npm install ./path/to/my-custom-chart # yarn yarn add file:./path/to/my-custom-chart ``` Then register it via the `plugins` prop. See [`WidgetPlugin`](../../modules/sdk-ui/interfaces/interface.WidgetPlugin.md) for the full plugin object shape: ```tsx import { Dashboard, SisenseContextProvider } from '@sisense/sdk-ui'; import myPlugin from 'my-custom-chart'; function App() { return ( ); } ``` The `customWidgetType` in your widget config must match the `name` field in `src/index.tsx`. For Vue and Angular integration, see the [Framework Integration](./plugin-devx-reference.md#framework-integration) section in the reference. *** ## 7. Deploy to Sisense Fusion Once your plugin is built, you can deploy it directly to a Sisense Fusion instance. Make sure `.env.local` contains your Sisense URL and an API token for a user with the 'Admin' role (see [Step 2](#2-set-up-environment-variables)), then run: ```bash # npm npm run deploy # yarn yarn deploy ``` This command builds the Fusion bundle (`dist-fusion/plugin.zip`) and uploads it to your instance in one step. After a successful deploy the plugin is available immediately — no manual upload needed. For details on the Fusion bundle format, `plugin.json` metadata, and what the deploy script does under the hood, see [Deploying to Sisense Fusion](./plugin-devx-reference.md#deploying-to-sisense-fusion) in the reference. *** ## Next Steps * **[AI-Driven Development](./ai-driven-development.md)** — use an AI agent to build plugins faster: describe your goal in plain language and the agent implements everything in one step * **[Widget Plugins Tutorial](../../tutorials/tutorial-widget-plugins/index.md)** — learn the plugin API: visualization props, data fetching, data panel, design panel, event handling * **[Plugin DevX Reference](./plugin-devx-reference.md)** — full CLI options, project structure, testing, all framework examples, Fusion deployment --- --- url: >- https://developer.sisense.com/guides/sdk/guides/plugins/plugin-devx-reference.md --- # Plugin DevX — Reference Complete reference for the Plugin DevX toolchain. For a guided first-time setup, see the [Quick Start](./plugin-devx-quickstart.md). *** ## CLI Reference ### Command ```bash npx @sisense/sdk-cli@latest create-plugin [name] [options] ``` | Option | Description | | ----------------------- | -------------------------------------------------------------------------------------------------------- | | `[name]` | Plugin name (also used as the folder name). Defaults to `my-custom-plugin` if omitted. | | `--name ` | Plugin name. Alphanumeric, hyphens, and underscores only. | | `--path ` | Override the output directory. Defaults to `{cwd}/{name}` when not set. | | `--template