dosubot[bot] commented on issue #29902:
URL: https://github.com/apache/superset/issues/29902#issuecomment-2277357852

   <!-- Greeting -->
   Hey @da201501245! 👋 I'm here to help you with any bugs, questions, or 
contributions. Let's squash those bugs together!
   
   <!-- Answer -->
   To address the issue where the `fetchGuestToken` function is not called 
after the internet connection is restored, you can implement a mechanism to 
detect when the internet connection is back online and then refresh the guest 
token. Here is an improved implementation that ensures the `fetchGuestToken` 
function is called to refresh the guest token after the internet connection is 
restored:
   
   ```ts
   import jwt_decode from "jwt-decode";
   
   const REFRESH_TIMING_BUFFER_MS = 5000; // refresh guest token early to avoid 
failed superset requests
   const MIN_REFRESH_WAIT_MS = 10000; // avoid blasting requests as fast as the 
cpu can handle
   const DEFAULT_TOKEN_EXP_MS = 300000; // (5 min) used only when parsing guest 
token exp fails
   
   function getGuestTokenRefreshTiming(currentGuestToken: string) {
     const parsedJwt = jwt_decode<Record<string, any>>(currentGuestToken);
     const exp = new Date(/[^0-9\.]/g.test(parsedJwt.exp) ? parsedJwt.exp : 
parseFloat(parsedJwt.exp) * 1000);
     const isValidDate = exp.toString() !== 'Invalid Date';
     const ttl = isValidDate ? Math.max(MIN_REFRESH_WAIT_MS, exp.getTime() - 
Date.now()) : DEFAULT_TOKEN_EXP_MS;
     return ttl - REFRESH_TIMING_BUFFER_MS;
   }
   
   function setupGuestTokenRefresh(fetchGuestToken: () => Promise<string>, 
ourPort: any) {
     async function refreshGuestToken() {
       const newGuestToken = await fetchGuestToken();
       ourPort.emit('guestToken', { guestToken: newGuestToken });
       setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(newGuestToken));
     }
   
     function handleOnline() {
       refreshGuestToken();
     }
   
     // Check if the browser is online when the script runs
     if (navigator.onLine) {
       refreshGuestToken();
     }
   
     // Add an event listener to detect when the internet connection is restored
     window.addEventListener('online', handleOnline);
   
     // Optionally, you can add an event listener to handle when the internet 
connection is lost
     window.addEventListener('offline', () => {
       console.log('Internet connection lost');
     });
   
     // Initial token refresh setup
     fetchGuestToken().then(initialToken => {
       ourPort.emit('guestToken', { guestToken: initialToken });
       setTimeout(refreshGuestToken, getGuestTokenRefreshTiming(initialToken));
     });
   }
   
   // Example usage within the embedDashboard function
   export async function embedDashboard({
     id,
     supersetDomain,
     mountPoint,
     fetchGuestToken,
     dashboardUiConfig,
     debug = false,
     iframeTitle = "Embedded Dashboard",
   }: EmbedDashboardParams): Promise<EmbeddedDashboard> {
     function log(...info: unknown[]) {
       if (debug) {
         console.debug(`[superset-embedded-sdk][dashboard ${id}]`, ...info);
       }
     }
   
     log('embedding');
   
     if (supersetDomain.endsWith("/")) {
       supersetDomain = supersetDomain.slice(0, -1);
     }
   
     function calculateConfig() {
       let configNumber = 0;
       if (dashboardUiConfig) {
         if (dashboardUiConfig.hideTitle) {
           configNumber += 1;
         }
         if (dashboardUiConfig.hideTab) {
           configNumber += 2;
         }
         if (dashboardUiConfig.hideChartControls) {
           configNumber += 8;
         }
       }
       return configNumber;
     }
   
     async function mountIframe(): Promise<Switchboard> {
       return new Promise(resolve => {
         const iframe = document.createElement('iframe');
         const dashboardConfigUrlParams = dashboardUiConfig ? { uiConfig: 
`${calculateConfig()}` } : undefined;
         const filterConfig = dashboardUiConfig?.filters || {};
         const filterConfigKeys = Object.keys(filterConfig);
         const filterConfigUrlParams = Object.fromEntries(filterConfigKeys.map(
           key => [DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY[key], 
filterConfig[key]]
         ));
   
         // Allow url query parameters from dashboardUiConfig.urlParams to 
override the ones from filterConfig
         const urlParams = { ...dashboardConfigUrlParams, 
...filterConfigUrlParams, ...dashboardUiConfig?.urlParams };
         const urlParamsString = Object.keys(urlParams).length ? '?' + new 
URLSearchParams(urlParams).toString() : '';
   
         // set up the iframe's sandbox configuration
         iframe.sandbox.add("allow-same-origin"); // needed for postMessage to 
work
         iframe.sandbox.add("allow-scripts"); // obviously the iframe needs 
scripts
         iframe.sandbox.add("allow-presentation"); // for fullscreen charts
         iframe.sandbox.add("allow-downloads"); // for downloading charts as 
image
         iframe.sandbox.add("allow-forms"); // for forms to submit
         iframe.sandbox.add("allow-popups"); // for exporting charts as csv
   
         // add the event listener before setting src, to be 100% sure that we 
capture the load event
         iframe.addEventListener('load', () => {
           const commsChannel = new MessageChannel();
           const ourPort = commsChannel.port1;
           const theirPort = commsChannel.port2;
   
           iframe.contentWindow!.postMessage(
             { type: IFRAME_COMMS_MESSAGE_TYPE, handshake: "port transfer" },
             supersetDomain,
             [theirPort],
           );
           log('sent message channel to the iframe');
   
           resolve(new Switchboard({ port: ourPort, name: 
'superset-embedded-sdk', debug }));
         });
         iframe.src = `${supersetDomain}/embedded/${id}${urlParamsString}`;
         iframe.title = iframeTitle;
         mountPoint.replaceChildren(iframe);
         log('placed the iframe');
       });
     }
   
     const [guestToken, ourPort]: [string, Switchboard] = await Promise.all([
       fetchGuestToken(),
       mountIframe(),
     ]);
   
     ourPort.emit('guestToken', { guestToken });
     log('sent guest token');
   
     setupGuestTokenRefresh(fetchGuestToken, ourPort);
   
     function unmount() {
       log('unmounting');
       mountPoint.replaceChildren();
     }
   
     const getScrollSize = () => ourPort.get<Size>('getScrollSize');
     const getDashboardPermalink = (anchor: string) =>
       ourPort.get<string>('getDashboardPermalink', { anchor });
     const getActiveTabs = () => ourPort.get<string[]>('getActiveTabs');
   
     return {
       getScrollSize,
       unmount,
       getDashboardPermalink,
       getActiveTabs,
     };
   }
   ```
   
   This code sets up an event listener for the `online` event, which is 
triggered when the browser regains internet connectivity. When this event is 
detected, the `refreshGuestToken` function is called to refresh the guest 
token. The `getGuestTokenRefreshTiming` function is used to determine the 
optimal timing for refreshing the guest token, ensuring it is refreshed early 
enough to avoid failed requests. The `setupGuestTokenRefresh` function is 
integrated into the `embedDashboard` function to handle the initial token setup 
and subsequent refreshes 
<sup>[[1]](https://github.com/apache/superset/blob/master/superset-embedded-sdk/src/guestTokenRefresh.test.ts)</sup><sup>[[2]](https://github.com/apache/superset/blob/master/superset-embedded-sdk/src/guestTokenRefresh.ts)</sup><sup>[[3]](https://github.com/apache/superset/blob/master/superset-embedded-sdk/src/index.ts)</sup>.
   
   
   <!-- Dosu Comment Footer -->
   *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to