-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ensure consistent line endings and simplify SQL Server retry logic (#479
) ### Summary & Motivation Ensure that C# files always use CRLF line endings for consistency across Windows and Mac environments. Clean up code with minor adjustments primarily in the Developer CLI. Wait for up to 10 seconds when generating `index.html` to ensure availability when the SPA is starting. This prevents failures when the API reads the file during startup. Simplify retry logic for handling when SQL Server is still starting on localhost. This change passes error codes to the SQL execution strategy, eliminating the need for try/catch blocks. Configure Rider and ReSharper to clean up code when saving, ensuring files are always formatted correctly. Upgrade JetBrains .NET tools to the latest version by updating `dotnet-tools.json`, aligning with the latest Rider and ReSharper versions. Fix warning when running bash script on a new Azure subscription without the `Microsoft.ContainerService` provider registered, addressing a minor issue during PlatformPlatform setup on a new Azure subscription. ### Checklist - [x] I have added a Label to the pull-request - [x] I have added tests, and done manual regression tests - [x] I have updated the documentation, if necessary
- Loading branch information
Showing
171 changed files
with
7,280 additions
and
7,291 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 39 additions & 39 deletions
78
application/AppGateway/Filters/ClusterDestinationConfigFilter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,39 @@ | ||
using Yarp.ReverseProxy.Configuration; | ||
|
||
namespace PlatformPlatform.AppGateway.Filters; | ||
|
||
public class ClusterDestinationConfigFilter : IProxyConfigFilter | ||
{ | ||
public ValueTask<ClusterConfig> ConfigureClusterAsync(ClusterConfig cluster, CancellationToken cancel) | ||
{ | ||
return cluster.ClusterId switch | ||
{ | ||
"account-management-api" => ReplaceDestinationAddress(cluster, "ACCOUNT_MANAGEMENT_API_URL"), | ||
"avatars-storage" => ReplaceDestinationAddress(cluster, "AVATARS_STORAGE_URL"), | ||
"back-office-api" => ReplaceDestinationAddress(cluster, "BACK_OFFICE_API_URL"), | ||
_ => throw new InvalidOperationException($"Unknown Cluster ID {cluster.ClusterId}") | ||
}; | ||
} | ||
|
||
public ValueTask<RouteConfig> ConfigureRouteAsync(RouteConfig route, ClusterConfig? cluster, CancellationToken cancel) | ||
{ | ||
return new ValueTask<RouteConfig>(route); | ||
} | ||
|
||
private static ValueTask<ClusterConfig> ReplaceDestinationAddress(ClusterConfig cluster, string environmentVariable) | ||
{ | ||
var destinationAddress = Environment.GetEnvironmentVariable(environmentVariable); | ||
if (destinationAddress is null) return new ValueTask<ClusterConfig>(cluster); | ||
|
||
// Each cluster has a dictionary with one and only one destination | ||
var destination = cluster.Destinations!.Single(); | ||
|
||
// This is read-only, so we'll create a new one with our updates | ||
var newDestinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
{ destination.Key, destination.Value with { Address = destinationAddress } } | ||
}; | ||
|
||
return new ValueTask<ClusterConfig>(cluster with { Destinations = newDestinations }); | ||
} | ||
} | ||
using Yarp.ReverseProxy.Configuration; | ||
|
||
namespace PlatformPlatform.AppGateway.Filters; | ||
|
||
public class ClusterDestinationConfigFilter : IProxyConfigFilter | ||
{ | ||
public ValueTask<ClusterConfig> ConfigureClusterAsync(ClusterConfig cluster, CancellationToken cancel) | ||
{ | ||
return cluster.ClusterId switch | ||
{ | ||
"account-management-api" => ReplaceDestinationAddress(cluster, "ACCOUNT_MANAGEMENT_API_URL"), | ||
"avatars-storage" => ReplaceDestinationAddress(cluster, "AVATARS_STORAGE_URL"), | ||
"back-office-api" => ReplaceDestinationAddress(cluster, "BACK_OFFICE_API_URL"), | ||
_ => throw new InvalidOperationException($"Unknown Cluster ID {cluster.ClusterId}") | ||
}; | ||
} | ||
|
||
public ValueTask<RouteConfig> ConfigureRouteAsync(RouteConfig route, ClusterConfig? cluster, CancellationToken cancel) | ||
{ | ||
return new ValueTask<RouteConfig>(route); | ||
} | ||
|
||
private static ValueTask<ClusterConfig> ReplaceDestinationAddress(ClusterConfig cluster, string environmentVariable) | ||
{ | ||
var destinationAddress = Environment.GetEnvironmentVariable(environmentVariable); | ||
if (destinationAddress is null) return new ValueTask<ClusterConfig>(cluster); | ||
|
||
// Each cluster has a dictionary with one and only one destination | ||
var destination = cluster.Destinations!.Single(); | ||
|
||
// This is read-only, so we'll create a new one with our updates | ||
var newDestinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
{ destination.Key, destination.Value with { Address = destinationAddress } } | ||
}; | ||
|
||
return new ValueTask<ClusterConfig>(cluster with { Destinations = newDestinations }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,51 @@ | ||
using Azure.Core; | ||
using PlatformPlatform.AppGateway.Filters; | ||
using PlatformPlatform.AppGateway.Transformations; | ||
using PlatformPlatform.SharedKernel.InfrastructureCore; | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
var reverseProxyBuilder = builder.Services | ||
.AddReverseProxy() | ||
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")) | ||
.AddConfigFilter<ClusterDestinationConfigFilter>(); | ||
|
||
if (InfrastructureCoreConfiguration.IsRunningInAzure) | ||
{ | ||
builder.Services.AddSingleton<TokenCredential>(InfrastructureCoreConfiguration.GetDefaultAzureCredential()); | ||
builder.Services.AddSingleton<ManagedIdentityTransform>(); | ||
builder.Services.AddSingleton<ApiVersionHeaderTransform>(); | ||
reverseProxyBuilder.AddTransforms(context => | ||
{ | ||
context.RequestTransforms.Add(context.Services.GetRequiredService<ManagedIdentityTransform>()); | ||
context.RequestTransforms.Add(context.Services.GetRequiredService<ApiVersionHeaderTransform>()); | ||
} | ||
); | ||
} | ||
else | ||
{ | ||
builder.Services.AddSingleton<SharedAccessSignatureRequestTransform>(); | ||
reverseProxyBuilder.AddTransforms(context => | ||
context.RequestTransforms.Add(context.Services.GetRequiredService<SharedAccessSignatureRequestTransform>()) | ||
); | ||
} | ||
|
||
builder.Services.AddNamedBlobStorages(builder, ("avatars-storage", "AVATARS_STORAGE_URL")); | ||
|
||
builder.WebHost.UseKestrel(option => option.AddServerHeader = false); | ||
|
||
var app = builder.Build(); | ||
|
||
// Adds middleware for redirecting HTTP Requests to HTTPS | ||
app.UseHttpsRedirection(); | ||
|
||
if (!app.Environment.IsDevelopment()) | ||
{ | ||
// Adds middleware for using HSTS, which adds the Strict-Transport-Security header | ||
// Defaults to 30 days. See https://aka.ms/aspnetcore-hsts, so be careful during development | ||
app.UseHsts(); | ||
} | ||
|
||
app.MapReverseProxy(); | ||
|
||
app.Run(); | ||
using Azure.Core; | ||
using PlatformPlatform.AppGateway.Filters; | ||
using PlatformPlatform.AppGateway.Transformations; | ||
using PlatformPlatform.SharedKernel.InfrastructureCore; | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
var reverseProxyBuilder = builder.Services | ||
.AddReverseProxy() | ||
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")) | ||
.AddConfigFilter<ClusterDestinationConfigFilter>(); | ||
|
||
if (InfrastructureCoreConfiguration.IsRunningInAzure) | ||
{ | ||
builder.Services.AddSingleton<TokenCredential>(InfrastructureCoreConfiguration.GetDefaultAzureCredential()); | ||
builder.Services.AddSingleton<ManagedIdentityTransform>(); | ||
builder.Services.AddSingleton<ApiVersionHeaderTransform>(); | ||
reverseProxyBuilder.AddTransforms(context => | ||
{ | ||
context.RequestTransforms.Add(context.Services.GetRequiredService<ManagedIdentityTransform>()); | ||
context.RequestTransforms.Add(context.Services.GetRequiredService<ApiVersionHeaderTransform>()); | ||
} | ||
); | ||
} | ||
else | ||
{ | ||
builder.Services.AddSingleton<SharedAccessSignatureRequestTransform>(); | ||
reverseProxyBuilder.AddTransforms(context => | ||
context.RequestTransforms.Add(context.Services.GetRequiredService<SharedAccessSignatureRequestTransform>()) | ||
); | ||
} | ||
|
||
builder.Services.AddNamedBlobStorages(builder, ("avatars-storage", "AVATARS_STORAGE_URL")); | ||
|
||
builder.WebHost.UseKestrel(option => option.AddServerHeader = false); | ||
|
||
var app = builder.Build(); | ||
|
||
// Adds middleware for redirecting HTTP Requests to HTTPS | ||
app.UseHttpsRedirection(); | ||
|
||
if (!app.Environment.IsDevelopment()) | ||
{ | ||
// Adds middleware for using HSTS, which adds the Strict-Transport-Security header | ||
// Defaults to 30 days. See https://aka.ms/aspnetcore-hsts, so be careful during development | ||
app.UseHsts(); | ||
} | ||
|
||
app.MapReverseProxy(); | ||
|
||
app.Run(); |
54 changes: 27 additions & 27 deletions
54
application/AppGateway/Transformations/ManagedIdentityTransform.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,27 @@ | ||
using Azure.Core; | ||
using Yarp.ReverseProxy.Transforms; | ||
|
||
namespace PlatformPlatform.AppGateway.Transformations; | ||
|
||
public class ManagedIdentityTransform(TokenCredential credential) | ||
: RequestHeaderTransform("Authorization", false) | ||
{ | ||
protected override string? GetValue(RequestTransformContext context) | ||
{ | ||
if (!context.HttpContext.Request.Path.StartsWithSegments("/avatars")) return null; | ||
|
||
var tokenRequestContext = new TokenRequestContext(["https://storage.azure.com/.default"]); | ||
var token = credential.GetToken(tokenRequestContext, context.HttpContext.RequestAborted); | ||
return $"Bearer {token.Token}"; | ||
} | ||
} | ||
|
||
public class ApiVersionHeaderTransform() : RequestHeaderTransform("x-ms-version", false) | ||
{ | ||
protected override string? GetValue(RequestTransformContext context) | ||
{ | ||
if (!context.HttpContext.Request.Path.StartsWithSegments("/avatars")) return null; | ||
|
||
return "2023-11-03"; | ||
} | ||
} | ||
using Azure.Core; | ||
using Yarp.ReverseProxy.Transforms; | ||
|
||
namespace PlatformPlatform.AppGateway.Transformations; | ||
|
||
public class ManagedIdentityTransform(TokenCredential credential) | ||
: RequestHeaderTransform("Authorization", false) | ||
{ | ||
protected override string? GetValue(RequestTransformContext context) | ||
{ | ||
if (!context.HttpContext.Request.Path.StartsWithSegments("/avatars")) return null; | ||
|
||
var tokenRequestContext = new TokenRequestContext(["https://storage.azure.com/.default"]); | ||
var token = credential.GetToken(tokenRequestContext, context.HttpContext.RequestAborted); | ||
return $"Bearer {token.Token}"; | ||
} | ||
} | ||
|
||
public class ApiVersionHeaderTransform() : RequestHeaderTransform("x-ms-version", false) | ||
{ | ||
protected override string? GetValue(RequestTransformContext context) | ||
{ | ||
if (!context.HttpContext.Request.Path.StartsWithSegments("/avatars")) return null; | ||
|
||
return "2023-11-03"; | ||
} | ||
} |
36 changes: 18 additions & 18 deletions
36
application/AppGateway/Transformations/SharedAccessSignatureRequestTransform.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,18 @@ | ||
using PlatformPlatform.SharedKernel.ApplicationCore.Services; | ||
using Yarp.ReverseProxy.Transforms; | ||
|
||
namespace PlatformPlatform.AppGateway.Transformations; | ||
|
||
public class SharedAccessSignatureRequestTransform([FromKeyedServices("avatars-storage")] IBlobStorage blobStorage) | ||
: RequestTransform | ||
{ | ||
public override ValueTask ApplyAsync(RequestTransformContext context) | ||
{ | ||
if (!context.Path.StartsWithSegments("/avatars")) return ValueTask.CompletedTask; | ||
|
||
var sharedAccessSignature = blobStorage.GetSharedAccessSignature("avatars", TimeSpan.FromMinutes(10)); | ||
context.HttpContext.Request.QueryString = new QueryString(sharedAccessSignature); | ||
|
||
return ValueTask.CompletedTask; | ||
} | ||
} | ||
using PlatformPlatform.SharedKernel.ApplicationCore.Services; | ||
using Yarp.ReverseProxy.Transforms; | ||
|
||
namespace PlatformPlatform.AppGateway.Transformations; | ||
|
||
public class SharedAccessSignatureRequestTransform([FromKeyedServices("avatars-storage")] IBlobStorage blobStorage) | ||
: RequestTransform | ||
{ | ||
public override ValueTask ApplyAsync(RequestTransformContext context) | ||
{ | ||
if (!context.Path.StartsWithSegments("/avatars")) return ValueTask.CompletedTask; | ||
|
||
var sharedAccessSignature = blobStorage.GetSharedAccessSignature("avatars", TimeSpan.FromMinutes(10)); | ||
context.HttpContext.Request.QueryString = new QueryString(sharedAccessSignature); | ||
|
||
return ValueTask.CompletedTask; | ||
} | ||
} |
Oops, something went wrong.