diff --git a/.github/workflows/build-and-dockerize.yml b/.github/workflows/build-and-dockerize.yml index 7fb4ebe5..2a7a68f4 100644 --- a/.github/workflows/build-and-dockerize.yml +++ b/.github/workflows/build-and-dockerize.yml @@ -39,7 +39,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: '9.0.x' - dotnet-quality: 'preview' - name: Install dependencies run: dotnet restore - name: Build diff --git a/BervProject.WebApi.Boilerplate.AppHost/BervProject.WebApi.Boilerplate.AppHost.csproj b/BervProject.WebApi.Boilerplate.AppHost/BervProject.WebApi.Boilerplate.AppHost.csproj new file mode 100644 index 00000000..0469cd84 --- /dev/null +++ b/BervProject.WebApi.Boilerplate.AppHost/BervProject.WebApi.Boilerplate.AppHost.csproj @@ -0,0 +1,26 @@ + + + + + Exe + net9.0 + enable + enable + true + 0f1966e4-7b2b-4a28-a9b6-8198aab433ec + + + + + + + + + + + + + + + + diff --git a/BervProject.WebApi.Boilerplate.AppHost/Program.cs b/BervProject.WebApi.Boilerplate.AppHost/Program.cs new file mode 100644 index 00000000..00f371eb --- /dev/null +++ b/BervProject.WebApi.Boilerplate.AppHost/Program.cs @@ -0,0 +1,32 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddRedis("cache").WithRedisInsight(); +var postgres = builder.AddPostgres("postgres").WithPgAdmin(); +var postgresdb = postgres.AddDatabase("postgresdb"); +var serviceBus = builder.AddAzureServiceBus("messaging").RunAsEmulator(); +var storage = builder.AddAzureStorage("storage").RunAsEmulator(); +var blobs = storage.AddBlobs("blobs"); +var queues = storage.AddQueues("queues"); +var tables = storage.AddTables("tables"); + +var migration = builder.AddProject("migrations") + .WithReference(postgresdb, connectionName: "BoilerplateConnectionString") + .WithExplicitStart(); + +builder.AddProject("apiservice") + .WithHttpEndpoint() + .WithReference(cache, connectionName: "Redis") + .WithReference(postgresdb, connectionName: "BoilerplateConnectionString") + .WithReference(blobs, connectionName: "AzureStorageBlob") + .WithReference(queues, connectionName: "AzureStorageQueue") + .WithReference(tables, connectionName: "AzureStorageTable") + .WithReference(serviceBus, connectionName: "AzureServiceBus") + .WaitFor(cache) + .WaitFor(postgresdb) + .WaitFor(blobs) + .WaitFor(queues) + .WaitFor(tables) + .WaitFor(serviceBus) + .WaitForCompletion(migration); + +builder.Build().Run(); diff --git a/BervProject.WebApi.Boilerplate.AppHost/appsettings.Development.json b/BervProject.WebApi.Boilerplate.AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/BervProject.WebApi.Boilerplate.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/BervProject.WebApi.Boilerplate.AppHost/appsettings.json b/BervProject.WebApi.Boilerplate.AppHost/appsettings.json new file mode 100644 index 00000000..31c092aa --- /dev/null +++ b/BervProject.WebApi.Boilerplate.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/BervProject.WebApi.Boilerplate.MigrationService/BervProject.WebApi.Boilerplate.MigrationService.csproj b/BervProject.WebApi.Boilerplate.MigrationService/BervProject.WebApi.Boilerplate.MigrationService.csproj new file mode 100644 index 00000000..ca71465b --- /dev/null +++ b/BervProject.WebApi.Boilerplate.MigrationService/BervProject.WebApi.Boilerplate.MigrationService.csproj @@ -0,0 +1,20 @@ + + + + net9.0 + enable + enable + dotnet-BervProject.WebApi.Boilerplate.MigrationService-02d1add9-9cd8-4b0e-96ab-9dedc82c871c + + + + + + + + + + + + + diff --git a/BervProject.WebApi.Boilerplate.MigrationService/Program.cs b/BervProject.WebApi.Boilerplate.MigrationService/Program.cs new file mode 100644 index 00000000..d228a1d4 --- /dev/null +++ b/BervProject.WebApi.Boilerplate.MigrationService/Program.cs @@ -0,0 +1,15 @@ +using BervProject.WebApi.Boilerplate.EntityFramework; +using BervProject.WebApi.Boilerplate.MigrationService; + +var builder = Host.CreateApplicationBuilder(args); + +builder.AddServiceDefaults(); + +builder.Services.AddHostedService(); + +builder.Services.AddOpenTelemetry() + .WithTracing(tracing => tracing.AddSource(Worker.ActivitySourceName)); +builder.AddNpgsqlDbContext("BoilerplateConnectionString"); + +var host = builder.Build(); +host.Run(); diff --git a/BervProject.WebApi.Boilerplate.MigrationService/Worker.cs b/BervProject.WebApi.Boilerplate.MigrationService/Worker.cs new file mode 100644 index 00000000..df663d0d --- /dev/null +++ b/BervProject.WebApi.Boilerplate.MigrationService/Worker.cs @@ -0,0 +1,52 @@ +using System.Diagnostics; +using BervProject.WebApi.Boilerplate.EntityFramework; +using Microsoft.EntityFrameworkCore; + +namespace BervProject.WebApi.Boilerplate.MigrationService; + +public class Worker : BackgroundService +{ + public const string ActivitySourceName = "Migrations"; + private static readonly ActivitySource SActivitySource = new(ActivitySourceName); + + private readonly IServiceProvider _serviceProvider; + private readonly IHostApplicationLifetime _hostApplicationLifetime; + + public Worker(IServiceProvider serviceProvider, + IHostApplicationLifetime hostApplicationLifetime) + { + _serviceProvider = serviceProvider; + _hostApplicationLifetime = hostApplicationLifetime; + } + + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + using var activity = SActivitySource.StartActivity("Migrating database", ActivityKind.Client); + + try + { + using var scope = _serviceProvider.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + await RunMigrationAsync(dbContext, cancellationToken); + } + catch (Exception ex) + { + activity?.AddException(ex); + throw; + } + + _hostApplicationLifetime.StopApplication(); + } + + private static async Task RunMigrationAsync(BoilerplateDbContext dbContext, CancellationToken cancellationToken) + { + var strategy = dbContext.Database.CreateExecutionStrategy(); + await strategy.ExecuteAsync(async () => + { + // Run migration in a transaction to avoid partial migration if it fails. + await dbContext.Database.MigrateAsync(cancellationToken); + }); + } + +} diff --git a/BervProject.WebApi.Boilerplate.MigrationService/appsettings.Development.json b/BervProject.WebApi.Boilerplate.MigrationService/appsettings.Development.json new file mode 100644 index 00000000..b2dcdb67 --- /dev/null +++ b/BervProject.WebApi.Boilerplate.MigrationService/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/BervProject.WebApi.Boilerplate.MigrationService/appsettings.json b/BervProject.WebApi.Boilerplate.MigrationService/appsettings.json new file mode 100644 index 00000000..b2dcdb67 --- /dev/null +++ b/BervProject.WebApi.Boilerplate.MigrationService/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/BervProject.WebApi.Boilerplate.ServiceDefaults/BervProject.WebApi.Boilerplate.ServiceDefaults.csproj b/BervProject.WebApi.Boilerplate.ServiceDefaults/BervProject.WebApi.Boilerplate.ServiceDefaults.csproj new file mode 100644 index 00000000..eeda27dd --- /dev/null +++ b/BervProject.WebApi.Boilerplate.ServiceDefaults/BervProject.WebApi.Boilerplate.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/BervProject.WebApi.Boilerplate.ServiceDefaults/Extensions.cs b/BervProject.WebApi.Boilerplate.ServiceDefaults/Extensions.cs new file mode 100644 index 00000000..2a3f4e07 --- /dev/null +++ b/BervProject.WebApi.Boilerplate.ServiceDefaults/Extensions.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/BervProject.WebApi.Boilerplate.sln b/BervProject.WebApi.Boilerplate.sln index e9626cd0..c83925f2 100644 --- a/BervProject.WebApi.Boilerplate.sln +++ b/BervProject.WebApi.Boilerplate.sln @@ -19,24 +19,94 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Integration.Test", "BervProject.WebApi.Integration.Test\BervProject.WebApi.Integration.Test.csproj", "{B1139AC3-3DC2-453D-9A72-9C7196F9759C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Boilerplate.AppHost", "BervProject.WebApi.Boilerplate.AppHost\BervProject.WebApi.Boilerplate.AppHost.csproj", "{D6E4407A-586B-4D75-A5F7-5AC9E7016614}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Boilerplate.ServiceDefaults", "BervProject.WebApi.Boilerplate.ServiceDefaults\BervProject.WebApi.Boilerplate.ServiceDefaults.csproj", "{3E0AA90A-F50F-4226-8236-947B6DC82BCC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BervProject.WebApi.Boilerplate.MigrationService", "BervProject.WebApi.Boilerplate.MigrationService\BervProject.WebApi.Boilerplate.MigrationService.csproj", "{94B60493-45E4-4437-9155-3D30DFBFB66A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x64.ActiveCfg = Debug|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x64.Build.0 = Debug|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x86.ActiveCfg = Debug|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Debug|x86.Build.0 = Debug|Any CPU {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|Any CPU.ActiveCfg = Release|Any CPU {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|Any CPU.Build.0 = Release|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x64.ActiveCfg = Release|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x64.Build.0 = Release|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x86.ActiveCfg = Release|Any CPU + {61F9CED4-DF01-40F5-A126-DE9F73C7AA89}.Release|x86.Build.0 = Release|Any CPU {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x64.Build.0 = Debug|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Debug|x86.Build.0 = Debug|Any CPU {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|Any CPU.Build.0 = Release|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x64.ActiveCfg = Release|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x64.Build.0 = Release|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x86.ActiveCfg = Release|Any CPU + {CE1B1BF2-9EF2-4C07-902F-817D81EFD22E}.Release|x86.Build.0 = Release|Any CPU {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x64.Build.0 = Debug|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Debug|x86.Build.0 = Debug|Any CPU {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|Any CPU.Build.0 = Release|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x64.ActiveCfg = Release|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x64.Build.0 = Release|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x86.ActiveCfg = Release|Any CPU + {B1139AC3-3DC2-453D-9A72-9C7196F9759C}.Release|x86.Build.0 = Release|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x64.ActiveCfg = Debug|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x64.Build.0 = Debug|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x86.ActiveCfg = Debug|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Debug|x86.Build.0 = Debug|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|Any CPU.Build.0 = Release|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x64.ActiveCfg = Release|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x64.Build.0 = Release|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x86.ActiveCfg = Release|Any CPU + {D6E4407A-586B-4D75-A5F7-5AC9E7016614}.Release|x86.Build.0 = Release|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x64.Build.0 = Debug|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Debug|x86.Build.0 = Debug|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|Any CPU.Build.0 = Release|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x64.ActiveCfg = Release|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x64.Build.0 = Release|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x86.ActiveCfg = Release|Any CPU + {3E0AA90A-F50F-4226-8236-947B6DC82BCC}.Release|x86.Build.0 = Release|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x64.ActiveCfg = Debug|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x64.Build.0 = Debug|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x86.ActiveCfg = Debug|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Debug|x86.Build.0 = Debug|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|Any CPU.Build.0 = Release|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x64.ActiveCfg = Release|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x64.Build.0 = Release|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x86.ActiveCfg = Release|Any CPU + {94B60493-45E4-4437-9155-3D30DFBFB66A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BervProject.WebApi.Boilerplate/BervProject.WebApi.Boilerplate.csproj b/BervProject.WebApi.Boilerplate/BervProject.WebApi.Boilerplate.csproj index bacabe8b..efe6affd 100644 --- a/BervProject.WebApi.Boilerplate/BervProject.WebApi.Boilerplate.csproj +++ b/BervProject.WebApi.Boilerplate/BervProject.WebApi.Boilerplate.csproj @@ -3,6 +3,7 @@ net9.0 BervProject.WebApi.Boilerplate + true @@ -31,6 +32,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BervProject.WebApi.Boilerplate/Extensions/SetupAzureExtension.cs b/BervProject.WebApi.Boilerplate/Extensions/SetupAzureExtension.cs index fbe1d6d7..0dc6a248 100644 --- a/BervProject.WebApi.Boilerplate/Extensions/SetupAzureExtension.cs +++ b/BervProject.WebApi.Boilerplate/Extensions/SetupAzureExtension.cs @@ -1,23 +1,24 @@ -namespace BervProject.WebApi.Boilerplate.Extenstions; +using Microsoft.Extensions.Configuration; -using BervProject.WebApi.Boilerplate.ConfigModel; -using BervProject.WebApi.Boilerplate.Entities; +namespace BervProject.WebApi.Boilerplate.Extenstions; + +using Entities; using BervProject.WebApi.Boilerplate.Services.Azure; -using BervProject.WebApi.Boilerplate.Services; +using Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Azure; public static class SetupAzureExtension { - public static void SetupAzure(this IServiceCollection services, AzureConfiguration azureConfig) + public static void SetupAzure(this IServiceCollection services, ConfigurationManager config) { services.AddAzureClients(builder => { - builder.AddBlobServiceClient(azureConfig.Storage.Blob.ConnectionString); - builder.AddQueueServiceClient(azureConfig.Storage.Queue.ConnectionString); - builder.AddServiceBusClient(azureConfig.ServiceBus.ConnectionString); - builder.AddTableServiceClient(azureConfig.Storage.Table.ConnectionString); + builder.AddBlobServiceClient(config.GetConnectionString("AzureStorageBlob")); + builder.AddQueueServiceClient(config.GetConnectionString("AzureStorageQueue")); + builder.AddServiceBusClient(config.GetConnectionString("AzureServiceBus")); + builder.AddTableServiceClient(config.GetConnectionString("AzureStorageTable")); }); services.AddScoped(); services.AddScoped(); diff --git a/BervProject.WebApi.Boilerplate/Program.cs b/BervProject.WebApi.Boilerplate/Program.cs index 989ffcd7..dd26e9be 100644 --- a/BervProject.WebApi.Boilerplate/Program.cs +++ b/BervProject.WebApi.Boilerplate/Program.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; using NLog.Web; var builder = WebApplication.CreateBuilder(args); @@ -36,17 +37,20 @@ builder.Services.SetupAWS(); // azure services -builder.Services.SetupAzure(azureConfig); +builder.Services.SetupAzure(builder.Configuration); // cron services builder.Services.AddScoped(); -builder.Services.AddHangfire(x => x.UsePostgreSqlStorage(builder.Configuration.GetConnectionString("BoilerplateConnectionString"))); +builder.Services.AddHangfire(x => x.UsePostgreSqlStorage(opt => +{ + opt.UseNpgsqlConnection(builder.Configuration.GetConnectionString("BoilerplateConnectionString")); +})); builder.Services.AddHangfireServer(); // essential services builder.Services.AddStackExchangeRedisCache(options => { - options.Configuration = builder.Configuration["Redis:ConnectionString"]; + options.Configuration = builder.Configuration.GetConnectionString("Redis"); }); builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("BoilerplateConnectionString"))); @@ -57,6 +61,12 @@ { var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + options.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "Boilerplate API", + Description = "An ASP.NET Core Web API" + }); }); var app = builder.Build(); diff --git a/BervProject.WebApi.Boilerplate/appsettings.json b/BervProject.WebApi.Boilerplate/appsettings.json index 7b3bd18b..c37a0274 100644 --- a/BervProject.WebApi.Boilerplate/appsettings.json +++ b/BervProject.WebApi.Boilerplate/appsettings.json @@ -25,27 +25,23 @@ "Azure": { "Storage": { "Blob": { - "ConnectionString": "UseDevelopmentStorage=true", "ContainerName": "testblob" }, "Queue": { - "ConnectionString": "UseDevelopmentStorage=true", "QueueName": "testqueue" - }, - "Table": { - "ConnectionString": "UseDevelopmentStorage=true" } }, "ServiceBus": { - "ConnectionString": "", - "QueueName": "", - "TopicName": "" + "QueueName": "testqueue", + "TopicName": "testtopic" } }, "ConnectionStrings": { - "BoilerplateConnectionString": "Host=localhost;Database=testdb;Username=postgres;Password=devpass4444" - }, - "Redis": { - "ConnectionString": "localhost:6379" + "BoilerplateConnectionString": "Host=localhost;Database=testdb;Username=postgres;Password=devpass4444", + "AzureStorageBlob": "UseDevelopmentStorage=true", + "AzureStorageQueue": "UseDevelopmentStorage=true", + "AzureStorageTable": "UseDevelopmentStorage=true", + "AzureServiceBus": "", + "Redis": "localhost:6379" } } diff --git a/BervProject.WebApi.Integration.Test/BervProject.WebApi.Integration.Test.csproj b/BervProject.WebApi.Integration.Test/BervProject.WebApi.Integration.Test.csproj index 2d056445..1bd40f14 100644 --- a/BervProject.WebApi.Integration.Test/BervProject.WebApi.Integration.Test.csproj +++ b/BervProject.WebApi.Integration.Test/BervProject.WebApi.Integration.Test.csproj @@ -4,7 +4,7 @@ net9.0 enable enable - + false false diff --git a/BervProject.WebApi.Test/BervProject.WebApi.Test.csproj b/BervProject.WebApi.Test/BervProject.WebApi.Test.csproj index 21d26101..0eaadeb6 100644 --- a/BervProject.WebApi.Test/BervProject.WebApi.Test.csproj +++ b/BervProject.WebApi.Test/BervProject.WebApi.Test.csproj @@ -2,7 +2,7 @@ net9.0 - + false false diff --git a/README.md b/README.md index 084826cd..e4043ea7 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Net Core Web API Boilerplate for My Project ## Build Status -| Github Action | Azure Pipelines | Codecov | -|:-------------:|:---------------:|:-------:| -| ![Build&Dockerize](https://github.com/bervProject/NETCoreAPIBoilerplate/workflows/Build&Dockerize/badge.svg) | [![Build Status](https://dev.azure.com/berviantoleo/NETCoreWebAPIBoilerplate/_apis/build/status/bervProject.NETCoreAPIBoilerplate?branchName=main)](https://dev.azure.com/berviantoleo/NETCoreWebAPIBoilerplate/_build/latest?definitionId=6&branchName=main) | [![codecov](https://codecov.io/gh/bervProject/NETCoreAPIBoilerplate/branch/main/graph/badge.svg?token=4OP6CFN2PX)](https://codecov.io/gh/bervProject/NETCoreAPIBoilerplate) | +| Github Action (Build) | Github Action (Docker) | Azure Pipelines | Codecov | +|:---:|:---:|:---:|:---:| +| [![Build .NET](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/build-and-dockerize.yml/badge.svg)](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/build-and-dockerize.yml) | [![Dockerize](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/docker.yml/badge.svg)](https://github.com/bervProject/NETCoreAPIBoilerplate/actions/workflows/docker.yml) | [![Build Status](https://dev.azure.com/berviantoleo/NETCoreWebAPIBoilerplate/_apis/build/status/bervProject.NETCoreAPIBoilerplate?branchName=main)](https://dev.azure.com/berviantoleo/NETCoreWebAPIBoilerplate/_build/latest?definitionId=6&branchName=main) | [![codecov](https://codecov.io/gh/bervProject/NETCoreAPIBoilerplate/branch/main/graph/badge.svg?token=4OP6CFN2PX)](https://codecov.io/gh/bervProject/NETCoreAPIBoilerplate) | ## LICENSE diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1dca922a..5c4a5af5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -82,7 +82,7 @@ jobs: displayName: 'Publish the project - $(buildConfiguration)' inputs: command: 'publish' - projects: '**/*.csproj' + projects: '**/BervProject.WebApi.Boilerplate.csproj' publishWebProjects: false arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)' zipAfterPublish: true