PowerBiDevCamp / SalesforceAppOwnsDataEmbedding

A SFDX project for Salesforce demonstrating how to implement App-Owns-Data embedding.
26 stars 25 forks source link

Javascript Error #4

Open mhouttemane opened 2 years ago

mhouttemane commented 2 years ago

Hi !

I'm trying to embed power Bi reports in my salesforce organization through LWC and Aura component (same problems with the 2). I follow this explanations : https://github.com/PowerBiDevCamp/SalesforceAppOwnsDataEmbedding

However, I get an error on each time, I load the page.User-added imageIn javascript, screenshot of the message is attached. PowerBIError1

auraproddebug.js:28891 Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Window': # could not be cloned. at Object.postMessage (https://myOrg.lightning.force.com/auraFW/javascript/7FPkrq-upw5gdD4giTZpg/aura_proddebug.js:28891:46) at WindowPostMessageProxy.sendResponse (https://myOrg.lightning.force.com/resource/1642763618000/powerbijs:6067:23) at eval (https://myOrg.lightning.force.com/resource/1642763618000/powerbijs:6135:32)

It seems that something went wrong between aura framework and powerbi javascript.

Here is the code :

powerBiReport.html : ` powerBiReport.js import { LightningElement, api, wire, track } from 'lwc'; import getEmbeddingDataForReport from '@salesforce/apex/PowerBiEmbedManager.getEmbeddingDataForReport'; import powerbijs from '@salesforce/resourceUrl/powerbijs'; import { loadScript, loadStyle } from 'lightning/platformResourceLoader';

export default class PowerBiReport extends LightningElement {

@api WorkspaceId =''; @api ReportId =''; @track reportBI; count = 0;

@wire(getEmbeddingDataForReport,{ WorkspaceId: "$WorkspaceId", ReportId: "$ReportId" }) report;

renderedCallback() {

   console.log('renderedCallback exectuting');

    Promise.all([ loadScript(this, powerbijs ) ]).then(() => { 

      console.log('renderedCallback 2');
      console.log("this.report", this.report);

        if(this.report.data){

          if(this.report.data.embedUrl && this.report.data.embedToken){
            var reportContainer = this.template.querySelector('[data-id="embed-container"');              

            var reportId = this.report.data.reportId;
            var embedUrl = this.report.data.embedUrl;
            var token = this.report.data.embedToken;

            var config = {
              type: 'report',
              id: reportId,
              embedUrl: embedUrl,
              accessToken: token,
              tokenType: 1,
              settings: {
                panes: {
                  filters: { expanded: false, visible: true },
                  pageNavigation: { visible: false }
                }
              }
            };

            // Embed the report and display it within the div container.
            var reportBI = powerbi.embed(reportContainer, config);

            console.log(powerbi);

          }
          else {
            console.log('no embedUrl or embedToken');
          }

          }
          else{
              console.log('no report.data yet');
          }

    });

}

}`

PowerBiEmbedManager.cls `public with sharing class PowerBiEmbedManager {

public PowerBiEmbedManager() {}

public class ClientCredentialPostData {
    public String client_id;
    public String client_info;
    public string client_secret;
    public String scope;
    public string grant_type;
    public String getPostData(){
      return 'client_id=' + this.client_id + 
            '&client_info=' + this.client_info +
            '&client_secret=' + this.client_secret +
            '&scope=' + this.scope +
            '&grant_type=' + grant_type;
    }
}

public class ClientCredentialResponse {
    public String access_token;
    public String expires_in;
    public String ext_expires_in;
    public String token_type;
}

public class PowerBiReport    {
    public String id { get; set; } 
    public String reportType { get; set; } 
    public String name { get; set; } 
    public String webUrl { get; set; } 
    public String embedUrl { get; set; } 
    public boolean isFromPbix { get; set; } 
    public boolean isOwnedByMe { get; set; } 
    public String datasetId { get; set; } 

}

public class PowerBiEmbedToken    {
    public string token { get; set; } 
    public string tokenId { get; set; } 
    public DateTime expiration { get; set; } 
}

public class PowerBiReportData    {
    @AuraEnabled
    public String workspaceId { get; set; } 
    @AuraEnabled
    public String reportId { get; set; } 
    @AuraEnabled
    public String name { get; set; } 
    @AuraEnabled
    public String embedUrl { get; set; } 
    @AuraEnabled
    public String embedToken { get; set; } 
    @AuraEnabled
    public DateTime embedTokenExpires { get; set; } 
    @AuraEnabled
    public String error { get; set; } 
 }

public static String getPowerBiAccessToken() {

    // get auth settings from Custom Metadata Type reconrd
    Power_BI_Auth_Setting__mdt authSetting = Power_BI_Auth_Setting__mdt.getInstance('PowerBiApp');
    string TenantId = authSetting.TenantId__c;    
    string ClientId = authSetting.ClientId__c;    
    string ClientSecret = authSetting.ClientSecret__c;        

    // construct URL for client credentials flow
    String aadTokenEndpoint = 'https://login.microsoftonline.com/' + TenantId + '/oauth2/v2.0/token';

    // prepare HTTP request 
    HttpRequest reqClientCredentialsFlow = new HttpRequest();
    reqClientCredentialsFlow.setMethod('POST');
    reqClientCredentialsFlow.setEndpoint(aadTokenEndpoint);        
    reqClientCredentialsFlow.setHeader('Content-Type', 'application/x-www-form-urlencoded');

    // compose data for POST body
    ClientCredentialPostData postData = new ClientCredentialPostData();
    postData.client_id = ClientId;
    postData.client_info = '1';
    postData.client_secret = ClientSecret;
    postData.scope = 'https://analysis.windows.net/powerbi/api/.default';
    postData.grant_type = 'client_credentials';        
    String postBody = postData.getPostData();
    reqClientCredentialsFlow.setBody(postBody);

    // send HTTP POST to execute client credentials flow
    Http http = new Http();        
    HttpResponse response = http.send(reqClientCredentialsFlow);

    // extract and return app-only access token for service principal
    String responseJson = response.getBody();
    ClientCredentialResponse responseData = (ClientCredentialResponse)JSON.deserialize(responseJson, ClientCredentialResponse.class);
    String access_token = responseData.access_token;         
    return access_token;
}

@AuraEnabled(cacheable=true)
public static PowerBiReportData getEmbeddingDataForReport(String WorkspaceId, String ReportId) {

    // get access token for Authorization header
    String access_token = getPowerBiAccessToken();        

    // Call to Power BI Service API to get report data for embedding        
    HttpRequest reqGetReport = new HttpRequest();
    reqGetReport.setMethod('GET');
    String urlGetReport = 'https://api.powerbi.com/v1.0/myorg/groups/' + WorkspaceId + '/reports/' + ReportId;
    reqGetReport.setEndpoint(urlGetReport);
    reqGetReport.setHeader('Authorization', 'Bearer ' + access_token);

    Http http = new Http();        
    HttpResponse response = http.send(reqGetReport);

    // check response for success
    if(response.getStatusCode()!=200){
        System.debug('ERROR --- Getting Report Data --- ERROR');
        System.debug('Status Code: ' + response.getStatusCode());
        PowerBiReportData getReportError = new PowerBiReportData();
        getReportError.error = 'Get Report Error: ' + response.getStatus();
        return getReportError;            
    }            

    // extract Power BI report data from JSON response
    String responseJson = response.getBody();
    PowerBiReport powerBiReport = (PowerBiReport)JSON.deserialize(responseJson, PowerBiReport.class);

    // send report info to debug window
    System.debug('id: ' + powerBiReport.id);        
    System.debug('reportType: ' + powerBiReport.reportType);        
    System.debug('name: ' + powerBiReport.name);  
    System.debug('webUrl: ' + powerBiReport.webUrl);      
    System.debug('embedUrl: ' + powerBiReport.embedUrl);        
    System.debug('isFromPbix: ' + powerBiReport.isFromPbix);        
    System.debug('isOwnedByMe: ' + powerBiReport.isOwnedByMe);        
    System.debug('datasetId: ' + powerBiReport.datasetId);        

    // Call to Power BI Service API to get embed token for report        
    HttpRequest reqGetEmbedToken = new HttpRequest();
    reqGetEmbedToken.setMethod('POST');
    String urlGetEmbedToken = 'https://api.powerbi.com/v1.0/myorg/groups/' + WorkspaceId + '/reports/' + ReportId + '/GenerateToken';
    reqGetEmbedToken.setEndpoint(urlGetEmbedToken);
    reqGetEmbedToken.setHeader('Authorization', 'Bearer ' + access_token);   
    reqGetEmbedToken.setHeader('Content-Type', 'application/json');
    //reqGetEmbedToken.setBody('{"accessLevel": "View", "datasetId": "' + powerBiReport.datasetId + '"}');
    reqGetEmbedToken.setBody('{"accessLevel": "View", "identities" : [{"username": "oneuser@myorg.fr", "roles":["All"], "datasets": ["cec11a11-1c1f-111b-b11b-11aac11111bd"]}]}');

    System.debug('Request body : ' + reqGetEmbedToken.getBody());

    HttpResponse responseEmbedToken = http.send(reqGetEmbedToken);

    // check response for success
    if(responseEmbedToken.getStatusCode()!=200){
        System.debug('ERROR --- Getting Embed Token --- ERROR');
        System.debug('Status Code: ' + responseEmbedToken.getStatusCode());            
        PowerBiReportData getEmbedTokenError = new PowerBiReportData();
        getEmbedTokenError.error = 'Get Embed Token Error: ' + responseEmbedToken.getStatus();
        System.debug('Response body : ' + responseEmbedToken.getBody());
        return getEmbedTokenError;            
    }            

    // extract Power BI embed token and expiration
    PowerBiEmbedToken embedToken = (PowerBiEmbedToken)JSON.deserialize(responseEmbedToken.getBody(), PowerBiEmbedToken.class);

    // send report info to debug window
    System.debug('EmbedToken: ' + embedToken.token);        
    System.debug('EmbedToken ID: ' + embedToken.tokenId);        
    System.debug('expiration: ' + embedToken.expiration);

    // create custom remote-able object to return to caller in browser 
    PowerBiReportData powerBiReportData = new PowerBiReportData();        
    powerBiReportData.workspaceId = WorkspaceId;
    powerBiReportData.reportId = ReportId;
    powerBiReportData.name = powerBiReport.name;
    powerBiReportData.embedUrl = powerBiReport.embedUrl;
    powerBiReportData.embedToken = embedToken.token;
    powerBiReportData.embedTokenExpires = embedToken.expiration;

    return powerBiReportData;            

}

}`

From the example, I just add two lines in PowerBiEmbedManager : reqGetEmbedToken.setHeader('Content-Type', 'application/json'); reqGetEmbedToken.setBody('{"accessLevel": "View", "identities" : [{"username": "oneuser@myorg.fr", "roles":["All"], "datasets": ["cec11a11-1c1f-111b-b11b-11aac11111bd"]}]}');

​​​​Can you please help me or give me some advices ?

Thank you Maxime

heartburn22 commented 2 years ago

Hi!

I am having the exact same problem. The exception is thrown 5 times on every page load.

auraprod.js:88 Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Window': # could not be cloned. at Object.postMessage (https://static.lightning.force.com/cs66/auraFW/javascript/7FPkrq-upw5gdD4giTZpg/aura_prod.js:88:92459) at WindowPostMessageProxy.sendResponse (https://campbellsfoodservice--boylanm.lightning.force.com/resource/1637179820000/powerbijs:6070:23) at eval (https://campbellsfoodservice--boylanm.lightning.force.com/resource/1637179820000/powerbijs:6138:32)

mattknight23 commented 2 years ago

Im also having the same issue with LWC, Aura seems to work ok.

mhouttemane commented 2 years ago

Im also having the same issue with LWC, Aura seems to work ok.

I've also the same issue in Aura... For me LWC and Aura don't work.

heartburn22 commented 2 years ago

Does anyone have any new information on this issue?

heartburn22 commented 2 years ago

Has there been any news on this? Has anyone found a solution?

mattknight23 commented 2 years ago

No In the end I went with Aura as the error didn't seem to occur.

mhouttemane commented 2 years ago

No In the end I went with Aura as the error didn't seem to occur.

Hello,

Can you please show me how you did it ? I have the same error with Aura as well.

Thank you !

jnyong89 commented 2 years ago

Hello,

found some information online that might have helped. It seems it is failing in the static resource file powerbijs.js at line 6067. I have added a workaround code at this property assignment:

WindowPostMessageProxy.prototype.sendResponse = function (targetWindow, message, trackingProperties) {
            this.addTrackingProperties(message, trackingProperties);
            if (this.logMessages) {
                console.log(this.name + " Sending response:");
                console.log(JSON.stringify(message, null, '  '));
            }

        // Fix starts here:
            var windowMessage = '' + message;
        // Replace below line:
            // targetWindow.postMessage(message, "*");
            targetWindow.postMessage(windowMessage, "*");

        };

did a quick test, and did not see any side effects, so I am assuming it works. Not a JS expert, so if there are any JS experts out there that can explain why this fixed the issue, that would be great.

Thanks!

mhouttemane commented 2 years ago

Hello,

found some information online that might have helped. It seems it is failing in the static resource file powerbijs.js at line 6067. I have added a workaround code at this property assignment:

WindowPostMessageProxy.prototype.sendResponse = function (targetWindow, message, trackingProperties) {
          this.addTrackingProperties(message, trackingProperties);
          if (this.logMessages) {
              console.log(this.name + " Sending response:");
              console.log(JSON.stringify(message, null, '  '));
          }

      // Fix starts here:
            var windowMessage = '' + message;
      // Replace below line:
            // targetWindow.postMessage(message, "*");
            targetWindow.postMessage(windowMessage, "*");

      };

did a quick test, and did not see any side effects, so I am assuming it works. Not a JS expert, so if there are any JS experts out there that can explain why this fixed the issue, that would be great.

Thanks!

Hello ! I can confirm this is working.

Thank you !

andy-fitch commented 2 years ago

This appears to fix the issue for me too. I was getting 5 popup error messages as described by other users