Error generating embed token for editable Power BI Embedded dashboard - powerbi

I am using a Powershell script to generate an embed token for a Power BI dashboard:
Login-PowerBI
$url = "https://api.powerbi.com/v1.0/myorg/groups/395ce617-f2b9-xyz/dashboards/084c9cc4-xyz/GenerateToken"
$body = "{ 'accessLevel': 'View' }"
$response = Invoke-PowerBIRestMethod -Url $url -Body $body -Method Post -ErrorAction "Stop"
$response
$json = $response | ConvertFrom-Json
$json.token
This works, however I was hoping to make the dashboard editable by changing the accessLebel like this:
$body = "{ 'accessLevel': 'Edit' }"
Instead of generating a token, an error is thrown indicating Bad Request, but with no other detail. How can I determine how the request should be created? Are dashboards even editable like reports are? (I can generate edit tokens for reports with no issue) I can't find a code sample for that, and I note the online sample doesn't allow you to edit dashboards like you are able to with reports: https://microsoft.github.io/PowerBI-JavaScript/demo/v2-demo/index.html

You got the error Bad request because accessLevel: Edit is not supported for dashboards.
The accessLevel supported for Generate EmbedToken for dashboard in the group is only View.
Create and Edit accessLevel is available only for reports.
Refer to this link: https://learn.microsoft.com/en-us/rest/api/power-bi/embedtoken/dashboards_generatetokeningroup#tokenaccesslevel
You can use the Try it feature there to see how the REST API calls are made.

Related

how to invoke web url from power bi

I am trying to invoke web url from Power BI using Web data source in Power BI. But I'm unable to achieve.
We have requirement that users can click button on power bi so it should trigger or invoke web url in the backed there is another job or task will trigger when user click on button.
I'm doing invoke web url from powershell using below code, so want to get this to be from power bi.
$data =#{
"emailId"= "xxxxxxx"
"emailSubject"= "xxxxx"
"emailBody"= "xxxxx"
}
$json = $data | ConvertTo-Json
$LogicAppsUrl = "https://prod-83.westeurope.logic.azure.com:443/workflows/xxxxxxx"
$LogicAppInfo = Invoke-WebRequest -Uri $LogicAppsUrl -Headers #{
"Content-Type" = "application/json"
} -Method Post -Body $json -UseBasicParsing
Write-Host "Trigger Complete"
How can we achieve same using Power BI? Is there any option we can do ?
Please suggest any alternative plans to do from power bi.
Thanks,
Brahma

Power BI Workspace - dataset credential patch receives 401 Unauthorized

I am trying to programatically deploy a Power BI Report and dataset from one workspace to another, using a mix of PowerShell and the PowerBI REST API. In the new workspace, I am updating a dataset parameters to point to a new DB name.
The dataset is pointed to an Azure SQL DB, and in my DEV workspace (the source for the clone), the dataset passes the accessing user's credential through to the DB.
I am authenticating with a Service Principal that I created and then added to the dataset as an Administrator.
This is the PowerShell code that I wrote to do this:
$config = gc .\EnvConfig.json -raw | ConvertFrom-Json
$envSettings = $config.Dev
$toEnvSettings = $config.QA
# Convert to SecureString
[securestring]$secStringPassword = ConvertTo-SecureString $config.ServicePrincipalSecret -AsPlainText -Force
$userId = "$($config.ServicePrincipalId)#$($config.ServicePrincipalTenant)"
[pscredential]$credObject = New-Object System.Management.Automation.PSCredential ($userId, $secStringPassword)
Connect-PowerBIServiceAccount -Tenant $config.ServicePrincipalTenantName -ServicePrincipal -Credential $credObject
Get-PowerBIReport -WorkspaceId $envSettings.PBIWorkspaceId | ForEach-Object {
$filename ="c:\temp\$($_.Name).pbix"
Remove-Item $filename
Invoke-PowerBIRestMethod -Method GET `
-Url "https://api.powerbi.com/v1.0/myorg/groups/$($envSettings.PBIWorkspaceId)/reports/$($_.Id)/Export" `
-ContentType "application/zip" -OutFile $filename
New-PowerBIReport -WorkspaceId $toEnvSettings.PBIWorkspaceId -ConflictAction CreateOrOverwrite -Path $filename
}
$datasets = Get-PowerBIDataset -WorkspaceId $toEnvSettings.PBIWorkspaceId
$datasetId = $datasets[0].Id
$updateDBParam = "{`"updateDetails`": [ { `"name`": `"DBName`", `"newValue`": `"$($toEnvSettings.DBName)`" }]}"
$updateUri = "https://api.powerbi.com/v1.0/myorg/groups/$($toEnvSettings.PBIWorkspaceId)/datasets/$datasetId/Default.UpdateParameters"
Invoke-PowerBIRestMethod -Method POST -Url $updateUri -Body $updateDBParam
When I have cloned the report and dataset, when I open the report in the new workspace I see an error that the dataset does not have credentials:
If I take over this dataset with my personal login, then the report loads. This is not sufficient, I want to set the credential to pass through the user's id programatically.
I found this discussion on the PowerBI site, where they say you can use the dataset ID and gateway ID from the dataset, and send a PATCH request to https://api.powerbi.com/v1.0/myorg/gateways/[gateway id]/datasources/[datasource id]
I suspect that is only relevant to "My Workspace" datasets, not datasets in a workspace.
When I try and send that patch request with a gateway and datasource ID that I got from performing a GET on https://api.powerbi.com/v1.0/myorg/groups/[workspace id]/datasets/[dataset id]/datasources, I get a 401 error. I have tried posting with my own PowerBI Tenant Admin login, as well as with an Admin app I created through the PowerBI app registration tool, and also I added a tenant level PowerBI Read / Write permission in the AAD portal for my service principal. Nothing works, I keep getting a 401.
Two questions:
Can I set the credentials on a dataset in a workspace?
If not, how can I clone the dataset between workspaces so that it has the credential passthrough to start with?
#Joon: I wanted to leave a comment but am not allowed. I'm in the same boat with getting the 401 errors. But I'm not following your resolution; did you change any logic, or you changed the user account being used? We're using the PBI AAD account that is the Admin of that workspace where dataset resides. Here's the code I'm using, which is based on this: https://martinschoombee.com/2020/10/20/automating-power-bi-deployments-change-data-source-credentials/
$ApiRequestBody = #"
{
"credentialDetails": {
"credentialType": "Basic",
"credentials": "{\"credentialData\":[{\"name\":\"username\", \"value\":\"$FormattedDataSourceUser\"},{\"name\":\"password\", \"value\":\"$FormattedDataSourcePassword\"}]}",
"encryptedConnection": "Encrypted",
"encryptionAlgorithm": "None",
"privacyLevel": "None"
}
}
"#
#. . . (tried other values for "privacyLevel")
#Update username & password
Invoke-PowerBIRestMethod -Url $ApiUrl -Method Patch -Body ("$ApiRequestBody")
Solved the problem.
The 401 error was originating from the credential I was posting itself, not from me not having permissions to post. I was using the OAuth credential method, and the token I was passing was invalid. The response from the PowerBI API is just a bare 401 error, nothing tells the user that the problem is that the API validated the OAuth token and that failed.
I tested with an invalid basic credential, and in that case you get a 400 Bad Request error, which makes more sense.

Using PowerShell to send data from SQL Server to service via SOAP

I'm fairly new to PowerShell and brand new (as in, today) to web services and SOAP. A vendor gave us documentation on their web service API that allows the creation of user accounts. I'm trying to use PowerShell to pull our users from SQL Server and send the data to their service. We will need to add users on an ongoing basis.
Below is a pared-down version of what I came up with and it actually seems to work; the vendor told me to include a dry_run parameter while testing and I'm getting a dry_run_success from the response_type.
My question is: Is this even close to being the appropriate way to do it with PowerShell?
# Open ADO.NET Connection to database
$dbConn = New-Object Data.SqlClient.SqlConnection;
$dbConn.ConnectionString = "Data Source=mydbserver;User ID=someuserid;Password=mypassword;Initial Catalog=mydatabase";
$dbConn.Open();
$sql = "select * from mytable";
$dbSqlCmd = New-Object Data.SqlClient.SqlCommand $sql, $dbConn;
$dbRd = $dbSqlCmd.ExecuteReader();
# Create a Web Service Proxy
$proxy = New-WebServiceProxy -Uri https://somedomain.com/service/wsdl
$namespace = $proxy.GetType().NameSpace
$param = New-Object($namespace + ".somemethod")
# Loop through records from SQL and invoke the web service
While ($dbRd.Read())
{
$param.user_id = $dbRd.GetString(0)
$param.password = $dbRd.GetString(1)
$param.display_name = $dbRd.GetString(2)
$request = $proxy.TheMethod($param)
if ($request.response_type -eq 'error')
{
$request.error.extended_error_text
}
}
# Clean up
$dbRd.Close();
$dbSqlCmd.Dispose();
$dbConn.Close();
A couple things you could improve:
Don't use select * in your SQL queries. Always specify the fields you need, in the order you need. As written, if someone were to restructure the table such that the user ID wasn't the first column, you'd have a mess on your hands because you're accessing the fields by their ordinal number
You're apparently storing those passwords in plaintext in your database. Anyone with access to your database knows the credentials for every one of your users. This is a very bad thing. Resolving this could be a very big discussion.
Your code keeps the database connection open until the script completes. Given the scope here, it's probably not going to cause a major problem, but your database access strategy should be to get in, get your data, get out & disconnect as quickly as possible.
$sql = "select user_id, password, display_name from mytable";
$QueryCmd = $dbConn();
$QueryCmd.CommandText = $sql;
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$QueryCmd.Connection = $dbConn;
$SqlAdapter.SelectCommand = $QueryCmd;
$DataSet = New-Object System.Data.DataSet;
$SqlAdapter.Fill($DataSet)
$dbConn.Close();
$dbConn.Dispose();
$MyResults = $DataSet.Tables[0];
$MyResults | foreach-object {
$param.user_id = $_.user_id;
$param.password = $_.password;
$param.display_name = $_.display_name;
$request = $proxy.TheMethod($param);
if ($request.response_type -eq 'error')
{
$request.error.extended_error_text;
}
}

Is there an API to force Facebook to scrape a page again?

I'm aware you can force update a page's cache by entering the URL on Facebook's debugger tool while been logged in as admin for that app/page:
https://developers.facebook.com/tools/debug
But what I need is a way to automatically call an API endpoint or something from our internal app whenever somebody from our Sales department updates the main image of one of our pages. It is not an option to ask thousands of sales people to login as an admin and manually update a page's cache whenever they update one of our item's description or image.
We can't afford to wait 24 hours for Facebook to update its cache because we're getting daily complaints from our clients whenever they don't see a change showing up as soon as we change it on our side.
Page metadata isn't the sort of thing that should change very often, but you can manually clear the cache by going to Facebook's Debug Tool and entering the URL you want to scrape
There's also an API for doing this, which works for any OG object:
curl -X POST \
-F "id={object-url OR object-id}" \
-F "scrape=true" \
-F "access_token={your access token}" \
"https://graph.facebook.com"
An access_token is now required. This can be an app or page access_token; no user authentication is required.
If you'd like to do this in PHP in a with-out waiting for a reply, the following function will do this:
//Provide a URL in $url to empty the OG cache
function clear_open_graph_cache($url, $token) {
$vars = array('id' => $url, 'scrape' => 'true', 'access_token' => $token);
$body = http_build_query($vars);
$fp = fsockopen('ssl://graph.facebook.com', 443);
fwrite($fp, "POST / HTTP/1.1\r\n");
fwrite($fp, "Host: graph.facebook.com\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: ".strlen($body)."\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");
fwrite($fp, $body);
fclose($fp);
}
If you're using the javascript sdk, the version of this you'd want to use is
FB.api('https://graph.facebook.com/', 'post', {
id: [your-updated-or-new-link],
scrape: true
}, function(response) {
//console.log('rescrape!',response);
});
I happen to like promises, so an alternate version using jQuery Deferreds might be
function scrapeLink(url){
var masterdfd = $.Deferred();
FB.api('https://graph.facebook.com/', 'post', {
id: [your-updated-or-new-link],
scrape: true
}, function(response) {
if(!response || response.error){
masterdfd.reject(response);
}else{
masterdfd.resolve(response);
}
});
return masterdfd;
}
then:
scrapeLink([SOME-URL]).done(function(){
//now the link should be scraped/rescraped and ready to use
});
Note that the scraper can take varying amounts of time to complete, so no guarantees that it will be quick. Nor do I know what Facebook thinks about repeated or automated usages of this method, so it probably pays to be judicious and conservative about using it.
This is a simple ajax implementation. Put this on any page you want facebook to scrape immediately;
var url= "your url here";
$.ajax({
type: 'POST',
url: 'https://graph.facebook.com?id='+url+'&scrape=true',
success: function(data){
console.log(data);
}
});
An alternative solution from within a Drupal node update using curl could be something like this :
<?php
function your_module_node_postsave($node) {
if($node->type == 'your_type') {
$url = url('node/'.$node->nid,array('absolute' => TRUE));
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://graph.facebook.com/v1.0/?id='. urlencode($url). '&scrape=true');
$auth_header = 'Oauth yOUR-ACCESS-TOKEn';
curl_setopt($ch, CURLOPT_HTTPHEADER, array($auth_header));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$r = curl_exec($ch);
curl_close ($ch);
}
}
Notice the hook_node_postsave() implementation which is not standard Drupal core supported.
I had to use www.drupal.org/project/hook_post_action in order to get this facebook scrape pickup last made changes to the node, since hook_node_update() is not triggered after databases have been updated.
Facebook requires now the access token in order to get this done.
Guidelines to acquire a token can be found here :
https://smashballoon.com/custom-facebook-feed/access-token/
I'm the author of Facebook Object Debugger CLI, a command-line interface written in PHP, aim to refresh the Facebook cache for a single URL or a bunch of URLS using as input a text file. The package is also available on Packagist and can be installed using Composer.
There are changes in Graph API v2.10:
When making a GET request against a URL we haven't scraped before, we will also omit the og_object field. To trigger a scrape and populate the og_object, issue a POST /{url}?scrape=true. Once scraped, the og_object will remain cached and returned on all future read requests.
We will require an access token for these requests in all versions of the Graph API beginning October 16, 2017.
Source: Introducing Graph API v2.10
So now we should use POST-method for scraping:
POST /{url}?scrape=true
Not
A solution with the PHP Facebook SDK:
<?php
try {
$params = [
'id' => 'https://www.mysitetoscrape.com/page',
'scrape' => 'true',
];
$response = $fb->post('/', $params);
print_r($response);
} catch(\Facebook\Exceptions\FacebookResponseException $e) {
// When Graph returns an error
echo 'Graph returned an error: ' . $e->getMessage();
} catch(\Facebook\Exceptions\FacebookSDKException $e) {
// When validation fails or other local issues
echo 'Facebook SDK returned an error: ' . $e->getMessage();
}
?>
Here's my Ruby solution using Koala gem and Facebook API v2.9
api = Koala::Facebook::API.new(access_token)
response = api.put_object(nil, nil, {scrape: true, id: "url-of-page-to-scrape"})
response should be a hash of attributes retrieved from the og: meta tags on the page which was scraped.
I was facing this same problem.
There is a simple way to clear cache.
http://developers.facebook.com/tools/debug
Enter the URL following by fbrefresh=CAN_BE_ANYTHING
Examples: http://www.example.com?fbrefresh=CAN_BE_ANYTHING

create events on my Application's page

I am in a process of writing an app which works this way
1. Asks a user permission for events_create, publish_stream
2. Gets the access_token for the same
3. takes input of Title, date, time, location( ie all inputs) from a form and then calls graph api like
/$app_id/events along with access token and post date
I want this to create an event within my APPLICATION's events and hence I use $app_id instead of ..../me/events
But it creates an event on USER's wall ???
I want users to be able to create an event on my wall. It should be an event whose owner is the application and the user should get invited along with his contacts( if he chooses so) ...
Basically, I think POST to /$app_id/events and /me/events work the same, I think it should not ... Am I missing something?
Thanks in advance ..
AC
HiShakyeb,
Same result ... It just creates an event in my account rather than the application's.
Here is the code I am using ..
$url = "https://graph.facebook.com/$app/events?" . $access_token;
$params = array();
// Prepare Event fields
foreach($_POST as $key=>$value)
if(strlen($value))
$params[$key] = $value;
$params['name']=mysql_real_escape_string($params['name']);
$params['description']=mysql_real_escape_string($params['description']);
$params['location']=mysql_real_escape_string($params['location']);
$params['end_time']=date('Y-m-d H:i:s', mktime(0, 0, 0, date("m") , date("d")+1, date("Y")));
$params['privacy_type']="SECRET";
// Start the Graph API call
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$result = curl_exec($ch);
$decoded = json_decode($result, true);
curl_close($ch);
I also tried just app/events? instead of $app/events?
Thanks a lot ...
Use a Page Admin access token to take an action on behalf of that application's page. To do this, get /me/accounts with the manage_pages permission. That will give you the pages you have access to (including the app's profile page), along with an access token to use for acting as that page/application. If you use that access token, then events will be created as the application page and not the user. See the Graph API documentation for the Page object for more details.
With PHP-SDK I uses the api call for automatic POSTING an Event in App page after filling a form.
Here the automatic process after the user (You) have an valid APP access_token and manage app/page permissions. The token to use is your APP token you receive with your FBapp connection.
Starting creating php-sdk object
$connectparam = array();
$connectparam['appId'] = 'YOURAPPID';
$connectparam['secret'] = 'YOURAPPSECRET';
$connectparam['fileUpload'] = 'true';
$connectparam['cookie'] = 'true';
$facebook = new Facebook($this->connectparam);
Building the event informations
$FBcreateAnTestEvent = array();
$nowtime = date('Y-m-d H:i:s');
$FBcreateAnTestEvent['name'] = 'Test Event in myApp from me';
$FBcreateAnTestEvent['start_time'] = $nowtime;
$FBcreateAnTestEvent['privacy_type'] = 'SECRET';
$FBcreateAnTestEvent['description'] = 'This Event is created from my WebFBApp and is a testing Event creating by Qtronik from myWebApp';
Automaticly create event
$resp = '<a href="'.$facebook->api('/me/events','POST',$FBcreateAnTestEvent).'">
Create a test event</a>';
echo $resp;
When is echoing the php-sdk create immediately the Event so it's better to put it in a ajax or in action linked page in a form
For FanPage use FQL link
If You parse the FBuser admin_pages and filter by APPLICATION field and/or filter by your FBpage id you can get this user FBpages admin access_token's. Of Course you need the manage_pages permissions. Then you need admin_events permission to publish to FBfanPage's. I create an FanPageEvent with GraphApi link like this:
$pageAccessToken = $facebook->api('/' . $YOURPAGEID . '/?fields=access_token');
$resp = '<a href="https://graph.facebook.com/'.$pageAccessToken['id'].'/events
?access_token=' . $pageAccessToken['access_token'] . '
&method=POST
&name='.$FBcreateAnTestEvent['name'].'
&start_time='.$FBcreateAnTestEvent['start_time'].'
&location='.$FBcreateAnTestEvent['location'].')">
Create a test event</a>';
Then voila I have allot of other things to do with this for handle it with Ajax but this is the two way of adding an Event's to Facebook from a website administrating way... All this it's not very intended for personal profile event create so it's more difficult to spam FBpages and or FBapp/FBwebapp.
I'm French speaking so excuse my poor English... ;)