Sisense Community logo
     
    • Community Feedback
    • Chapters
    • Events
    • Forums
      • Help and How To
      • Product Feedback Forum
      • Strategy & Use Cases
    • Blogs
    • KB Docs
      • KB Docs
      • Add-Ons & Plug-Ins
      • APIs
      • Best Practices
      • Blox
      • CDT
      • Cloud Managed Service
      • Data Models
      • Data Sources
      • Embedding Analytics
      • How-Tos & FAQs
      • Onboarding
      • PySisense
      • Security
      • Sisense Administration
      • Sisense Intelligence & AI
      • Troubleshooting
      • Widget & Dashboard Scripts
    • Support
    • Learning
      • Sisense Academy: Free Courses and Certifications
      • Official Developer Documentation
      • Official Product Documentation
      • Official Sisense Youtube Channel
      • Sisense Compose SDK Playground
      • Official Sisense Discord
    • Use Case Gallery
    •      
    Discussions
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    Discussions
    • TagsChevronRightIcon
    Api-Gateway
    • Blog banner
      • APIsChevronRightIcon

      Connection Tool - Programmatically Remove Unused Datasource Connections, and List All Connections

                                                                                                                                                                       

      Connection Tool - A Tool to Programmatically Remove Unused Datasource Connections, and List All Connections     Managing connections within your Sisense server can become complex over time, if there are a large number of connections, and connections are often added, and replace earlier datasource connections. In some scenarios unused connections can accumulate, potentially cluttering the Connection Manager UI with no longer relevant connections. Although unused connections typically represent minimal direct security risk, it's considered best practice to maintain a clean, organized list of connections, and in some scenarios it can be desired to remove all unused connections. Sisense prevents the deletion of connections actively used in datasources, safeguarding your dashboards and datasources from disruptions. However, inactive or "orphaned" connections remain after datasources are deleted or a connection is replaced, potentially contributing to unnecessary UI complexity in the connection manager UI. Connections can be of any type Sisense supports, common types include various SQL connections, Excel files, and CSV files, as well as many data providers, such as Big Panda. This tool can also be used to list all connections, with no automatic deletion of unused connections. Introducing the Sisense Connection Prune Tool The Sisense Connection Prune Tool is a Python-based Sisense API based tool designed to programmatically identify and delete unused connections. It generates a CSV report listing all connections and their associated datasources, streamlining your connection management process. If desired, it can automatically remove all unused connections automatically from a Sisense server.   Using the Tool, sourcing the Virtual Environment, generating the Connection CSV   CSV Output of Used Connections and Associated Datasources   CSV opened visually to view as Table, Excel and other programs and text editors can open CSV files   Sisense Connection Prune Tool README Here's the full README included with the tool: # Sisense Connection Prune Tool A command-line tool to list used data connections and prune unused Sisense connections via CSV. It allows you to generate a CSV file of all connections and their dependencies, then delete those connections if needed, after removing the connections to keep from the CSV. ## Features - **Dry Run Mode**: Simulate deletions without making any changes. - **CSV-Based Flow**: Easily inspect and list connections to remove before deletion. - **Logging**: Extensive logs if needed. - **Error Handling**: Clear and descriptive messages for issues encountered during execution. ## Usage 1. **Activate the Virtual Environment** After downloading this project folder, activate the Python virtual environment bundled with it that includes all Python dependencies. - **Windows**: `venv\Scripts\activate` - **macOS/Linux**: `source venv/bin/activate` 2. **Configure the Tool** Open the `config.yaml` file and set your Sisense server URL, bearer token, CSV file path, and log file path. For example: ```yaml server_url: "https://your.sisense.server" bearer_token: "your_bearer_token_here" dry_run: true csv_file_path: "connections.csv" log_file_path: "connection_tool.log" ``` - **server_url**: The URL of your Sisense instance. - **bearer_token**: Your Sisense API token for authentication. - **dry_run**: If set to `true`, deletions will be simulated (no real deletions). - **csv_file_path**: Where the CSV file should be created and read from. - **log_file_path**: Where log file will be stored. 3. **Run the Tool** ```bash python3 ConnectionPruneTool.py ``` You will be prompted to choose an option: 1. **Generate connection CSV** - Fetches all Sisense connections. - Immediately removes (or simulates removing, if `dry_run` is `true`) any connection with no dependencies. - Writes all remaining connections and their dependencies to the CSV file. - **Important**: Inspect the CSV file and remove lines for any connections you want to **keep**. 2. **Delete connections from CSV list** - Reads the CSV file. - Removes or simulates removing each connection still listed. - Provides a summary report of which connections were deleted or bypassed. 4. **Review the Logs** Check the file specified in `log_file_path` for a record of all actions taken or simulated if needed. This is helpful for understanding what happened during each run and diagnosing any issues. ## Example Workflow 1. **Generate CSV** ```bash python3 ConnectionPruneTool.py # Choose option 1 when prompted ``` After generation, open the CSV file and **delete rows** corresponding to any connections you want to **keep**. 2. **Delete Connections** ```bash python3 ConnectionPruneTool.py # Choose option 2 when prompted ``` The tool will read the CSV and delete the remaining listed connections (or simulate deletion, if `dry_run` is enabled). ## Notes - Unused connections are removed automatically in step 1, without a CSV step - To keep a connection, remove its line from the CSV before proceeding with deletion. - If `dry_run` is set to `true`, no actual deletions will occur, only simulated logs and printed messages. - The log file will be cleared at the start of each run, so be sure to review or archive logs (or change log file name in config), if needed. ​ This is a command-line tool to list used data connections and prune unused Sisense connections, in general and via a CSV list. It allows a user with a data admin or higher bearer token to generate a CSV file of all connections and their dependencies, then delete those connections if needed, from the remaining connections in the CSV. Example Output: Deleted unused connection: Old_DB_Connection (ID: 123abc) CSV file generated at connections.csv. It contains 25 row(s) of active connections. Please review and remove lines for connections you want to keep before running deletion step by running tool again. Remaining lines will be deleted in deletion mode. Summary Report: Total lines in CSV (active used connections): 25 Deleted Unused Connections: - Old_DB_Connection No connections were bypassed.   API Endpoints Used Retrieve connections: GET /api/v2/connections Retrieve dependencies: GET /api/v2/connections/{connection_id}/getAllDependencies Delete connection: DELETE /api/v2/connections/{connection_id} Full Code connections.py - Uses Sisense API endpoints to: Fetch all connections (GET /api/v2/connections). Retrieve datasource dependencies for a specific connection (GET /api/v2/connections/{connection_id}/getAllDependencies). Delete a specific connection (DELETE /api/v2/connections/{connection_id}).     from helperFunctions import load_config, api_get, api_delete config = load_config() headers = {"Authorization": f"Bearer {config['bearer_token']}"} base_url = config["server_url"] # Retrieve all connections from Sisense def get_all_connections(): endpoint = f"{base_url}/api/v2/connections" return api_get(endpoint, headers) # Retrieve dependencies of a specific connection def get_connection_dependencies(connection_id): endpoint = f"{base_url}/api/v2/connections/{connection_id}/getAllDependencies" return api_get(endpoint, headers) # Delete a specific connection def delete_connection(connection_id): endpoint = f"{base_url}/api/v2/connections/{connection_id}" return api_delete(endpoint, headers) helperFunctions.py - Uses the Requests library to handle API requests (GET/DELETE). Catches and logs API errors. Uses PyYAML to read the config.yaml file for configuration.     import yaml import requests import logging # Load configuration from YAML file def load_config(): with open("config.yaml", "r") as file: return yaml.safe_load(file) # Configure logging settings def setup_logging(log_file): logging.basicConfig( filename=log_file, level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", ) # Perform a GET request with error handling def api_get(endpoint, headers): try: response = requests.get(endpoint, headers=headers, verify=False) response.raise_for_status() return response.json() except requests.RequestException as e: logging.error(f"GET request failed: {e}") print(f"Error during GET request: {e}") return None # Perform a DELETE request with error handling def api_delete(endpoint, headers): try: response = requests.delete(endpoint, headers=headers, verify=False) response.raise_for_status() return response except requests.RequestException as e: logging.error(f"DELETE request failed: {e}") print(f"Error during DELETE request: {e}") return None ConnectionPruneTool.py - Serves as the entry point for the entire tool. Implements a CLI for user interaction. Invokes connections.py and helperFunctions.py to retrieve, analyze, and optionally delete Sisense connections. Uses Pandas to build a DataFrame of connection details and writes the results to a CSV file. Reads back the CSV file to remove selected connections after manual edits. Manages logs and prints summary reports to the terminal. import pandas as pd import logging import os from connections import ( get_all_connections, get_connection_dependencies, delete_connection, ) from helperFunctions import load_config, setup_logging # Load config yaml config = load_config() setup_logging(config["log_file_path"]) # Clear log file at start open(config["log_file_path"], "w").close() dry_run = config["dry_run"] csv_path = config["csv_file_path"] def generate_connections_csv(): """ Retrieves all connections from Sisense, deletes any that have no dependencies (unless in dry_run mode), and writes the remaining used connections and their dependent data models to a CSV file. """ # Create or clear existing CSV open(csv_path, "w").close() connections = get_all_connections() if connections is None: logging.error("Failed to retrieve connections.") print("Failed to retrieve connections.") return data = [] deleted = [] bypassed = [] # Go through each connection returned from the API for conn in connections: dependencies = get_connection_dependencies(conn["oid"]) if dependencies is None: logging.warning( f"Failed to retrieve dependencies for: {conn['name']} ({conn['oid']})" ) print( f"Failed to retrieve dependencies for: {conn['name']} ({conn['oid']})" ) bypassed.append(conn["name"]) continue # If no dependencies, optionally delete the connection (not in dry-run) if not dependencies: action_msg = "[Dry Run] Would delete" if dry_run else "Deleted" log_str = ( f"{action_msg} unused connection: {conn['name']} (ID: {conn['oid']})" ) print(log_str) logging.info(log_str) if not dry_run: try: response = delete_connection(conn["oid"]) # If response is None, treat it as a failed deletion if response is None: logging.error( f"Failed to delete unused connection: {conn['name']} ({conn['oid']})." ) print( f"Error deleting unused connection: {conn['name']} ({conn['oid']})" ) bypassed.append(conn["name"]) else: deleted.append(conn["name"]) except Exception as e: logging.error( f"Exception while deleting unused connection: {conn['name']} ({conn['oid']}) - {e}" ) print( f"Error deleting unused connection: {conn['name']} ({conn['oid']})" ) bypassed.append(conn["name"]) else: # If the connection is used, add each dependency row to CSV. # Use a fallback value if 'title' or 'oid' is missing. for dep in dependencies: dep_title = dep.get( "title", "NULL TITLE Share Datasource with associated Bearer Token user to include in CSV", ) dep_oid = dep.get("oid", "NULL OID") data.append( { "Connection Name": conn["name"], "Connection ID": conn["oid"], "Elasticube/Data Model Name": dep_title, "Elasticube/Data Model ID": dep_oid, } ) # Create a DataFrame of only the used connections (with datasource dependencies) df = pd.DataFrame(data) df.to_csv(csv_path, index=False) row_count = len(df) logging.info(f"Generated CSV at {csv_path}") print( f"CSV file generated at {csv_path}. " f"It contains {row_count} row(s) of used connections.\n" "Please review and remove lines for connections you want to keep " "before running deletion step by running tool again. Remaining lines will be deleted in deletion mode." ) # Summaries for auto-deleted (unused) connections print("\nSummary Report:") print(f"Total lines in CSV (active used connections): {row_count}") if deleted: print("\nDeleted Unused Connections:") for d in deleted: print(f" - {d}") else: print("\nNo connections were deleted in this step.") if bypassed: print("\nBypassed Connections:") for b in bypassed: print(f" - {b}") else: print("\nNo connections were bypassed in this step.") def delete_connections_from_csv(): """ Reads the CSV (remaining lines after user review, removing lines for connections to keep), then deletes each connection listed. If a delete fails or returns None, the connection is logged and added to 'bypassed'. """ if not os.path.exists(csv_path): print( "CSV file does not exist. Please generate it first or fix the config path." ) return df = pd.read_csv(csv_path) deleted = [] bypassed = [] for _, row in df.iterrows(): conn_id = row["Connection ID"] conn_name = row["Connection Name"] action_msg = "[Dry Run] Would delete" if dry_run else "Deleted" log_str = f"{action_msg} connection: {conn_name} ({conn_id})" print(log_str) logging.info(log_str) if not dry_run: try: response = delete_connection(conn_id) if response is None: logging.error( f"Failed to delete connection: {conn_name} ({conn_id})" ) print(f"Error deleting connection: {conn_name} ({conn_id})") bypassed.append(conn_name) else: deleted.append(conn_name) except Exception as e: logging.error( f"Exception while deleting connection: {conn_name} ({conn_id}) - {e}" ) print(f"Error deleting connection: {conn_name} ({conn_id})") bypassed.append(conn_name) # Print summary report print("\nSummary Report:") if deleted: print("Deleted Connections:") for d in deleted: print(f" - {d}") else: print("No connections were deleted.") if bypassed: print("\nBypassed:") for b in bypassed: print(f" - {b}") else: print("No connections were bypassed.") if __name__ == "__main__": # If there is no CSV file found, select step 1 automatically if not os.path.exists(csv_path): print( "No CSV file found; defaulting to generating connections CSV. " "Correct config if CSV file name has changed." ) generate_connections_csv() else: choice = input( "Choose an option:\n" "1 - Generate connection CSV\n" "2 - Delete connections from CSV list\n" "Enter your choice (1 or 2): " ) if choice == "1": generate_connections_csv() elif choice == "2": delete_connections_from_csv() else: print("Invalid option. Please enter '1' or '2'.") Conclusion By automating the detection of inactive connections and simplifying their removal, the Sisense Connection Prune Tool reduces clutter in the Sisense server Connection Manager UI while minimizing the risk of unintentionally impacting active datasources. Whether you opt for a dry-run mode to review potential deletions in a generated CSV file, or to simply list connections, or proceed with the full removal of unused connections, this tool offers a clear, flexible, and reliable approach to keeping your connections organized. A full copy of the tool, is attached below.        

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago • Last reply 4 months ago
      4
               
      • TroubleshootingChevronRightIcon

      Troubleshooting 500 internal server error on REST API PATCH: updating connection parameters

               

      Troubleshooting 500 internal server error on REST API PATCH: updating connection parameters Step-by-Step Guide:  Identify the Cause of the Error : The 500 Internal Server Error typically arises from trying to use outdated API endpoints for updating connections after recent platform upgrades. Check Release Notes : Refer to the latest release notes for insights into recent changes that might impact the API endpoints. Recent updates may have altered the behavior of certain endpoints. Example release notes link: Release Notes . Switch to Connection Manager APIs : To list current connections, use GET /connections . To update connections, apply the PATCH method under the correct section for Connection Management. The changes may relate to a new feature, the Connection Manager, which mandates using its specific API set. Navigate to API version 2.0 -> Connection Management section: Verify Connection Parameters : Ensure your PATCH request includes the appropriate parameter structure, like provider , schema , parameters , Server , UserName , Database , etc., to avert further API errors. Example JSON structure for connection parameters: { "connection": { "provider": "sql", "schema": "Dashboard", "parameters": { "Password": "password", "ApiVersion": 2, "Server": "server", "UserName": "username", "port": "1433", "DefaultDatabase": "", "encrypt": false, "AdditionalParameters": "", "Schema": "", "Database": "database" } } } Switching to the updated Connection Manager API methods and ensuring accurate parameter configuration can resolve the 500 Internal Server Error when modifying connections in Sisense REST API. If problems remain unresolved, enabling access for support can facilitate targeted troubleshooting. Refrences For more detailed information, you can visit: Connection Management API Documentation: API Documentation Sisense Release Notes: Release Notes  

      Ihor Yokhym
      Ihor YokhymPosted 1 year ago
      0
               
      • BloxChevronRightIcon

      BloX: replicating action “send me the report now”

                                                                                       

      BloX: replicating action “send me the report now” This article explains how to develop an action to send a dashboard as a report to the end user. This action replicates the action “Send me the report now”. This action is available only to the dashboard’s owner, but we will develop a BloX action, which will be available for other users. To solve this challenge you will need to do the following: Create a widget of the type ‘BloX’ on the dashboard you want to have the ability to send reports to the end-users; Create a custom action with the following code:       const { widget } = payload; //Get widget’s object from the payload const internalHttp = prism.$injector.get('base.factories.internalHttp'); //Get internal factory to run API requests internalHttp({ url: '/api/v1/reporting', method: 'POST', contentType: 'application/json', data: JSON.stringify({ assetId: widget.dashboard.oid, assetType: "dashboard", recipients: [ { type: 'user', recipient: prism.user._id } ], preferences: { inline: true } }) }).then(res => console.log(res.data));     This action will have the following snippet:   { "type": "sendMeReport", "title": "Send me report" }   Use this snippet in the widget you have created:   { "style": "", "script": "", "title": "", "showCarousel": true, "body": [], "actions": [ { "type": "sendMeReport", "title": "Send me report" } ] }   Now, you have a button on the widget. After clicking this button, Sisense will send a report for the currently authenticated user. Please, note that there will be no indication of the running action. When the action is completed there will be a message in the browser’s console. Feel free to customize the logic of this action to show the end-user that the report is generating. Custom action is a powerful tool, which allows you to create custom interactions. Use this approach to create a new action easily. You can customize the proposed action by adding an indication of the running action or by using custom parameters to change format or size of the generated report (check the description of the endpoint /api/v1/reporting for additional information). Check out related content: Creating customer actions Reporting Send Reports

      Oleksii Demianyk
      Oleksii DemianykPosted 1 year ago
      0
               
      • APIsChevronRightIcon

      Using the InternalHttp Function Within Scripts and Plugins

                                                                                                                               

      Using the InternalHttp Function Within Scripts and Plugins The InternalHttp function is a Sisense function within the Sisense internal Prism object. The prism object and the InternalHttp function are present on all Sisense pages and can be used in scripts and plugins, including when embedded with various forms of Sisense embedding . It facilitates custom additional API requests to the Sisense server by applying the same request headers used for internal Sisense requests to handle details of API requests such as authentication , CORS , and CSRF . Custom additional requests to Sisense API endpoints via custom scripts and plugins are often utilized to perform additional JAQL requests for fetching data from Sisense data models. This process is documented in this informative Sisense Community page . The Community page code remains current and includes an example of using the InternalHttp function for additional JAQL requests. Below is a simplified and shortened version of the example code:     widget.on('ready', function (se, ev) { function runHTTP(jaql) { // Use $internalHttp service if exists const $internalHttp = prism.$injector.get("base.factories.internalHttp"); const ajaxConfig = { url: "/api/datasources/x/jaql", method: "POST", data: JSON.stringify(jaql), contentType: "application/json", dataType: "json", async: false, }; const httpPromise = $internalHttp(ajaxConfig, true); // Return response values return httpPromise.responseJSON.values; } const jaqlObj = { "datasource": { "title": "Sample ECommerce", }, "metadata": [ { "jaql": { "dim": "[Category.Category]", } } ] }; console.log('JAQL Result Values are ', runHTTP(jaqlObj)); });     While InternalHttp is commonly used for JAQL API requests, it can be utilized for any Sisense endpoint, allowing custom requests to any Sisense API, not just those related to JAQL. The JAQL API is how Sisense requests data from Sisense data sources. The flexibility offered by custom API calls significantly enhances the capabilities of scripts, enabling developers to interact with the full range of Sisense functionalities. Notable Recent Resolved Scenarios and the InternalHttp Function CSRF Header Issue: A customer who previously did not use InternalHttp had various widget scripts making custom JAQL requests via the JAQL API endpoint. After modifying the CSRF setting, these scripts failed, and not only did not work, but viewing the widget logged the user out of Sisense immediately. The issue was due to missing CSRF header parameters in the request headers. This was most easily resolved by using InternalHttp in the widget scripts, which automatically includes the required CSRF header parameters. Unauthorized Errors with Sisense.js: A customer using Sisense embedded with Sisense.js on a third party domain and using WAT for authentication faced "Unauthorized" API errors for the custom widget script JAQL requests. Despite working outside of WAT , these requests failed within it. This issue is a known limitation with internalHttp. The workaround for this issue is demonstrated below:     widget.on('beforequery', function (se, ev) { function runHTTP(jaql) { // Use $internalHttp service if exists const $internalHttp = prism.$injector.has("base.factories.internalHttp") ? prism.$injector.get("base.factories.internalHttp") : null; const ajaxConfig = { url: "/api/datasources/x/jaql", method: "POST", data: JSON.stringify(jaql), contentType: "application/json", dataType: "json", async: false, xhrFields: { withCredentials: true, }, }; const httpPromise = $internalHttp ? $internalHttp(ajaxConfig, false) : $.ajax(ajaxConfig); // Return response return httpPromise; } const jaqlObj = { "datasource": { "title": "Sample ECommerce", }, "metadata": [ { "jaql": { "dim": "[Category.Category]", } } ] }; runHTTP(jaqlObj).then((API_Response) => { console.log('JAQL Result Values are ', API_Response.data.values); }); }); ​     In this workaround, the second parameter of the InternalHttp function is set to false, and the returned object is handled as a JavaScript promise. The InternalHttp function is designed to be updated along with Sisense, and to adapt to any changes, allowing for scripts written for one environment (in the native Sisense UI for example) to work in many others without issue (when embedded in a third-party domain for example). These scenarios highlight the value of InternalHttp and its potential applications. Leveraging this function allows developers to ensure their custom scripts remain robust and secure in all scenarios, including in environments with specific security requirements or complex authentication mechanisms. As Sisense continues to evolve and update, understanding and utilizing internal tools like internalHttp will be helpful for maximizing the platform's capabilities and maintaining seamless integration within various deployment and embedded contexts.

      Jeremy Friedel
      Jeremy FriedelPosted 1 year ago
      0
               
      • APIsChevronRightIcon

      How to List Dashboards and Data Models a User Has Access To

                                       

      How to List Dashboards and Data Models a User Has Access To As a Sisense administrator sometimes you want a quick way to get the list of data models and dashboards a user can access. There is no direct way to do this through the user interface and through the API it requires multiple calls.   Included with this article are 2 scripts that each make multiple API calls and return the list of either dashboard (userdashboardlist.py) or data models (userdatamodellist.py) based on the user that contains the passed parameter. To have these scripts work with your Sisense environment, edit each file and replace the “<replaceurl>” with your Sisense URL (i.e. https://companyname.sisense.com ) and “<replacetoken>” with an administrator user’s API token ( User Profile Settings ).   From userdashboardlist.txt (to be renamed to .py) From userdatamodellist.txt (to be renamed to .py) Sample output List of accessible dashboards for users who have “fr” in their name and then for users who have “coke” in their name:   List of accessible data models for users who have “fr” in their name and then for users who have “coke” in their name:  

      kreycraft
      kreycraftPosted 2 years ago • Last reply 2 years ago
      2
               
      • How-Tos & FAQsChevronRightIcon

      An overview of Sisense Linux microservices and what they do

                                       

      An overview of Sisense Linux microservices and what they do This article provides an advanced description of Sisense’s Microservices and how they work internally. This is mainly for Administrators responsible for supporting Sisense in their organization. When we deploy Sisense on the Linux platform Sisense application deploys multiple pods this article helps us understand how they are tied to each Sisense functionality and how they internally operate. See  Sisense Basic Concepts and Terminology and  Sisense Architecture  for a high-level overview. [PLEASE OPEN ATTACHMENT TO VIEW REMAINDER OF THIS ARTICLE]

      Krutika
      KrutikaPosted 2 years ago
      0