PowerBIClient does not have any value - powerbi

I am trying to read powerbi report using powerbi SDk. I got correct access token (tested in postman). Same access token is passed for TokenCredentials. but not getting any value in _powerBIClient. it has no value. I'm using below code.
var functionCred = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(_configuration[BotConfigurationEnum.FunctionClientId.GetDescription()], _configuration[BotConfigurationEnum.FunctionClientSecret.GetDescription()]);
var userAssertion = (!string.IsNullOrWhiteSpace(token)) ? new Microsoft.IdentityModel.Clients.ActiveDirectory.UserAssertion(token, "urn:ietf:params:oauth:grant-type:jwt-bearer") : null;
var context = new AuthenticationContext("https://login.microsoftonline.com/{tenentid}");
var res = await context.AcquireTokenAsync("https://analysis.windows.net/powerbi/api",
functionCred, userAssertion);
var tokenCredentials = new TokenCredentials(res.AccessToken, "Bearer");
var _powerBIClient = new PowerBIClient(new Uri("https://api.powerbi.com/"), tokenCredentials);
var pbiReport = _powerBIClient.Reports.GetReport(reportId);

there was small correction in my code.. I have removed '/' in new Uri and it worked. I know its silly..
var _powerBIClient = new PowerBIClient(new Uri("https://api.powerbi.com"), tokenCredentials);

Related

script.google.com error: TypeError: Cannot read property 'values' of undefined [duplicate]

This question already has answers here:
How can I test a trigger function in GAS?
(4 answers)
Closed 4 months ago.
I am trying to use a form submit to automatically complete a google doc template I have created. But I keep getting the error TypeError: Cannot read property 'values' of undefined, I am a complete newbie, can someone help me please?
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[0];
var EmailAddress = e.values[1];
var CallReportWriter = e.values[2];
var DateOfMeeting = e.values[3];
var NameOfTheCustomerProspect = e.values[4];
var ContactNameFromClient = e.values[5];
var TitleandContacts = e.values[6];
var PresentfromSesomo = e.values[7];
var MeetingObjectives = e.values[8];
var MeetingResult = e.values[9];
var Background = e.values[10];
var Opportunities = e.values[11];
var Followuprequired = e.values[12];
var Responsible = e.values[13];
var Targetdates = e.values[14];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('12zJTxLgy_Nmxk1FScyZ3vqHzYnKQ55ckdQ8RsYy-MdA');
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById('1SorfCjOGknFVt1ch39MJ7atBorLf_Sdr')
var copy = file.makeCopy(DateOfMeeting + ',' + NameOfTheCustomer/Prospect, CallReport);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('{{MeetingDate}}', DateOfMeeting);
body.replaceText('{{NameOfCustomer}}', NameOfTheCustomer/Prospect);
body.replaceText('{{ReportWriterName}}', CallReportWriter);
body.replaceText('{{ContactNameFromClient}}', ContactNameFromClient);
body.replaceText('{{TitleAndContact}}', TitleandContacts);
body.replaceText('{{PresentFromSesomo}}', PresentfromSesomo);
body.replaceText('{{MeetingObjectives}}', MeetingObjectives);
body.replaceText('{{MeetingResults}}', MeetingResult);
body.replaceText('{{Background}}', Background);
body.replaceText('{{Opportunities}}', Opportunities);
body.replaceText('{{FollowUpRequired}}', Followuprequired);
body.replaceText('{{Responsible}}', Responsible);
body.replaceText('{{TargetDate}}', Targetdates);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
Open the script editor of the sheet where the form response is landing
//
function onFormSubmit(e) {
var resp = e.source.getActiveSheet().getRange(e.range.rowStart,1, e.range.rowStart,14 ).getValues();
var timestamp = resp[0][0];
var EmailAddress = resp[0][1];
var CallReportWriter = resp[0][2];
var DateOfMeeting = resp[0][3];
// continue your code - I have not tested it
}
In script editor, you have to install the on-form-submit trigger
Then as soon as form response lands, your code will run and do whatever you want.

Has anyone got a guide on how to upgrade from PowerBi Embeded v2 to v3? Or a tutorial for v3?

This appears to be a nightmare, sure its easy to upgrade the nuget package to 3.11 I think the latest is, but then nothing at all compiles. So you fix the compile errors, and then it doesn't work. I'm getting an error when it tries to create the PowerBI client.
Getting the token and also creating the client appears to be totally different to v2.
This is my code:
public PowerBiConfig GetPowerBiConfig(string reportId)
{
var result = new PowerBiConfig();
try
{
if (!Guid.TryParse(reportId, out var _))
{
result.ErrorMessage = $"Invalid report guid: {reportId}";
return result;
}
var credential = new UserPasswordCredential(_powerBiProMasterUsername, _powerBiProMasterPassword);
var authenticationContext = new AuthenticationContext(AuthorityUrl);
// Taken from https://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously
var authenticationResult = authenticationContext.AcquireTokenAsync(ResourceUrl, dataArchiverSettings.PowerBiApplicationId, credential).GetAwaiter().GetResult();
if (authenticationResult == null)
{
result.ErrorMessage = "Authentication Failed.";
return result;
}
var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
using (var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials))
{
var report = client.Reports.GetReportInGroup(dataArchiverSettings.PowerBiWorkspaceId, reportId);
if (report == null)
{
result.ErrorMessage = $"No report with the ID {reportId} was found in the workspace.";
return result;
}
var datasets = client.Datasets.GetDatasetById(dataArchiverSettings.PowerBiWorkspaceId, report.DatasetId);
result.IsEffectiveIdentityRequired = datasets.IsEffectiveIdentityRequired;
result.IsEffectiveIdentityRolesRequired = datasets.IsEffectiveIdentityRolesRequired;
GenerateTokenRequest tokenRequest;
if (datasets.IsEffectiveIdentityRequired == true)
{
var username = UserHelper.GetCurrentUser();
var roles = _userService.GetRolesForUser(username);
tokenRequest = new GenerateTokenRequest(accessLevel: "view",
identities: new List<EffectiveIdentity>
{
new EffectiveIdentity(username: username,
roles: new List<string> (roles.Select(x=> x.RoleName)),
datasets: new List<string> {datasets.Id})
});
}
else
{
tokenRequest = new GenerateTokenRequest(accessLevel: "view");
}
var tokenResponse =
client.Reports.GenerateTokenInGroup(dataArchiverSettings.PowerBiWorkspaceId, report.Id,
tokenRequest);
if (tokenResponse == null)
{
result.ErrorMessage = "Failed to generate embed token.";
return result;
}
// Generate Embed Configuration.
result.EmbedToken = tokenResponse;
result.EmbedUrl = report.EmbedUrl;
result.Id = report.Id.ToString();
result.WorkloadResourceName = dataArchiverSettings.PowerBiWorkloadResourceName.Trim();
}
}
catch (HttpOperationException exc)
{
result.ErrorMessage =
$"Status: {exc.Response.StatusCode} ({(int)exc.Response.StatusCode})\r\n" +
$"Response: {exc.Response.Content}\r\n" +
$"RequestId: {exc.Response.Headers["RequestId"].FirstOrDefault()}";
}
catch (Exception exc)
{
result.ErrorMessage = exc.ToString();
}
return result;
}
The closest to "upgrade guide" is the announcement in Power BI blog. It looks like your code is using v2 (e.g. reportId is string, while in v3 it should be Guid).
Here is a brief summary of the changes:
What you should know about v3
Here are the key changes with this version update:
Namespaces renaming:
Microsoft.PowerBI.Api.V2 was changed to Microsoft.PowerBI.Api
Microsoft.PowerBI.Api.Extensions.V2 was changed to Microsoft.PowerBI.Api.Extensions
Microsoft.PowerBI.Api.V1 namespace was removed.
SetAllConnections and SetAllConnectionsInGroup operations are deprecated and marked as obsolete. You should use UpdateDatasources or UpdateParameters APIs instead.
PowerBI artifacts IDs typing was changed* from string to Guid, we recommend to work with Guid when possible.
*Dataset ID is an exception and it’s typing will remain string.
ODataResponse[List[Object]] types was changed to Objects, thus returning an objects collection on responses. For example, a response of ODataResponse[List[Report]] type will now return Reports collection as the return type.
New credentials classes allow easier build of credentialDetails. The new classes include: BasicCredentials, WindowsCredentials, OAuth2Credentials, and more.
Read Configure credentials article to learn more.
New encryption helper classes for easier encryption when creating CredentialDetails.
For example, using AsymmetricKeyEncryptor class with a gateway public key:
GatewayPublicKey publicKey = new GatewayPublicKey
{
Exponent = "...",
Modulus = "..."
};
CredentialsBase credentials = new BasicCredentials("<USER>", "<PASSWORD>");
var credentialsEncryptor = new AsymmetricKeyEncryptor(publicKey);
var credentialDetails = new CredentialDetails(credentials, PrivacyLevel.None, EncryptedConnection.Encrypted, credentialsEncryptor);
Read Configure credentials article to learn more.
Consistency on field names.
For example, reportKey, datasetKey, dashboardKey and tileKey was changed to reportId, datasetId, dashboardId and tileId.
Consistency on operations names.
For example, use GetDataset instead of GetDatasetById. The effected opertation names are imports, datasets, gateways and datasources.
Use enum class instead of string for enumerated types.
For example, In the generateTokenRequest, we recommend to use TokenAccessLevel.View, and not explicitly use “view” as value.
Required fields was marked – some fields was changed to required fields are not nullable anymore.
Examples
Change in Get Reports call if WorkspaceId is a string:
var reports = await client.Reports.GetReportsInGroupAsync(WorkspaceId);
var reports = await client.Reports.GetReportsInGroupAsync(new Guid( WorkspaceId ) );
Change in response handling if a string is expected:
report = reports.Value.FirstOrDefault(r => r.Id.Equals(ReportId, StringComparison.InvariantCultureIgnoreCase));
report = reports.Value.FirstOrDefault(r => r.Id .ToString() .Equals(ReportId, StringComparison.InvariantCultureIgnoreCase));
Change in Generate token:
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync(WorkspaceId, report.Id, generateTokenRequestParameters);
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync( new Guid( WorkspaceId ), report.Id, generateTokenRequestParameters);
Change in Generate token response handling if a string is expected:
m_embedConfig.Id = report.Id;
m_embedConfig.Id = report.Id .ToString() ;
Required fields are not nullable, i.e. Expiration is not nullable and the Value property should be removed:
var minutesToExpiration = EmbedToken.Expiration .Value – DateTime.UtcNow;
var minutesToExpiration = EmbedToken.Expiration – DateTime.UtcNow;
Consistency on operations names, i.e. use GetDataset instead of GetDatasetById:
var datasets = await client.Datasets.GetDataset ById InGroupAsync(WorkspaceId, report.DatasetId);
var datasets = await client.Datasets.GetDatasetInGroupAsync(new Guid(WorkspaceId), report.DatasetId);

PowerBI Embedded V2 Direct Query - Azure Sql credentials not updated programatically

I'm programatically creating a workspace and importing a Direct-Query report into PowerBI Embedded V2. Everything works fine except the report data source credentials which are not updated.
This is the code for the import flow:
await _powerBIService.ImportPbixAsync(newWorkspaceId, reportNameWithoutExtension, fileStream);
Console.WriteLine("Imported report {0} for {1} ({2})", reportNameWithoutExtension, persona.Name, persona.Id);
string datasetId = null;
while (datasetId == null)
{
//get DataSource Id
Thread.Sleep(5000);
datasetId = await _powerBIService.GetDatasetIdFromWorkspace(newWorkspaceId, reportNameWithoutExtension);
}
//update the connection details
await _powerBIService.UpdateConnectionAsync(newWorkspaceId, datasetId, persona.SQLUser, persona.SQLPassword);
Console.WriteLine("Updated connection details for dataset {0}", reportNameWithoutExtension);
// get gateway ID
var gatewayId = await _powerBIService.GetGatewayIdFromWorkspaceAndDataset(newWorkspaceId, datasetId);
if (gatewayId != null)
{
//update credentials
await _powerBIService.UpdateGatewayDatasourcesCredentials(gatewayId, persona.SQLUser, persona.SQLPassword);
Console.WriteLine("Updated connection details for gateway {0}", reportNameWithoutExtension);
}
These are the individual methods:
public async Task UpdateConnectionAsync(string workspaceId, string datasetId, string sqlUser, string sqlPwd)
{
var bearerToken = await GetBearerTokenAsync();
var tokenCredentials = new TokenCredentials(bearerToken, "Bearer");
using (var client = new PowerBIClient(new Uri(_pbiApiUrl), tokenCredentials))
{
var dataSourcesResponse = await client.Datasets.GetDatasourcesInGroupAsync(workspaceId, datasetId);
var sqlDataSources = dataSourcesResponse.Value?.Where(s => s.DatasourceType == "Sql")
.Where(s => !s.ConnectionString.Contains(_reportsSQLServer) || !s.ConnectionString.Contains(_reportsSQLServer));
foreach (var sqlDataSource in sqlDataSources)
{
var updateDataSourceConnectionRequest = new UpdateDatasourceConnectionRequest();
updateDataSourceConnectionRequest.ConnectionDetails = new DatasourceConnectionDetails(_reportsSQLServer, _reportsSQLDatabase);
//updateDataSourceConnectionRequest.DatasourceSelector = new Datasource(datasourceId: sqlDataSource.DatasourceId);
var datasourcesRequest = new UpdateDatasourcesRequest();
datasourcesRequest.UpdateDetails = new List<UpdateDatasourceConnectionRequest>() { updateDataSourceConnectionRequest };
var result = await client.Datasets.UpdateDatasourcesInGroupAsync(workspaceId, datasetId, datasourcesRequest);
//await client.Gateways.UpdateDatasourceAsync()
}
}
}
public async Task UpdateGatewayDatasourcesCredentials(string gatewayId, string sqlUser, string sqlPassword)
{
var bearerToken = await GetBearerTokenAsync();
var tokenCredentials = new TokenCredentials(bearerToken, "Bearer");
using (var client = new PowerBIClient(new Uri(_pbiApiUrl), tokenCredentials))
{
var datasourcesResult = await client.Gateways.GetDatasourcesAsync(gatewayId);
var sqlGatewayDatasources = datasourcesResult.Value?
.Where(s => s.DatasourceType == "Sql")
.Where(s => s.ConnectionDetails.Contains(_reportsSQLServer) && s.ConnectionDetails.Contains(_reportsSQLDatabase));
foreach (var gatewayDatasource in sqlGatewayDatasources)
{
var updateDataSourceRequest = new UpdateDatasourceRequest();
updateDataSourceRequest.CredentialDetails = new CredentialDetails();
updateDataSourceRequest.CredentialDetails.CredentialType = "Basic";
updateDataSourceRequest.CredentialDetails.Credentials = "{\"credentialData\":[{\"name\":\"username\", \"value\":\"" + sqlUser + "\"},{\"name\":\"password\", \"value\":\"" + sqlPassword + "\"}]}";
updateDataSourceRequest.CredentialDetails.EncryptedConnection = "Encrypted";
updateDataSourceRequest.CredentialDetails.EncryptionAlgorithm = "None";
updateDataSourceRequest.CredentialDetails.PrivacyLevel = "None";
var result = await client.Gateways.UpdateDatasourceAsync(gatewayId, gatewayDatasource.Id, updateDataSourceRequest);
}
}
}
When I attempt to view the report there is no data being fetched from the database.
Interestingly I can login manually using the PowerBI portal using the same credentials. After the manual login the report is fetching data.
If after the manual login I delete the workspace and then recreate and re-import the report programatically then the report will show data. This makes me think there is some caching involved after the manual logic which is persisted outside of the workspace.
Any suggestions on what is missing here?

Using GP Web Service: How do I delete a SalesOrderLine from a SalesOrder?

Can someone help me delete a SalesOrderLine from a SalesOrder?
I'm using the GP WS Native Endpoint
My code executes without an error.
However, after updating the SalesOrder, the line I removed still remains.
var salesDocumentKeyObject = new SalesDocumentKey {Id = salesDocumentKey, CompanyKey = this.CompanyKey};
var salesOrder = this.DynamicsGpClient.GetSalesOrderByKey(salesDocumentKeyObject, this.Context);
var newLines = salesOrder.Lines.Where(l => l.Key.LineSequenceNumber != lineItemSequence).ToArray();
salesOrder.Lines = newLines;
var salesOrderUpdatePolicy = this.DynamicsGpClient.GetPolicyByOperation("UpdateSalesOrder", this.Context);
this.DynamicsGpClient.UpdateSalesOrder(salesOrder, this.Context, salesOrderUpdatePolicy);
Thank you for any help,
Karl
Microsoft Dynamics GP Web Service has poor documentation and examples.
Do delete a sales order line, you retrieve the order, locate the line to delete and set the sales order line property, DeleteOnUpdate to true, and then save the order.
Updated code below:
var salesDocumentKeyObject = new SalesDocumentKey {Id = salesDocumentKey, CompanyKey = this.CompanyKey};
var salesOrder = this.DynamicsGpClient.GetSalesOrderByKey(salesDocumentKeyObject, this.Context);
var target= salesOrder.Lines.FirstOrDefault(l => l.Key.LineSequenceNumber == lineItemSequence);
if (target != null) {
target.DeleteOnUpdate = true;
}
var salesOrderUpdatePolicy = this.DynamicsGpClient.GetPolicyByOperation("UpdateSalesOrder", this.Context);
this.DynamicsGpClient.UpdateSalesOrder(salesOrder, this.Context, salesOrderUpdatePolicy);

RhinoMocks - Unable to cast type foo to type bar when using non static Repository

I am trying out RhinoMocks (only just started, please be gentle!) and am confused as to why if I use this setup:
var mockRepos = new MockRepository();
var mockServiceProvider = mockRepos.DynamicMock<IServiceProvider>(null);
var mockContext = mockRepos.DynamicMock<IPluginExecutionContext>(null);
mockServiceProvider.Expect(x => x.GetService(typeof(IPluginExecutionContext))).Return(mockContext);
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
At some point in SomeClassOrOther, the method GetService is called
var context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
and causes the exception:
Unable to cast object of type 'Castle.Proxies.ObjectProxyd0bf4b879a6341bbba3478cf1189d621' to type 'Microsoft.Xrm.Sdk.IPluginExecutionContext'.
However if I use:
var mockServiceProvider = MockRepository.GenerateMock<IServiceProvider>(null);
var mockContext = MockRepository.GenerateMock<IPluginExecutionContext>(null);
mockServiceProvider.Expect(x => x.GetService(typeof(IPluginExecutionContext))).Return(mockContext);
i.e. the static MockRepository.GenerateMock and it executes with no errors.
Where am I going wrong here?
I don't know why you are getting this very exception. However, when using the "old" non-static syntax, you get mocks in "Record" state. Before using them, you need to set them in Replay mode:
var mockRepos = new MockRepository();
var mockServiceProvider = mockRepos.DynamicMock<IServiceProvider>();
var mockContext = mockRepos.DynamicMock<IPluginExecutionContext>();
mockRepos.ReplayAll();
mockServiceProvider
.Stub(x => x.GetService(typeof(IPluginExecutionContext)))
.Return(mockContext);
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
You are mixing new AAA syntax and old Record/Replay syntax. When you are using Expect extension method and new AAA syntax, you don't need to operate on mocks repository and change its mode manually(Record/Replay). Create mocks with new static methods (which create mocks in replay mode) and verify expectations later:
// Arrange
var mockServiceProvider = MockRepository.GenerateMock<IServiceProvider>();
var mockContext = MockRepository.GenerateMock<IPluginExecutionContext>();
mockServiceProvider.Expect(x => x.GetService(typeof(IPluginExecutionContext)))
.Return(mockContext);
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
// Act
// Assert
mockServiceProvider.VerifyAllExpectations();
Here is old Record/Replay syntax for comparison:
var mockRepos = new MockRepository();
var mockServiceProvider = mockRepos.DynamicMock<IServiceProvider>();
var mockContext = mockRepos.DynamicMock<IPluginExecutionContext>();
Expect.Call(mockServiceProvider.GetService(typeof(IPluginExecutionContext)))
.Return(mockContext);
mockRepos.ReplayAll();
var someSampleClass = new SomeClassOrOther(mockServiceProvider);
// exercise your SUT
mockRepos.VerifyAll();