Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/abl-83-fix-warning-ef-singlequer…
Browse files Browse the repository at this point in the history
…y' into abl-83-fix-warning-ef-singlequery
  • Loading branch information
ericbrunner committed Dec 18, 2024
2 parents cbf0a46 + 8a3f07b commit 3d291c2
Show file tree
Hide file tree
Showing 27 changed files with 415 additions and 131 deletions.
7 changes: 5 additions & 2 deletions .ci/appsettings.override.postgres.docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@
"ConnectionString": "User ID=files;Password=Passw0rd;Server=postgres;Port=5432;Database=enmeshed;"
},
"BlobStorage": {
"CloudProvider": "Azure",
"ConnectionInfo": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;"
"ProductName": "AzureStorageAccount",
"AzureStorageAccount": {
"ContainerName": "files",
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;"
}
}
}
},
Expand Down
7 changes: 5 additions & 2 deletions .ci/appsettings.override.postgres.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@
"ConnectionString": "User ID=files;Password=Passw0rd;Server=localhost;Port=5432;Database=enmeshed;"
},
"BlobStorage": {
"CloudProvider": "Azure",
"ConnectionInfo": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
"ProductName": "AzureStorageAccount",
"AzureStorageAccount": {
"ContainerName": "files",
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
}
}
}
},
Expand Down
7 changes: 5 additions & 2 deletions .ci/appsettings.override.sqlserver.docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@
"ConnectionString": "Server=sqlserver;Database=enmeshed;User Id=files;Password=Passw0rd;TrustServerCertificate=True"
},
"BlobStorage": {
"CloudProvider": "Azure",
"ConnectionInfo": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;"
"ProductName": "AzureStorageAccount",
"AzureStorageAccount": {
"ContainerName": "files",
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;"
}
}
}
},
Expand Down
7 changes: 5 additions & 2 deletions .ci/appsettings.override.sqlserver.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@
"ConnectionString": "Server=localhost;Database=enmeshed;User Id=files;Password=Passw0rd;TrustServerCertificate=True"
},
"BlobStorage": {
"CloudProvider": "Azure",
"ConnectionInfo": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
"ProductName": "AzureStorageAccount",
"AzureStorageAccount": {
"ContainerName": "files",
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
}
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion Applications/AdminApi/src/AdminApi/AdminApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.1.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="5.8.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.24.0" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="9.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion Applications/AdminApi/src/AdminApi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ RUN dotnet publish /p:ContinuousIntegrationBuild=true /p:UseAppHost=false --no-r
RUN dotnet publish /p:ContinuousIntegrationBuild=true --configuration Release --output /app/publish/health "/src/Applications/HealthCheck/src/HealthCheck.csproj"

#### Build Flutter Admin UI ####
FROM ghcr.io/cirruslabs/flutter:3.27.0 AS flutter-build-env
FROM ghcr.io/cirruslabs/flutter:3.27.1 AS flutter-build-env

COPY Applications/AdminUi /src
WORKDIR /src
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@
"ConnectionString": "User ID=postgres;Password=admin;Server=localhost;Port=5432;Database=enmeshed;"
},
"BlobStorage": {
"Provider": "Azure",
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
"ProductName": "AzureStorageAccount",
"AzureStorageAccount": {
"ContainerName": "files",
"ConnectionString": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;"
}
}
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="Autofac" Version="8.1.1" />
<PackageReference Include="Autofac" Version="8.2.0" />
<PackageReference Include="AWSSDK.S3" Version="3.7.410.7" />
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.18.2" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />
<PackageReference Include="Google.Cloud.PubSub.V1" Version="3.19.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
using System.ComponentModel.DataAnnotations;
using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.Persistence.BlobStorage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.AzureStorageAccount;

public static class AzureStorageAccountServiceCollectionExtensions
{
public static void AddAzureStorageAccount(this IServiceCollection services,
Action<AzureStorageAccountOptions> setupOptions)
{
var options = new AzureStorageAccountOptions();
setupOptions.Invoke(options);

services.AddAzureStorageAccount(options);
}


public static void AddAzureStorageAccount(this IServiceCollection services, AzureStorageAccountOptions options)
{
services.Configure<AzureStorageAccountOptions>(opt => opt.ConnectionString = options.ConnectionString);
services.AddSingleton<IOptions<AzureStorageAccountOptions>>(new OptionsWrapper<AzureStorageAccountOptions>(options));
services.Configure<AzureStorageAccountOptions>(opt =>
{
opt.ConnectionString = options.ConnectionString;
opt.ContainerName = options.ContainerName;
});
services.AddSingleton<AzureStorageAccountContainerClientFactory>();
services.AddScoped<IBlobStorage, AzureStorageAccount>();
}
}

public class AzureStorageAccountOptions
{
public string ConnectionString { get; set; } = string.Empty;
public required string ConnectionString { get; set; }

[Required]
[MinLength(2)]
public required string ContainerName { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public class BlobStorageHealthCheck : IHealthCheck
private static int _numberOfTries;

private readonly IBlobStorage _storage;
private readonly string _bucketName;
private readonly string _rootFolderName;

public BlobStorageHealthCheck(IBlobStorage storage, string bucketName)
public BlobStorageHealthCheck(IBlobStorage storage, string rootFolderName)
{
_storage = storage;
_bucketName = bucketName;
_rootFolderName = rootFolderName;
}

public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
Expand All @@ -42,7 +42,7 @@ private async Task<bool> IsUploadPossible(string filename)
{
try
{
_storage.Add(_bucketName, filename, FILE_TEXT.GetBytes());
_storage.Add(_rootFolderName, filename, FILE_TEXT.GetBytes());
await _storage.SaveAsync();
return true;
}
Expand All @@ -56,7 +56,7 @@ private async Task<bool> IsDownloadPossible(string filename)
{
try
{
var downloadBytes = await _storage.FindAsync(_bucketName, filename);
var downloadBytes = await _storage.FindAsync(_rootFolderName, filename);
var downloadedString = Encoding.UTF8.GetString(downloadBytes);
return downloadedString == FILE_TEXT;
}
Expand All @@ -70,7 +70,7 @@ private async Task<bool> IsDeletionPossible(string filename)
{
try
{
_storage.Remove(_bucketName, filename);
_storage.Remove(_rootFolderName, filename);
await _storage.SaveAsync();
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.ComponentModel.DataAnnotations;
using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.Persistence.BlobStorage;
using Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.AzureStorageAccount;
using Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.GoogleCloudStorage;
using Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.S3;
using Backbone.Tooling.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
Expand All @@ -9,9 +11,6 @@ namespace Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage;

public static class BlobStorageServiceCollectionExtensions
{
public const string AZURE_CLOUD_PROVIDER = "Azure";
public const string GOOGLE_CLOUD_PROVIDER = "GoogleCloud";

public static void AddBlobStorage(this IServiceCollection services, Action<BlobStorageOptions> setupOptions)
{
var options = new BlobStorageOptions();
Expand All @@ -22,43 +21,67 @@ public static void AddBlobStorage(this IServiceCollection services, Action<BlobS

public static void AddBlobStorage(this IServiceCollection services, BlobStorageOptions options)
{
switch (options.CloudProvider)
switch (options.ProductName)
{
case AZURE_CLOUD_PROVIDER:
services.AddAzureStorageAccount(azureStorageAccountOptions => { azureStorageAccountOptions.ConnectionString = options.ConnectionInfo!; });
case BlobStorageOptions.AZURE_STORAGE_ACCOUNT:
services.AddAzureStorageAccount(options.AzureStorageAccount!);
break;
case BlobStorageOptions.GOOGLE_CLOUD_STORAGE:
services.AddGoogleCloudStorage(options.GoogleCloudStorage!);
break;
case GOOGLE_CLOUD_PROVIDER:
services.AddGoogleCloudStorage(googleCloudStorageOptions =>
{
googleCloudStorageOptions.GcpAuthJson = options.ConnectionInfo;
googleCloudStorageOptions.BucketName = options.Container;
});
case BlobStorageOptions.S3_BUCKET:
services.AddS3(options.S3Bucket!);
break;

default:
{
if (options.CloudProvider.IsNullOrEmpty())
if (options.ProductName.IsNullOrEmpty())
throw new NotSupportedException("No cloud provider was specified.");

throw new NotSupportedException(
$"{options.CloudProvider} is not a currently supported cloud provider.");
$"{options.ProductName} is not a currently supported cloud provider.");
}
}

services.AddHealthChecks().Add(
new HealthCheckRegistration(
"blob_storage",
sp => new BlobStorageHealthCheck(sp.GetRequiredService<IBlobStorage>(), options.Container),
sp => new BlobStorageHealthCheck(sp.GetRequiredService<IBlobStorage>(), options.RootFolder),
HealthStatus.Unhealthy, null
)
);
}
}

public class BlobStorageOptions
public class BlobStorageOptions : IValidatableObject
{
public string CloudProvider { get; set; } = null!;
public const string AZURE_STORAGE_ACCOUNT = "AzureStorageAccount";
public const string GOOGLE_CLOUD_STORAGE = "GoogleCloudStorage";
public const string S3_BUCKET = "S3Bucket";

[RegularExpression($"{AZURE_STORAGE_ACCOUNT}|{GOOGLE_CLOUD_STORAGE}|{S3_BUCKET}")]
public string ProductName { get; set; } = null!;

public AzureStorageAccountOptions? AzureStorageAccount { get; set; }

public GoogleCloudStorageOptions? GoogleCloudStorage { get; set; }

public string Container { get; set; } = null!;
public S3BucketOptions? S3Bucket { get; set; }

public string? ConnectionInfo { get; set; }
public string RootFolder => ProductName switch
{
AZURE_STORAGE_ACCOUNT => AzureStorageAccount!.ContainerName,
GOOGLE_CLOUD_STORAGE => GoogleCloudStorage!.BucketName,
S3_BUCKET => S3Bucket!.BucketName,
_ => throw new Exception("Unsupported ProductName")
};

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ProductName == AZURE_STORAGE_ACCOUNT && AzureStorageAccount == null)
yield return new ValidationResult($"The property '{nameof(AzureStorageAccount)}' must be set when the {nameof(ProductName)} is '{AZURE_STORAGE_ACCOUNT}'.", [nameof(AzureStorageAccount)]);

if (ProductName == GOOGLE_CLOUD_STORAGE && GoogleCloudStorage == null)
yield return new ValidationResult($"The property '{nameof(GoogleCloudStorage)}' must be set when the {nameof(ProductName)} is '{GOOGLE_CLOUD_STORAGE}'.", [nameof(GoogleCloudStorage)]);
}
}
Original file line number Diff line number Diff line change
@@ -1,57 +1,33 @@
using System.ComponentModel.DataAnnotations;
using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.Persistence.BlobStorage;
using Backbone.Tooling.Extensions;
using Google.Apis.Auth.OAuth2;
using Google.Cloud.Storage.V1;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Backbone.BuildingBlocks.Infrastructure.Persistence.BlobStorage.GoogleCloudStorage;

public static class GoogleCloudStorageServiceCollectionExtensions
{
public static void AddGoogleCloudStorage(this IServiceCollection services,
Action<GoogleCloudStorageOptions> setupOptions)
{
services.Configure(setupOptions);

var options = new GoogleCloudStorageOptions();
setupOptions.Invoke(options);

services.AddGoogleCloudStorage(options);
}

public static void AddGoogleCloudStorage(this IServiceCollection services, GoogleCloudStorageOptions options)
{
services.AddSingleton(_ =>
{
var storageClient = options.GcpAuthJson.IsNullOrEmpty()
var storageClient = options.ServiceAccountJson.IsNullOrEmpty()
? StorageClient.Create()
: StorageClient.Create(GoogleCredential.FromJson(options.GcpAuthJson));
: StorageClient.Create(GoogleCredential.FromJson(options.ServiceAccountJson));
return storageClient;
});

services.AddScoped<IBlobStorage>(sp =>
{
var storageClient = sp.GetService<StorageClient>();
var logger = sp.GetService<ILogger<GoogleCloudStorage>>();

if (storageClient == null)
{
throw new Exception("A StorageClient was not registered in the dependency container.");
}

if (logger == null)
{
throw new Exception("A Logger was not registered in the dependency container.");
}

return new GoogleCloudStorage(storageClient, logger);
});
services.AddScoped<IBlobStorage, GoogleCloudStorage>();
}
}

public class GoogleCloudStorageOptions
{
public string? GcpAuthJson { get; set; }
public string BucketName { get; set; } = string.Empty;
public required string? ServiceAccountJson { get; set; }

[Required]
[MinLength(2)]
public required string BucketName { get; set; }
}
Loading

0 comments on commit 3d291c2

Please sign in to comment.