I am a javascript novice. I mainly write code in c# so I'm every green at this.
I'm attempting to embed a PowerBI embedded report into a WPF application. I can do this, I have the report showing correctly and the data refreshing on a timer.
However the access token which is loaded with the page expires after a period of time. I'm updating the token into a local file and reading that local file when the HTML page loads.
I don't want to have to reload the HTML page in the WPF web control to refresh the access token, I want to reload the file's contents and update the PowerBI access token in live use.
When I attempt to reload the new token on a timer, the variable holding the token doesn't update. It's still using the new token.
How can I reload the contents of the 'data.json' file to update the PowerBI 'report.setAccessToken'?
see "myMethod".
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Power BI </title>
<!-- Include Power BI JavaScript library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js"></script>
<script src="https://microsoft.github.io/PowerBI-JavaScript/demo/node_modules/powerbi-client/dist/powerbi.js"></script>
</head>
<body>
<div id="embedContainer" style="height: 1080px;"></div>
<script src="app.js" async></script>
<!-- File which contains the access token -->
<script type="text/javascript" src="data.json" id="data"></script>
</body>
</html>
app.js
let loadedResolve, reportLoaded = new Promise((res, rej) => { loadedResolve = res; });
let renderedResolve, reportRendered = new Promise((res, rej) => { renderedResolve = res; });
models = window['powerbi-client'].models;
function embedPowerBIReport() {
var mydata = JSON.parse(data)
let accessToken = mydata[0].accessToken;
// Read embed URL
let embedUrl = "https://app.powerbi.com/reportEmbed?reportId=xxxxx";
// Read report Id
let embedReportId = "xxxx";
// Read embed type from radio
let tokenType = "Bearer";
// We give All permissions to demonstrate switching between View and Edit mode and saving report.
let permissions = models.Permissions.All;
// Create the embed configuration object for the report
let config = {
type: 'report',
tokenType: tokenType == '0' ? models.TokenType.Aad : models.TokenType.Embed,
accessToken: accessToken,
embedUrl: embedUrl,
id: embedReportId,
permissions: permissions,
settings: {
panes: {
filters: {
visible: false
},
pageNavigation: {
visible: false
}
},
bars: {
statusBar: {
visible: false
}
}
}
};
// Get a reference to the embedded report HTML element
let embedContainer = $('#embedContainer')[0];
// Embed the report and display it within the div container.
report = powerbi.embed(embedContainer, config);
// report.off removes all event handlers for a specific event
report.off("loaded");
// report.on will add an event handler
report.on("loaded", function () {
loadedResolve();
report.off("loaded");
});
// report.off removes all event handlers for a specific event
report.off("error");
report.on("error", function (event) {
console.log(event.detail);
});
// report.off removes all event handlers for a specific event
report.off("rendered");
// report.on will add an event handler
report.on("rendered", function () {
renderedResolve();
report.off("rendered");
});
window.setInterval(function () {
report.refresh();
}, 30 * 1000);
}
setInterval(myMethod, 20000);
function myMethod( )
{
var mydata = JSON.parse(data)
accessToken = mydata[0].accessToken;
report.setAccessToken(accessToken);
alert(mydata[0].accessToken);
}
async function loadReport() {
embedPowerBIReport();
await reportLoaded;
await reportRendered;
}
loadReport();
data.json
data = '[{"accessToken" : "xxxxxxx"}]';
I learnt more about JS and decided to create a web socket connection between my WFP desktop application and the JS loading and refreshing the report.
The socket server gets a new token when the JS queries it. The JS then updates the token used by PowerBI.
It's a POC at the moment, code to be improved but this is the working example.
C#
public class LocalWebSocketServer
{
public async Task StartServer(string ipAddress, int port)
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add($"http://{ipAddress}:{port}/");
listener.Start();
Console.WriteLine("Server started. Waiting for connections...");
while (true)
{
HttpListenerContext context = await listener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
ProcessWebSocketRequest(context);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
}
private async Task ProcessWebSocketRequest(HttpListenerContext context)
{
HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
WebSocket socket = webSocketContext.WebSocket;
// Handle incoming messages
byte[] buffer = new byte[1024];
while (socket.State == WebSocketState.Open)
{
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
string receivedMessage = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received message: {receivedMessage}");
var r = await EmbedService.GetEmbedParams(ConfigValidatorService.WorkspaceId, ConfigValidatorService.ReportId);
Console.WriteLine($"Sending message: {r.EmbedToken.Token}");
string messageback = r.EmbedToken.Token;
byte[] messagebackBytes = System.Text.Encoding.UTF8.GetBytes(messageback);
await socket.SendAsync(new ArraySegment<byte>(messagebackBytes), WebSocketMessageType.Text, true, CancellationToken.None);
}
else if (result.MessageType == WebSocketMessageType.Close)
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}
}
}
}
JS
//the token that will be used to authenticate the account
let authToken;
//the socket that will be used to communicate with the server
let socket = new WebSocket("ws://localhost:3000");
//checks if report is loaded - defines flow control on what do to when the auth token is received
let isReportLoaded = false;
//when connected send a message to the server to get the auth token
socket.onopen = function (e) {
console.log("Connection established");
socket.send("GET");
console.log("GET sent");
}
//token update interval - every 15 minutes
setInterval(function() {
socket.send("GET");
}, 900*1000);
//when a message is received from the server, set the auth token and load the report - also update the auth token if the report is already loaded
socket.onmessage = function (event) {
authToken = event.data;
console.log("Token set: " + event.data);
if (isReportLoaded === false)
{
loadReport();
} else {
report.setAccessToken(event.data);
}
};
//load the report
async function loadReport() {
embedPowerBIReport();
await reportLoaded;
console.log("Report loaded");
await reportRendered;
console.log("Report Rendered");
isReportLoaded = true;
}