Skip to content

Commit d42c529

Browse files
Improve discovery of PBI cloud environments (#958)
Refactor PBICloudConfigurationService to improve cloud environment discovery.
1 parent cbc07b7 commit d42c529

42 files changed

Lines changed: 596 additions & 717 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Bravo.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Copyright>SQLBI Corporation</Copyright>
1313
<RootNamespace>Sqlbi.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
1414
<Nullable>enable</Nullable>
15+
<LangVersion>14.0</LangVersion>
1516
<GenerateDocumentationFile>True</GenerateDocumentationFile>
1617
<NoWarn>$(NoWarn);1591</NoWarn>
1718
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>

src/Controllers/AuthenticationController.cs

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,9 @@
22
{
33
using Microsoft.AspNetCore.Http;
44
using Microsoft.AspNetCore.Mvc;
5-
using Sqlbi.Bravo.Infrastructure;
6-
using Sqlbi.Bravo.Infrastructure.Configuration;
7-
using Sqlbi.Bravo.Infrastructure.Models.PBICloud;
85
using Sqlbi.Bravo.Infrastructure.Services.PowerBI;
9-
using Sqlbi.Bravo.Models;
6+
using Sqlbi.Bravo.Models.Authentication;
107
using Sqlbi.Bravo.Services;
11-
using System.Collections.Generic;
12-
using System.Linq;
13-
using System.Net.Mime;
14-
using System.Threading;
15-
using System.Threading.Tasks;
168

179
/// <summary>
1810
/// Authentication controller
@@ -21,56 +13,67 @@
2113
[Route("auth/[action]")]
2214
[ApiController]
2315
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))]
24-
public class AuthenticationController : ControllerBase
16+
public sealed class AuthenticationController(
17+
IPBICloudAuthenticationService pbicloudAuthenticationService,
18+
IPBICloudService pbicloudService,
19+
IAuthenticationService authenticationService) : ControllerBase
2520
{
26-
private readonly IAuthenticationService _authenticationService;
27-
private readonly IPBICloudService _pbicloudService;
28-
29-
public AuthenticationController(IAuthenticationService authenticationService, IPBICloudService pbicloudService)
30-
{
31-
_authenticationService = authenticationService;
32-
_pbicloudService = pbicloudService;
33-
}
21+
private readonly IAuthenticationService _authenticationService = authenticationService;
22+
private readonly IPBICloudAuthenticationService _pbicloudAuthenticationService = pbicloudAuthenticationService;
23+
private readonly IPBICloudService _pbicloudService = pbicloudService;
3424

3525
/// <summary>
36-
/// TODO
26+
/// Returns the list of available PowerBI cloud environments for the specified email account.
3727
/// </summary>
3828
/// <response code="200">Status200OK - Success</response>
3929
[HttpGet]
40-
[ActionName("powerbi/GetEnvironments")]
30+
[ActionName("GetEnvironments")]
4131
[Produces(MediaTypeNames.Application.Json)]
42-
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<IPBICloudEnvironment>))]
32+
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(GetEnvironmentsResponse))]
4333
[ProducesDefaultResponseType]
44-
public async Task<IActionResult> GetPBICloudEnvironmentsAsync(string userPrincipalName, CancellationToken cancellationToken)
34+
public async Task<IActionResult> GetEnvironmentsAsync(
35+
[FromQuery] GetEnvironmentsRequest request,
36+
CancellationToken cancellationToken)
4537
{
46-
var environments = await _authenticationService.GetPBICloudEnvironmentsAsync(userPrincipalName, cancellationToken);
47-
return Ok(environments);
38+
var environments = await _pbicloudAuthenticationService.GetEnvironmentsAsync(
39+
request.Email,
40+
cancellationToken);
41+
42+
var response = new GetEnvironmentsResponse(environments);
43+
return Ok(response);
4844
}
4945

5046
/// <summary>
5147
/// Attempts to authenticate and acquire an access token for the account to access the PowerBI cloud services
5248
/// </summary>
5349
/// <response code="200">Status200OK - Success</response>
5450
[HttpPost]
55-
[ActionName("powerbi/SignIn")]
51+
[ActionName("SignIn")]
5652
[Produces(MediaTypeNames.Application.Json)]
57-
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IBravoAccount))]
53+
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SignInResponse))]
5854
[ProducesDefaultResponseType]
59-
public async Task<IActionResult> PBICloudSignInAsync(PBICloudAuthenticationRequest request, CancellationToken cancellationToken)
55+
public async Task<IActionResult> SignInAsync(
56+
SignInRequest request,
57+
CancellationToken cancellationToken)
6058
{
61-
await _authenticationService.PBICloudSignInAsync(request.UserPrincipalName!, request.Environment!, cancellationToken);
62-
return Ok(_authenticationService.PBICloudAuthentication.Account);
59+
await _authenticationService.PBICloudSignInAsync(
60+
request.Email,
61+
request.Environment.ToModel(),
62+
cancellationToken);
63+
64+
var response = new SignInResponse(_authenticationService.PBICloudAuthentication.Account);
65+
return Ok(response);
6366
}
6467

6568
/// <summary>
6669
/// Clear the token cache for all the accounts
6770
/// </summary>
6871
/// <response code="200">Status200OK - Success</response>
6972
[HttpGet]
70-
[ActionName("powerbi/SignOut")]
73+
[ActionName("SignOut")]
7174
[ProducesResponseType(StatusCodes.Status200OK)]
7275
[ProducesDefaultResponseType]
73-
public async Task<IActionResult> PBICloudSignOutAsync(CancellationToken cancellationToken)
76+
public async Task<IActionResult> SignOutAsync(CancellationToken cancellationToken)
7477
{
7578
await _authenticationService.PBICloudSignOutAsync(cancellationToken);
7679
return Ok();
@@ -89,7 +92,7 @@ public async Task<IActionResult> PBICloudSignOutAsync(CancellationToken cancella
8992
[ProducesResponseType(StatusCodes.Status404NotFound)]
9093
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
9194
[ProducesDefaultResponseType]
92-
public async Task<IActionResult> GetPBICloudAccountAvatarAsync(CancellationToken cancellationToken)
95+
public async Task<IActionResult> GetUserAvatarAsync(CancellationToken cancellationToken)
9396
{
9497
if (await _authenticationService.IsPBICloudSignInRequiredAsync(cancellationToken))
9598
return Unauthorized();

src/Infrastructure/AppEnvironment.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ internal static class AppEnvironment
5555
// NBSP char instead of whitespace - Latvian/lv
5656
"\u00A0\u2014 Power BI Desktop",
5757
};
58-
public static readonly TimeSpan MSALSignInTimeout = TimeSpan.FromMinutes(5);
5958
public static readonly Color ThemeColorDark = ColorTranslator.FromHtml("#202020");
6059
public static readonly Color ThemeColorLight = ColorTranslator.FromHtml("#F3F3F3");
6160
public static readonly DaxLineBreakStyle FormatDaxLineBreakDefault = DaxLineBreakStyle.InitialLineBreak;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Sqlbi.Bravo.Infrastructure.Contracts.PBICloud
2+
{
3+
using System.Text.Json.Serialization;
4+
5+
[DebuggerDisplay("{Name}")]
6+
internal sealed class CloudEnvironmentClientContract
7+
{
8+
[JsonPropertyName("name")]
9+
public string Name { get; set; } = null!;
10+
11+
[JsonPropertyName("appId")]
12+
public string AppId { get; set; } = null!;
13+
14+
[JsonPropertyName("redirectUri")]
15+
public string RedirectUri { get; set; } = null!;
16+
}
17+
18+
internal static class CloudEnvironmentClientContractExtension
19+
{
20+
public static bool IsPowerBIDesktop(this CloudEnvironmentClientContract client)
21+
=> client.Name.Equals("powerbi-desktop", StringComparison.Ordinal);
22+
}
23+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace Sqlbi.Bravo.Infrastructure.Contracts.PBICloud
2+
{
3+
using System.Text.Json.Serialization;
4+
5+
[DebuggerDisplay("{CloudName}")]
6+
internal sealed class CloudEnvironmentContract
7+
{
8+
[JsonPropertyName("cloudName")]
9+
public string CloudName { get; set; } = null!;
10+
11+
[JsonPropertyName("clients")]
12+
public CloudEnvironmentClientContract[] Clients { get; set; } = null!;
13+
14+
[JsonPropertyName("services")]
15+
public CloudEnvironmentServiceContract[] Services { get; set; } = null!;
16+
}
17+
18+
internal static class CloudEnvironmentContractExtension
19+
{
20+
public static string GetDescription(this CloudEnvironmentContract environment) => environment.CloudName switch
21+
{
22+
"GlobalCloud" => "Power BI",
23+
"ChinaCloud" => "Power BI operated by 21Vianet in China",
24+
"USGovCloud" => "Power BI for US Government", // gcc
25+
"USGovDoDL4Cloud" => "Power BI for US Government (L4)", // gcc_high
26+
"USGovDoDL5Cloud" => "Power BI for US Government (L5)", // gcc_dod
27+
_ => environment.CloudName,
28+
};
29+
30+
public static bool IsMicrosoftInternalCloud(this CloudEnvironmentContract environment)
31+
=> s_microsoftInternalClouds.Contains(environment.CloudName);
32+
33+
private readonly static HashSet<string> s_microsoftInternalClouds = new(StringComparer.OrdinalIgnoreCase)
34+
{
35+
"OneBox",
36+
"DAILY",
37+
"Int3",
38+
"PpeCloud", // edog
39+
"DXT"
40+
};
41+
}
42+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Sqlbi.Bravo.Infrastructure.Contracts.PBICloud
2+
{
3+
using System.Text.Json.Serialization;
4+
5+
// Sample response from the discover API:
6+
// Invoke-RestMethod -Method POST -Uri "https://api.powerbi.com/powerbi/globalservice/v202003/environments/discover?client=powerbi-msolap" | ConvertTo-Json -Depth 10
7+
8+
internal sealed class CloudEnvironmentResponseContract
9+
{
10+
[JsonPropertyName("environments")]
11+
public CloudEnvironmentContract[] Environments { get; set; } = null!;
12+
}
13+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace Sqlbi.Bravo.Infrastructure.Contracts.PBICloud
2+
{
3+
using System.Text.Json.Serialization;
4+
5+
[DebuggerDisplay("{Name}")]
6+
internal sealed class CloudEnvironmentServiceContract
7+
{
8+
[JsonPropertyName("name")]
9+
public string Name { get; set; } = null!;
10+
11+
[JsonPropertyName("endpoint")]
12+
public string Endpoint { get; set; } = null!;
13+
14+
[JsonPropertyName("resourceId")]
15+
public string ResourceId { get; set; } = null!;
16+
17+
//[JsonPropertyName("allowedDomains")]
18+
//public string[] AllowedDomains { get; set; } = null!;
19+
}
20+
21+
internal static class CloudEnvironmentServiceContractExtension
22+
{
23+
public static bool IsAad(this CloudEnvironmentServiceContract service)
24+
=> service.Name.Equals("aad", StringComparison.Ordinal);
25+
26+
public static bool IsPowerBIBackend(this CloudEnvironmentServiceContract service)
27+
=> service.Name.Equals("powerbi-backend", StringComparison.Ordinal);
28+
}
29+
}

src/Infrastructure/Contracts/PBICloud/GlobalService.cs

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/Infrastructure/Contracts/PBICloud/GlobalServiceEnvironment.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/Infrastructure/Contracts/PBICloud/GlobalServiceEnvironmentClient.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)