apache / superset

Apache Superset is a Data Visualization and Data Exploration Platform
https://superset.apache.org/
Apache License 2.0
61.98k stars 13.59k forks source link

Dashboard gets stuck on the Loader logo of Superset when embedded into Angular application using Public role. #30402

Open Prachil-MI opened 1 day ago

Prachil-MI commented 1 day ago

Bug description

Dashboard gets stuck on the Loader logo of Superset when embedded into Angular application using Public role.

How to reproduce the bug

  1. Load 4-5 dashboards on a single page, create 4-5 such pages.
  2. Then try switching between the dashboards and pages.
  3. After browsing for some time, on any one random dashboard, issue occurs where the loader does not disappear.

Even if the page has two dashboards, the issue appears after a few navigations. If the user scrolls to the next dashboard, the next dashboard loads. When the user goes back to the same dashboard where the loader was stuck forever, the dashboard loads correctly. Alternatively, if the user refreshes the dashboard, it loads. It looks like the dashboard rendering gets stuck due to some reason within the embedded iframe.

Screenshots/recordings

https://github.com/user-attachments/assets/36cb0b78-4328-4255-8de4-b1b9d03a719c

What we have already tried:

  1. We preloaded couple of dashboards in the background to reduce the download overhead. The actual dashboards load all the static contents from memory. (Ex. JS, CSS, Images Etc. common across multiple dashboards)
  2. We have minimised all the other API calls from the Angular components to leave all the resources free for Superset to use. Basically, apart from visible dashboard's API calls, there are no other API calls which are executed at the time of loading that specific dashboard.
  3. We are loading the dashboards dynamically so that they do not use too many resources. i.e. based on the scroll position.
  4. The dashboards are loaded based on the scroll position.

Operating system

Ubuntu 22.04 LTS

Superset version

4.0.1

Python version

3.9

Node version

16

Browser

Chrome

Additional context

It is our understanding that the issue could be present in the rendering mechanism of the dashboard and how the rendering is handled between multiple API calls which are required for the dashboard to load.

Checklist

dosubot[bot] commented 1 day ago

Hey @Prachil-MI, I'm here to help with any bugs, questions, or contributions you have for Superset! Let's tackle this issue together and get it sorted out. 🤖

I found a similar solved discussion and several closed issues that might help you resolve the problem where the Superset dashboard gets stuck on the Loader logo when embedded into an Angular application using the Public role.

  1. Similar Discussion:

  2. Closed Issues:

To resolve the issue, ensure that the guest token is being fetched and sent correctly to the iframe. Here is a code snippet that shows how to embed a Superset dashboard and handle guest tokens:

const [guestToken, ourPort]: [string, Switchboard] = await Promise.all([
  fetchGuestToken(),
  mountIframe(),
]);

ourPort.emit('guestToken', { guestToken });
log('sent guest token');

async function refreshGuestToken() {
  const newGuestToken = await fetchGuestToken();
  ourPort.emit('guestToken', { guestToken: newGuestToken });
  setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(newGuestToken));
}

setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(guestToken));

Ensure that:

  1. The fetchGuestToken function is correctly implemented and returns a valid guest token.
  2. The guest token is being emitted to the iframe using ourPort.emit('guestToken', { guestToken }).

Additionally, ensure that the embedded page is correctly set up to handle the guest token. The following code from superset-frontend/src/embedded/index.tsx shows how the guest token is processed:

function setupGuestClient(guestToken: string) {
  setupClient({
    guestToken,
    guestTokenHeaderName: bootstrapData.config?.GUEST_TOKEN_HEADER_NAME,
    unauthorizedHandler: guestUnauthorizedHandler,
  });
}

window.addEventListener('message', function embeddedPageInitializer(event) {
  try {
    validateMessageEvent(event);
  } catch (err) {
    log('ignoring message unrelated to embedded comms', err, event);
    return;
  }

  const port = event.ports?.[0];
  if (event.data.handshake === 'port transfer' && port) {
    log('message port received', event);

    Switchboard.init({
      port,
      name: 'superset',
      debug: debugMode,
    });

    let started = false;

    Switchboard.defineMethod(
      'guestToken',
      ({ guestToken }: { guestToken: string }) => {
        setupGuestClient(guestToken);
        if (!started) {
          start();
          started = true;
        }
      },
    );

    Switchboard.start();
  }
});

You can also use the Superset Embedded SDK to simplify the embedding process:

import { embedDashboard } from "@superset-ui/embedded-sdk";

embedDashboard({
  id: "abc123", // given by the Superset embedding UI
  supersetDomain: "https://superset.example.com",
  mountPoint: document.getElementById("my-superset-container"), // any html element that can contain an iframe
  fetchGuestToken: () => fetchGuestTokenFromBackend(),
  dashboardUiConfig: { // dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded (optional), urlParams (optional)
      hideTitle: true,
      filters: {
          expanded: true,
      },
      urlParams: {
          foo: 'value1',
          bar: 'value2',
          // ...
      }
  },
  // optional additional iframe sandbox attributes
  iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox']
});

Ensure that your backend correctly creates and provides the guest token by making a POST request to /security/guest_token with the necessary parameters [5][6][7].

To continue talking to Dosu, mention @dosu.

Prachil-MI commented 21 hours ago

@dosu We have already used the Embedded SDK with the exact given code. The issue still seems to persist in Superset version 4.0.1. The issues mentioned are already resolved and are related to one of the filter option, which is a different case.

dosubot[bot] commented 21 hours ago

The issue where the Superset dashboard gets stuck on the Loader logo when embedded into an Angular application using the Public role, even when using the Embedded SDK with Superset version 4.0.1, could be due to several factors. Here are some specific conditions and configurations to check:

  1. Invalid supersetDomain: Ensure that the supersetDomain does not end with a trailing slash and is a valid URL format.

  2. Failure to Fetch Guest Token: Verify that the fetchGuestToken function is correctly implemented and returns a valid guest token. If the guest token is invalid or not fetched correctly, the dashboard will not load properly.

  3. Incorrect mountPoint: Ensure that the mountPoint HTML element is correctly specified and exists in the DOM.

  4. Misconfigured dashboardUiConfig: Check that the dashboardUiConfig contains valid configurations and parameters.

  5. Iframe Sandbox Configuration: Ensure that the iframeSandboxExtras contains valid sandbox attributes.

  6. Message Channel Initialization: Verify that the message channel between the host window and the iframe initializes correctly.

  7. Unauthorized Embedding: Ensure that the page is embedded in an origin authorized by the curator.

Additionally, specific error messages or logs generated by Superset can help diagnose the issue. Look for errors such as DashboardInvalidError, DashboardNotFoundError, DashboardAccessDeniedError, and others in the Superset logs.

Here is a detailed code snippet for embedding a dashboard and handling guest tokens:

import { embedDashboard } from "@superset-ui/embedded-sdk";

const fetchGuestToken = async () => {
  const response = await fetch('https://your-backend-api.com/get-guest-token', {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
    },
  });

  if (!response.ok) {
    throw new Error('Failed to fetch guest token');
  }

  const data = await response.json();
  return data.guestToken;
};

embedDashboard({
  id: 'your-dashboard-id',
  supersetDomain: 'https://superset.example.com',
  mountPoint: document.getElementById('your-mount-point'),
  fetchGuestToken: fetchGuestToken,
  dashboardUiConfig: {
    hideTitle: true,
    filters: {
      expanded: true,
    },
    urlParams: {
      foo: 'value1',
      bar: 'value2',
    }
  },
  iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox']
});

Ensure that your backend correctly creates and provides the guest token by making a POST request to /security/guest_token with the necessary parameters [1][2][3].

If the issue persists, it might be helpful to look into potential performance issues or limitations when embedding multiple dashboards on a single page, as this could also contribute to the problem.

This is a tough nut to crack, could really use your help on this, @rusackas.

To continue talking to Dosu, mention @dosu.