From 4785a680da7d1e24df3b4b63045c09c4200752cc Mon Sep 17 00:00:00 2001 From: Meysam Hadeli <35596795+meysamhadeli@users.noreply.github.com> Date: Wed, 18 Dec 2024 23:28:18 +0330 Subject: [PATCH] feat: update dotnet to version 9 --- .config/dotnet-tools.json | 4 +- .editorconfig | 5 + Directory.Build.props | 14 +-- README.md | 25 ++-- global.json | 2 +- src/BuildingBlocks/BuildingBlocks.csproj | 111 ++++++++++-------- src/BuildingBlocks/Core/EventDispatcher.cs | 61 ++++------ src/BuildingBlocks/EFCore/AppDbContextBase.cs | 15 ++- .../EFCore/DesignTimeDbContextFactoryBase.cs | 2 - src/BuildingBlocks/EFCore/EfTxBehavior.cs | 57 ++++----- src/BuildingBlocks/EFCore/Extensions.cs | 60 +++++----- src/BuildingBlocks/EFCore/IDataSeeder.cs | 4 + src/BuildingBlocks/EFCore/ISeedManager.cs | 6 + src/BuildingBlocks/EFCore/SeedManagers.cs | 39 ++++++ src/BuildingBlocks/MassTransit/Extensions.cs | 2 - src/BuildingBlocks/Mongo/MongoDbContext.cs | 8 ++ src/BuildingBlocks/Mongo/MongoRepository.cs | 1 + src/BuildingBlocks/OpenApi/Extensions.cs | 55 +++++++++ .../SecuritySchemeDocumentTransformer.cs | 44 +++++++ .../PersistMessageConfiguration.cs | 33 ------ .../PersistMessageProcessor/Extensions.cs | 70 ++++++----- .../IPersistMessageDbContext.cs | 2 +- .../MessageDeliveryType.cs | 3 +- .../PersistMessageProcessor/MessageStatus.cs | 1 + .../PersistMessageProcessor/PersistMessage.cs | 2 +- .../PersistMessageBackgroundService.cs | 31 ++--- .../{Data => }/PersistMessageDbContext.cs | 23 ++-- .../PersistMessageProcessor.cs | 14 +-- .../Swagger/ConfigureSwaggerOptions.cs | 90 -------------- .../Swagger/ServiceCollectionExtensions.cs | 107 ----------------- .../Swagger/SwaggerDefaultValues.cs | 70 ----------- src/BuildingBlocks/Swagger/SwaggerOptions.cs | 9 -- src/BuildingBlocks/TestBase/TestBase.cs | 55 ++++----- .../Booking/src/Booking/Booking.csproj | 2 +- .../InfrastructureExtensions.cs | 10 +- .../IntegrationTest/Integration.Test.csproj | 4 +- .../FlightDbContextModelSnapshot.cs | 2 +- .../src/Flight/Data/Seed/FlightDataSeeder.cs | 64 ++++------ .../InfrastructureExtensions.cs | 12 +- src/Services/Flight/src/Flight/Flight.csproj | 8 +- .../DeletingFlight/V1/DeleteFlight.cs | 54 ++++++--- .../V1/GetAvailableFlights.cs | 2 + .../GettingFlightById/V1/GetFlightById.cs | 6 +- .../V1/GetAvailableSeats.cs | 2 + .../tests/EndToEndTest/EndToEnd.Test.csproj | 4 +- .../EndToEndTest/FlightTestDataSeeder.cs | 85 ++++++++++++++ .../Flight/Features/CreateFlightTests.cs | 1 - .../IntegrationTest/FlightTestDataSeeder.cs | 85 ++++++++++++++ .../IntegrationTest/Integration.Test.csproj | 4 +- .../Flight/tests/UnitTest/Unit.Test.csproj | 4 +- .../InfrastructureExtensions.cs | 16 ++- .../Identity/src/Identity/Identity.csproj | 2 +- .../IntegrationTest/IdentityTestDataSeeder.cs | 63 ++++++++++ .../IntegrationTest/Integration.Test.csproj | 4 +- .../InfrastructureExtensions.cs | 10 +- .../Passenger/src/Passenger/Passenger.csproj | 4 +- .../IntegrationTest/Integration.Test.csproj | 4 +- 57 files changed, 766 insertions(+), 711 deletions(-) create mode 100644 src/BuildingBlocks/EFCore/ISeedManager.cs create mode 100644 src/BuildingBlocks/EFCore/SeedManagers.cs create mode 100644 src/BuildingBlocks/OpenApi/Extensions.cs create mode 100644 src/BuildingBlocks/OpenApi/SecuritySchemeDocumentTransformer.cs delete mode 100644 src/BuildingBlocks/PersistMessageProcessor/Data/Configurations/PersistMessageConfiguration.cs rename src/BuildingBlocks/PersistMessageProcessor/{Data => }/PersistMessageDbContext.cs (85%) delete mode 100644 src/BuildingBlocks/Swagger/ConfigureSwaggerOptions.cs delete mode 100644 src/BuildingBlocks/Swagger/ServiceCollectionExtensions.cs delete mode 100644 src/BuildingBlocks/Swagger/SwaggerDefaultValues.cs delete mode 100644 src/BuildingBlocks/Swagger/SwaggerOptions.cs create mode 100644 src/Services/Flight/tests/EndToEndTest/FlightTestDataSeeder.cs create mode 100644 src/Services/Flight/tests/IntegrationTest/FlightTestDataSeeder.cs create mode 100644 src/Services/Identity/tests/IntegrationTest/IdentityTestDataSeeder.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a4d1f263..776a0bf0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -10,11 +10,11 @@ "rollForward": false }, "dotnet-ef": { - "version": "8.0.8", + "version": "9.0.0", "commands": [ "dotnet-ef" ], "rollForward": false } } -} +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index b388e303..d36b9a29 100644 --- a/.editorconfig +++ b/.editorconfig @@ -241,6 +241,11 @@ dotnet_diagnostic.IDE0055.severity = suggestion # CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name' dotnet_diagnostic.CS1574.severity = error +# IDE0160, IDE0161: Report violations when block-scoped namespaces are used +dotnet_diagnostic.IDE0160.severity = none +dotnet_diagnostic.IDE0161.severity = none + + ################################################################################## # https://jetbrains.com.xy2401.com/help/resharper/EditorConfig_Index.html # https://jetbrains.com.xy2401.com/help/resharper/Reference__Code_Inspections_CSHARP.html diff --git a/Directory.Build.props b/Directory.Build.props index dcca6505..b19e86e1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable @@ -10,22 +10,22 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/README.md b/README.md index b40dd661..bcdd1f16 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ - :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`. - :sparkle: Using `Fluent Validation` and a `Validation Pipeline Behaviour` on top of `MediatR`. - :sparkle: Using `Minimal API` for all endpoints. +- :sparkle: Using `AspNetCore OpenApi` for `generating` built-in support `OpenAPI documentation` in ASP.NET Core. - :sparkle: Using `Health Check` for `reporting` the `health` of app infrastructure components. - :sparkle: Using `Docker-Compose` and `Kubernetes` for our deployment mechanism. - :sparkle: Using `Kibana` on top of `Serilog` for `logging`. @@ -85,24 +86,26 @@ High-level plan is represented in the table ## Technologies - Libraries -- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core -- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core -- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations +- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core. +- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core. +- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations. +- ✔️ **[`AspNetCore OpenApi`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi?view=aspnetcore-9.0&tabs=visual-studio#configure-openapi-document-generation)** - Provides built-in support for OpenAPI document generation in ASP.NET Core. - ✔️ **[`Masstransit`](https://github.com/MassTransit/MassTransit)** - Distributed Application Framework for .NET. - ✔️ **[`MediatR`](https://github.com/jbogard/MediatR)** - Simple, unambitious mediator implementation in .NET. -- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules -- ✔️ **[`Swagger & Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core +- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules. +- ✔️ **[`Scalar`](https://github.com/scalar/scalar/tree/main/packages/scalar.aspnetcore)** - Scalar provides an easy way to render beautiful API references based on OpenAPI/Swagger documents. +- ✔️ **[`Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core. - ✔️ **[`Serilog`](https://github.com/serilog/serilog)** - Simple .NET logging with fully-structured events -- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner +- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. - ✔️ **[`Scrutor`](https://github.com/khellang/Scrutor)** - Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection - ✔️ **[`Opentelemetry-dotnet`](https://github.com/open-telemetry/opentelemetry-dotnet)** - The OpenTelemetry .NET Client -- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core +- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core. - ✔️ **[`EasyCaching`](https://github.com/dotnetcore/EasyCaching)** - Open source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easier. - ✔️ **[`Mapster`](https://github.com/MapsterMapper/Mapster)** - Convention-based object-object mapper in .NET. -- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core -- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs -- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET -- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier +- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core. +- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs. +- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET. +- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier. - ✔️ **[`gRPC-dotnet`](https://github.com/grpc/grpc-dotnet)** - gRPC functionality for .NET. - ✔️ **[`EventStore`](https://github.com/EventStore/EventStore)** - The open-source, functional database with Complex Event Processing. - ✔️ **[`MongoDB.Driver`](https://github.com/mongodb/mongo-csharp-driver)** - .NET Driver for MongoDB. diff --git a/global.json b/global.json index 5cf96894..ccd7812e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.401", + "version": "9.0.100", "rollForward": "latestFeature" } } diff --git a/src/BuildingBlocks/BuildingBlocks.csproj b/src/BuildingBlocks/BuildingBlocks.csproj index 59523cd8..2a34911d 100644 --- a/src/BuildingBlocks/BuildingBlocks.csproj +++ b/src/BuildingBlocks/BuildingBlocks.csproj @@ -1,11 +1,12 @@ + - + - + @@ -19,41 +20,42 @@ - + - + - - - - - - + + + + + + - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + + @@ -63,48 +65,53 @@ - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - + - + - + - - + + - + - - + + - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/BuildingBlocks/Core/EventDispatcher.cs b/src/BuildingBlocks/Core/EventDispatcher.cs index 36f278c0..b10f295f 100644 --- a/src/BuildingBlocks/Core/EventDispatcher.cs +++ b/src/BuildingBlocks/Core/EventDispatcher.cs @@ -9,30 +9,17 @@ namespace BuildingBlocks.Core; -public sealed class EventDispatcher : IEventDispatcher +public sealed class EventDispatcher( + IServiceScopeFactory serviceScopeFactory, + IEventMapper eventMapper, + ILogger logger, + IPersistMessageProcessor persistMessageProcessor, + IHttpContextAccessor httpContextAccessor +) + : IEventDispatcher { - private readonly IEventMapper _eventMapper; - private readonly ILogger _logger; - private readonly IPersistMessageProcessor _persistMessageProcessor; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IServiceScopeFactory _serviceScopeFactory; - - public EventDispatcher(IServiceScopeFactory serviceScopeFactory, - IEventMapper eventMapper, - ILogger logger, - IPersistMessageProcessor persistMessageProcessor, - IHttpContextAccessor httpContextAccessor) - { - _serviceScopeFactory = serviceScopeFactory; - _eventMapper = eventMapper; - _logger = logger; - _persistMessageProcessor = persistMessageProcessor; - _httpContextAccessor = httpContextAccessor; - } - - public async Task SendAsync(IReadOnlyList events, Type type = null, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default) where T : IEvent { if (events.Count > 0) @@ -45,7 +32,7 @@ async Task PublishIntegrationEvent(IReadOnlyList integrationE { foreach (var integrationEvent in integrationEvents) { - await _persistMessageProcessor.PublishMessageAsync( + await persistMessageProcessor.PublishMessageAsync( new MessageEnvelope(integrationEvent, SetHeaders()), cancellationToken); } @@ -74,7 +61,7 @@ await _persistMessageProcessor.PublishMessageAsync( foreach (var internalMessage in internalMessages) { - await _persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken); + await persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken); } } } @@ -89,20 +76,20 @@ public async Task SendAsync(T @event, Type type = null, private Task> MapDomainEventToIntegrationEventAsync( IReadOnlyList events) { - _logger.LogTrace("Processing integration events start..."); + logger.LogTrace("Processing integration events start..."); var wrappedIntegrationEvents = GetWrappedIntegrationEvents(events.ToList())?.ToList(); if (wrappedIntegrationEvents?.Count > 0) return Task.FromResult>(wrappedIntegrationEvents); var integrationEvents = new List(); - using var scope = _serviceScopeFactory.CreateScope(); + using var scope = serviceScopeFactory.CreateScope(); foreach (var @event in events) { var eventType = @event.GetType(); - _logger.LogTrace($"Handling domain event: {eventType.Name}"); + logger.LogTrace($"Handling domain event: {eventType.Name}"); - var integrationEvent = _eventMapper.MapToIntegrationEvent(@event); + var integrationEvent = eventMapper.MapToIntegrationEvent(@event); if (integrationEvent is null) continue; @@ -110,7 +97,7 @@ private Task> MapDomainEventToIntegrationEventA integrationEvents.Add(integrationEvent); } - _logger.LogTrace("Processing integration events done..."); + logger.LogTrace("Processing integration events done..."); return Task.FromResult>(integrationEvents); } @@ -119,16 +106,16 @@ private Task> MapDomainEventToIntegrationEventA private Task> MapDomainEventToInternalCommandAsync( IReadOnlyList events) { - _logger.LogTrace("Processing internal message start..."); + logger.LogTrace("Processing internal message start..."); var internalCommands = new List(); - using var scope = _serviceScopeFactory.CreateScope(); + using var scope = serviceScopeFactory.CreateScope(); foreach (var @event in events) { var eventType = @event.GetType(); - _logger.LogTrace($"Handling domain event: {eventType.Name}"); + logger.LogTrace($"Handling domain event: {eventType.Name}"); - var integrationEvent = _eventMapper.MapToInternalCommand(@event); + var integrationEvent = eventMapper.MapToInternalCommand(@event); if (integrationEvent is null) continue; @@ -136,7 +123,7 @@ private Task> MapDomainEventToInternalCommandAsy internalCommands.Add(integrationEvent); } - _logger.LogTrace("Processing internal message done..."); + logger.LogTrace("Processing internal message done..."); return Task.FromResult>(internalCommands); } @@ -159,9 +146,9 @@ private IEnumerable GetWrappedIntegrationEvents(IReadOnlyList private IDictionary SetHeaders() { var headers = new Dictionary(); - headers.Add("CorrelationId", _httpContextAccessor?.HttpContext?.GetCorrelationId()); - headers.Add("UserId", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier)); - headers.Add("UserName", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name)); + headers.Add("CorrelationId", httpContextAccessor?.HttpContext?.GetCorrelationId()); + headers.Add("UserId", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier)); + headers.Add("UserName", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name)); return headers; } diff --git a/src/BuildingBlocks/EFCore/AppDbContextBase.cs b/src/BuildingBlocks/EFCore/AppDbContextBase.cs index 1fd548ec..0dcd9c10 100644 --- a/src/BuildingBlocks/EFCore/AppDbContextBase.cs +++ b/src/BuildingBlocks/EFCore/AppDbContextBase.cs @@ -1,15 +1,14 @@ -namespace BuildingBlocks.EFCore; - using System.Collections.Immutable; -using Core.Event; -using Core.Model; +using BuildingBlocks.Core.Event; +using BuildingBlocks.Core.Model; +using BuildingBlocks.Web; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Logging; -using Web; -using Exception = System.Exception; using IsolationLevel = System.Data.IsolationLevel; +namespace BuildingBlocks.EFCore; + public abstract class AppDbContextBase : DbContext, IDbContext { private readonly ICurrentUserProvider? _currentUserProvider; @@ -174,9 +173,9 @@ private void OnBeforeSaving() } } } - catch (Exception ex) + catch (System.Exception ex) { - throw new Exception("try for find IAggregate", ex); + throw new System.Exception("try for find IAggregate", ex); } } } diff --git a/src/BuildingBlocks/EFCore/DesignTimeDbContextFactoryBase.cs b/src/BuildingBlocks/EFCore/DesignTimeDbContextFactoryBase.cs index a1bd716c..177441cb 100644 --- a/src/BuildingBlocks/EFCore/DesignTimeDbContextFactoryBase.cs +++ b/src/BuildingBlocks/EFCore/DesignTimeDbContextFactoryBase.cs @@ -1,5 +1,3 @@ -using System; -using System.IO; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; diff --git a/src/BuildingBlocks/EFCore/EfTxBehavior.cs b/src/BuildingBlocks/EFCore/EfTxBehavior.cs index 3323f946..cb429072 100644 --- a/src/BuildingBlocks/EFCore/EfTxBehavior.cs +++ b/src/BuildingBlocks/EFCore/EfTxBehavior.cs @@ -2,49 +2,38 @@ using BuildingBlocks.Core; using MediatR; using Microsoft.Extensions.Logging; +using System.Transactions; +using BuildingBlocks.PersistMessageProcessor; +using BuildingBlocks.Polly; namespace BuildingBlocks.EFCore; -using System.Transactions; -using PersistMessageProcessor; -using Polly; -public class EfTxBehavior : IPipelineBehavior - where TRequest : notnull, IRequest - where TResponse : notnull +public class EfTxBehavior( + ILogger> logger, + IDbContext dbContextBase, + IPersistMessageDbContext persistMessageDbContext, + IEventDispatcher eventDispatcher +) + : IPipelineBehavior +where TRequest : notnull, IRequest +where TResponse : notnull { - private readonly ILogger> _logger; - private readonly IDbContext _dbContextBase; - private readonly IPersistMessageDbContext _persistMessageDbContext; - private readonly IEventDispatcher _eventDispatcher; - - public EfTxBehavior( - ILogger> logger, - IDbContext dbContextBase, - IPersistMessageDbContext persistMessageDbContext, - IEventDispatcher eventDispatcher) - { - _logger = logger; - _dbContextBase = dbContextBase; - _persistMessageDbContext = persistMessageDbContext; - _eventDispatcher = eventDispatcher; - } - public async Task Handle(TRequest request, RequestHandlerDelegate next, - CancellationToken cancellationToken) + CancellationToken cancellationToken) { - _logger.LogInformation( + logger.LogInformation( "{Prefix} Handled command {MediatrRequest}", nameof(EfTxBehavior), typeof(TRequest).FullName); - _logger.LogDebug( + logger.LogDebug( "{Prefix} Handled command {MediatrRequest} with content {RequestContent}", nameof(EfTxBehavior), typeof(TRequest).FullName, JsonSerializer.Serialize(request)); - _logger.LogInformation( + logger.LogInformation( "{Prefix} Open the transaction for {MediatrRequest}", nameof(EfTxBehavior), typeof(TRequest).FullName); @@ -56,32 +45,32 @@ public async Task Handle(TRequest request, RequestHandlerDelegate), typeof(TRequest).FullName); while (true) { - var domainEvents = _dbContextBase.GetDomainEvents(); + var domainEvents = dbContextBase.GetDomainEvents(); if (domainEvents is null || !domainEvents.Any()) { return response; } - await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken); + await eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken); // Save data to database with some retry policy in distributed transaction - await _dbContextBase.RetryOnFailure(async () => + await dbContextBase.RetryOnFailure(async () => { - await _dbContextBase.SaveChangesAsync(cancellationToken); + await dbContextBase.SaveChangesAsync(cancellationToken); }); // Save data to database with some retry policy in distributed transaction - await _persistMessageDbContext.RetryOnFailure(async () => + await persistMessageDbContext.RetryOnFailure(async () => { - await _persistMessageDbContext.SaveChangesAsync(cancellationToken); + await persistMessageDbContext.SaveChangesAsync(cancellationToken); }); scope.Complete(); diff --git a/src/BuildingBlocks/EFCore/Extensions.cs b/src/BuildingBlocks/EFCore/Extensions.cs index 11a21a87..282de70b 100644 --- a/src/BuildingBlocks/EFCore/Extensions.cs +++ b/src/BuildingBlocks/EFCore/Extensions.cs @@ -1,19 +1,20 @@ using System.Linq.Expressions; +using Ardalis.GuardClauses; using BuildingBlocks.Core.Model; using BuildingBlocks.Web; +using Humanizer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace BuildingBlocks.EFCore; -using Ardalis.GuardClauses; -using Humanizer; -using Microsoft.EntityFrameworkCore.Metadata; - public static class Extensions { public static IServiceCollection AddCustomDbContext(this IServiceCollection services) @@ -36,32 +37,30 @@ public static IServiceCollection AddCustomDbContext(this IServiceColle { dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name); }) - // https://github.com/efcore/EFCore.NamingConventions .UseSnakeCaseNamingConvention(); + + // Suppress warnings for pending model changes + options.ConfigureWarnings( + w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); }); - services.AddScoped(provider => provider.GetService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); return services; } - public static IApplicationBuilder UseMigration( - this IApplicationBuilder app, - IWebHostEnvironment env - ) + + public static IApplicationBuilder UseMigration(this IApplicationBuilder app) where TContext : DbContext, IDbContext { - MigrateDatabaseAsync(app.ApplicationServices).GetAwaiter().GetResult(); + MigrateAsync(app.ApplicationServices).GetAwaiter().GetResult(); - if (!env.IsEnvironment("test")) - { - SeedDataAsync(app.ApplicationServices).GetAwaiter().GetResult(); - } + SeedAsync(app.ApplicationServices).GetAwaiter().GetResult(); return app; } - // ref: https://github.com/pdevito3/MessageBusTestingInMemHarness/blob/main/RecipeManagement/src/RecipeManagement/Databases/RecipesDbContext.cs public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder) { @@ -114,23 +113,30 @@ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder) } } - private static async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) + private static async Task MigrateAsync(IServiceProvider serviceProvider) where TContext : DbContext, IDbContext { - using var scope = serviceProvider.CreateScope(); - + await using var scope = serviceProvider.CreateAsyncScope(); var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.MigrateAsync(); - } + var logger = scope.ServiceProvider.GetRequiredService>(); - private static async Task SeedDataAsync(IServiceProvider serviceProvider) - { - using var scope = serviceProvider.CreateScope(); - var seeders = scope.ServiceProvider.GetServices(); + var pendingMigrations = await context.Database.GetPendingMigrationsAsync(); - foreach (var seeder in seeders) + if (pendingMigrations.Any()) { - await seeder.SeedAllAsync(); + logger.LogInformation("Applying {Count} pending migrations...", pendingMigrations.Count()); + + await context.Database.MigrateAsync(); + logger.LogInformation("Migrations applied successfully."); } } + + private static async Task SeedAsync(IServiceProvider serviceProvider) + { + await using var scope = serviceProvider.CreateAsyncScope(); + + var seedersManager = scope.ServiceProvider.GetRequiredService(); + + await seedersManager.ExecuteAsync(); + } } diff --git a/src/BuildingBlocks/EFCore/IDataSeeder.cs b/src/BuildingBlocks/EFCore/IDataSeeder.cs index 81829683..f09d485e 100644 --- a/src/BuildingBlocks/EFCore/IDataSeeder.cs +++ b/src/BuildingBlocks/EFCore/IDataSeeder.cs @@ -4,4 +4,8 @@ public interface IDataSeeder { Task SeedAllAsync(); } + + public interface ITestDataSeeder : IDataSeeder + { + } } diff --git a/src/BuildingBlocks/EFCore/ISeedManager.cs b/src/BuildingBlocks/EFCore/ISeedManager.cs new file mode 100644 index 00000000..c6f0e790 --- /dev/null +++ b/src/BuildingBlocks/EFCore/ISeedManager.cs @@ -0,0 +1,6 @@ +namespace BuildingBlocks.EFCore; + +public interface ISeedManager +{ + Task ExecuteAsync(); +} diff --git a/src/BuildingBlocks/EFCore/SeedManagers.cs b/src/BuildingBlocks/EFCore/SeedManagers.cs new file mode 100644 index 00000000..cf91b0de --- /dev/null +++ b/src/BuildingBlocks/EFCore/SeedManagers.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace BuildingBlocks.EFCore; + +public class SeedManager( +IServiceProvider serviceProvider +) + : ISeedManager +{ + public async Task ExecuteAsync() + { + await using var scope = serviceProvider.CreateAsyncScope(); + var logger = scope.ServiceProvider.GetRequiredService>(); + var env = scope.ServiceProvider.GetRequiredService(); + var dataSeeders = scope.ServiceProvider.GetServices(); + + if (env.IsEnvironment("test")) + { + foreach (var seeder in dataSeeders.Where(x => x is ITestDataSeeder)) + { + logger.LogInformation("Test Seed {SeederName} is started.", seeder.GetType().Name); + await seeder.SeedAllAsync(); + logger.LogInformation("Test Seed {SeederName} is completed.", seeder.GetType().Name); + } + } + else + { + foreach (var seeder in dataSeeders.Where(x => x is not ITestDataSeeder)) + { + logger.LogInformation("Seed {SeederName} is started.", seeder.GetType().Name); + await seeder.SeedAllAsync(); + logger.LogInformation("Seed {SeederName} is completed.", seeder.GetType().Name); + } + } + } +} diff --git a/src/BuildingBlocks/MassTransit/Extensions.cs b/src/BuildingBlocks/MassTransit/Extensions.cs index 1f466545..b264bbd4 100644 --- a/src/BuildingBlocks/MassTransit/Extensions.cs +++ b/src/BuildingBlocks/MassTransit/Extensions.cs @@ -1,7 +1,5 @@ using System.Reflection; -using BuildingBlocks.Core.Event; using BuildingBlocks.Web; -using Humanizer; using MassTransit; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; diff --git a/src/BuildingBlocks/Mongo/MongoDbContext.cs b/src/BuildingBlocks/Mongo/MongoDbContext.cs index fbbce103..8f25d25e 100644 --- a/src/BuildingBlocks/Mongo/MongoDbContext.cs +++ b/src/BuildingBlocks/Mongo/MongoDbContext.cs @@ -1,7 +1,9 @@ using System.Globalization; using Microsoft.Extensions.Options; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; namespace BuildingBlocks.Mongo; @@ -14,6 +16,12 @@ public class MongoDbContext : IMongoDbContext public IMongoDatabase Database { get; } public IMongoClient MongoClient { get; } protected readonly IList> _commands; + private static readonly bool _isSerializerRegisterd; + + static MongoDbContext() + { + BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String)); + } public MongoDbContext(IOptions options) { diff --git a/src/BuildingBlocks/Mongo/MongoRepository.cs b/src/BuildingBlocks/Mongo/MongoRepository.cs index 458cdea8..f479e107 100644 --- a/src/BuildingBlocks/Mongo/MongoRepository.cs +++ b/src/BuildingBlocks/Mongo/MongoRepository.cs @@ -1,6 +1,7 @@ using System.Linq.Expressions; using BuildingBlocks.Core.Model; using MongoDB.Driver; +using MongoDB.Driver.Linq; namespace BuildingBlocks.Mongo; diff --git a/src/BuildingBlocks/OpenApi/Extensions.cs b/src/BuildingBlocks/OpenApi/Extensions.cs new file mode 100644 index 00000000..32077e9f --- /dev/null +++ b/src/BuildingBlocks/OpenApi/Extensions.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Scalar.AspNetCore; + +namespace BuildingBlocks.OpenApi +{ + public static class Extensions + { + // ref: https://github.com/dotnet/eShop/blob/main/src/eShop.ServiceDefaults/OpenApi.Extensions.cs + public static IServiceCollection AddAspnetOpenApi(this IServiceCollection services) + { + string[] versions = ["v1"]; + + foreach (var description in versions) + { + services.AddOpenApi( + description, + options => + { + options.AddDocumentTransformer(); + }); + } + + return services; + } + + public static IApplicationBuilder UseAspnetOpenApi(this WebApplication app) + { + app.MapOpenApi(); + + app.UseSwaggerUI( + options => + { + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach (var description in descriptions) + { + var openApiUrl = $"/openapi/{description.GroupName}.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint(openApiUrl, name); + } + }); + + // Add scalar ui + app.MapScalarApiReference( + redocOptions => + { + redocOptions.WithOpenApiRoutePattern("/openapi/{documentName}.json"); + }); + + return app; + } + } +} diff --git a/src/BuildingBlocks/OpenApi/SecuritySchemeDocumentTransformer.cs b/src/BuildingBlocks/OpenApi/SecuritySchemeDocumentTransformer.cs new file mode 100644 index 00000000..0fec484e --- /dev/null +++ b/src/BuildingBlocks/OpenApi/SecuritySchemeDocumentTransformer.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; + +public class SecuritySchemeDocumentTransformer : IOpenApiDocumentTransformer +{ + public Task TransformAsync( + OpenApiDocument document, + OpenApiDocumentTransformerContext context, + CancellationToken cancellationToken + ) + { + document.Components ??= new(); + + // Bearer token scheme + document.Components.SecuritySchemes.Add( + "Bearer", + new OpenApiSecurityScheme + { + Name = "Authorization", + Type = SecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = + "Enter 'Bearer' [space] and your token in the text input below.\n\nExample: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'", + } + ); + + // API Key scheme + document.Components.SecuritySchemes.Add( + "ApiKey", + new OpenApiSecurityScheme + { + Name = "X-API-KEY", + Type = SecuritySchemeType.ApiKey, + In = ParameterLocation.Header, + Description = + "Enter your API key in the text input below.\n\nExample: '12345-abcdef'", + } + ); + + return Task.CompletedTask; + } +} diff --git a/src/BuildingBlocks/PersistMessageProcessor/Data/Configurations/PersistMessageConfiguration.cs b/src/BuildingBlocks/PersistMessageProcessor/Data/Configurations/PersistMessageConfiguration.cs deleted file mode 100644 index 8395030b..00000000 --- a/src/BuildingBlocks/PersistMessageProcessor/Data/Configurations/PersistMessageConfiguration.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace BuildingBlocks.PersistMessageProcessor.Data.Configurations; - -public class PersistMessageConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(PersistMessage)); - - builder.HasKey(x => x.Id); - - builder.Property(r => r.Id) - .IsRequired().ValueGeneratedNever(); - - // // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api - builder.Property(r => r.Version).IsConcurrencyToken(); - - builder.Property(x => x.DeliveryType) - .HasDefaultValue(MessageDeliveryType.Outbox) - .HasConversion( - x => x.ToString(), - x => (MessageDeliveryType)Enum.Parse(typeof(MessageDeliveryType), x)); - - - builder.Property(x => x.MessageStatus) - .HasDefaultValue(MessageStatus.InProgress) - .HasConversion( - v => v.ToString(), - v => (MessageStatus)Enum.Parse(typeof(MessageStatus), v)); - } -} diff --git a/src/BuildingBlocks/PersistMessageProcessor/Extensions.cs b/src/BuildingBlocks/PersistMessageProcessor/Extensions.cs index 3ef16900..5aef3604 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/Extensions.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/Extensions.cs @@ -1,51 +1,57 @@ -using BuildingBlocks.PersistMessageProcessor.Data; using BuildingBlocks.Web; +using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.DependencyInjection; namespace BuildingBlocks.PersistMessageProcessor; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; - public static class Extensions { - public static IServiceCollection AddPersistMessageProcessor(this IServiceCollection services, - IWebHostEnvironment env) + public static IServiceCollection AddPersistMessageProcessor( + this IServiceCollection services, + IWebHostEnvironment env + ) { AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); services.AddValidateOptions(); - services.AddDbContext((sp, options) => - { - var persistMessageOptions = sp.GetRequiredService(); - - options.UseNpgsql(persistMessageOptions.ConnectionString, - dbOptions => - { - dbOptions.MigrationsAssembly(typeof(PersistMessageDbContext).Assembly.GetName().Name); - }) - // https://github.com/efcore/EFCore.NamingConventions - .UseSnakeCaseNamingConvention(); - }); - - services.AddScoped(provider => - { - var persistMessageDbContext = provider.GetRequiredService(); - - persistMessageDbContext.Database.EnsureCreated(); - persistMessageDbContext.CreatePersistMessageTable(); - - return persistMessageDbContext; - }); + services.AddDbContext( + (sp, options) => + { + var persistMessageOptions = sp.GetRequiredService(); + + options.UseNpgsql( + persistMessageOptions.ConnectionString, + dbOptions => + { + dbOptions.MigrationsAssembly( + typeof(PersistMessageDbContext).Assembly.GetName().Name); + }) + // https://github.com/efcore/EFCore.NamingConventions + .UseSnakeCaseNamingConvention(); + + // Todo: follow up the issues of .net 9 to use better approach taht will provide by .net! + options.ConfigureWarnings( + w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); + }); + + services.AddScoped( + provider => + { + var persistMessageDbContext = + provider.GetRequiredService(); + + persistMessageDbContext.Database.EnsureCreated(); + persistMessageDbContext.CreatePersistMessageTableIfNotExists(); + + return persistMessageDbContext; + }); services.AddScoped(); - if (env.EnvironmentName != "test") - { - services.AddHostedService(); - } + services.AddHostedService(); return services; } diff --git a/src/BuildingBlocks/PersistMessageProcessor/IPersistMessageDbContext.cs b/src/BuildingBlocks/PersistMessageProcessor/IPersistMessageDbContext.cs index b91888ec..c67b8fa6 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/IPersistMessageDbContext.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/IPersistMessageDbContext.cs @@ -4,7 +4,7 @@ namespace BuildingBlocks.PersistMessageProcessor; public interface IPersistMessageDbContext { - DbSet PersistMessages { get; } + DbSet PersistMessage { get; } Task SaveChangesAsync(CancellationToken cancellationToken = default); Task ExecuteTransactionalAsync(CancellationToken cancellationToken = default); } diff --git a/src/BuildingBlocks/PersistMessageProcessor/MessageDeliveryType.cs b/src/BuildingBlocks/PersistMessageProcessor/MessageDeliveryType.cs index 8d690aad..52aa7977 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/MessageDeliveryType.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/MessageDeliveryType.cs @@ -3,7 +3,8 @@ namespace BuildingBlocks.PersistMessageProcessor; [Flags] public enum MessageDeliveryType { + Unknown = 0, Outbox = 1, Inbox = 2, - Internal = 4 + Internal = 3 } diff --git a/src/BuildingBlocks/PersistMessageProcessor/MessageStatus.cs b/src/BuildingBlocks/PersistMessageProcessor/MessageStatus.cs index 039d65e7..3b40c605 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/MessageStatus.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/MessageStatus.cs @@ -2,6 +2,7 @@ namespace BuildingBlocks.PersistMessageProcessor; public enum MessageStatus { + Unknown = 0, InProgress = 1, Processed = 2 } diff --git a/src/BuildingBlocks/PersistMessageProcessor/PersistMessage.cs b/src/BuildingBlocks/PersistMessageProcessor/PersistMessage.cs index 3e2110f2..551777fe 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/PersistMessage.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/PersistMessage.cs @@ -1,6 +1,6 @@ +using BuildingBlocks.Core.Model; namespace BuildingBlocks.PersistMessageProcessor; -using Core.Model; public class PersistMessage : IVersion { diff --git a/src/BuildingBlocks/PersistMessageProcessor/PersistMessageBackgroundService.cs b/src/BuildingBlocks/PersistMessageProcessor/PersistMessageBackgroundService.cs index 0c0caf1e..68f40925 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/PersistMessageBackgroundService.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/PersistMessageBackgroundService.cs @@ -5,27 +5,20 @@ namespace BuildingBlocks.PersistMessageProcessor; -public class PersistMessageBackgroundService : BackgroundService +public class PersistMessageBackgroundService( + ILogger logger, + IServiceProvider serviceProvider, + IOptions options +) + : BackgroundService { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; - private PersistMessageOptions _options; + private PersistMessageOptions _options = options.Value; private Task? _executingTask; - public PersistMessageBackgroundService( - ILogger logger, - IServiceProvider serviceProvider, - IOptions options) - { - _logger = logger; - _serviceProvider = serviceProvider; - _options = options.Value; - } - protected override Task ExecuteAsync(CancellationToken stoppingToken) { - _logger.LogInformation("PersistMessage Background Service Start"); + logger.LogInformation("PersistMessage Background Service Start"); _executingTask = ProcessAsync(stoppingToken); @@ -34,7 +27,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) public override Task StopAsync(CancellationToken cancellationToken) { - _logger.LogInformation("PersistMessage Background Service Stop"); + logger.LogInformation("PersistMessage Background Service Stop"); return base.StopAsync(cancellationToken); } @@ -43,15 +36,15 @@ private async Task ProcessAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { - await using (var scope = _serviceProvider.CreateAsyncScope()) + await using (var scope = serviceProvider.CreateAsyncScope()) { var service = scope.ServiceProvider.GetRequiredService(); await service.ProcessAllAsync(stoppingToken); } var delay = _options.Interval is { } - ? TimeSpan.FromSeconds((int)_options.Interval) - : TimeSpan.FromSeconds(30); + ? TimeSpan.FromSeconds((int)_options.Interval) + : TimeSpan.FromSeconds(30); await Task.Delay(delay, stoppingToken); } diff --git a/src/BuildingBlocks/PersistMessageProcessor/Data/PersistMessageDbContext.cs b/src/BuildingBlocks/PersistMessageProcessor/PersistMessageDbContext.cs similarity index 85% rename from src/BuildingBlocks/PersistMessageProcessor/Data/PersistMessageDbContext.cs rename to src/BuildingBlocks/PersistMessageProcessor/PersistMessageDbContext.cs index 4e569a46..27636a7d 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/Data/PersistMessageDbContext.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/PersistMessageDbContext.cs @@ -1,14 +1,11 @@ +using BuildingBlocks.Core.Model; using BuildingBlocks.EFCore; using Microsoft.EntityFrameworkCore; - -namespace BuildingBlocks.PersistMessageProcessor.Data; - -using Configurations; -using Core.Model; using Microsoft.Extensions.Logging; -using Exception = System.Exception; using IsolationLevel = System.Data.IsolationLevel; +namespace BuildingBlocks.PersistMessageProcessor; + public class PersistMessageDbContext : DbContext, IPersistMessageDbContext { private readonly ILogger? _logger; @@ -20,11 +17,10 @@ public PersistMessageDbContext(DbContextOptions options _logger = logger; } - public DbSet PersistMessages => Set(); + public DbSet PersistMessage => Set(); protected override void OnModelCreating(ModelBuilder builder) { - builder.ApplyConfiguration(new PersistMessageConfiguration()); base.OnModelCreating(builder); builder.ToSnakeCaseTables(); } @@ -79,13 +75,8 @@ public override async Task SaveChangesAsync(CancellationToken cancellationT } } - public void CreatePersistMessageTable() + public void CreatePersistMessageTableIfNotExists() { - if (Database.GetPendingMigrations().Any()) - { - throw new InvalidOperationException("Cannot create table if there are pending migrations."); - } - string createTableSql = @" create table if not exists persist_message ( id uuid not null, @@ -120,9 +111,9 @@ private void OnBeforeSaving() } } } - catch (Exception ex) + catch (System.Exception ex) { - throw new Exception("try for find IVersion", ex); + throw new System.Exception("try for find IVersion", ex); } } } diff --git a/src/BuildingBlocks/PersistMessageProcessor/PersistMessageProcessor.cs b/src/BuildingBlocks/PersistMessageProcessor/PersistMessageProcessor.cs index 7042b5c8..faa4b84a 100644 --- a/src/BuildingBlocks/PersistMessageProcessor/PersistMessageProcessor.cs +++ b/src/BuildingBlocks/PersistMessageProcessor/PersistMessageProcessor.cs @@ -54,13 +54,13 @@ await SavePersistMessageAsync(new MessageEnvelope(internalCommand), MessageDeliv public async Task> GetByFilterAsync(Expression> predicate, CancellationToken cancellationToken = default) { - return (await _persistMessageDbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken)) + return (await _persistMessageDbContext.PersistMessage.Where(predicate).ToListAsync(cancellationToken)) .AsReadOnly(); } public Task ExistMessageAsync(Guid messageId, CancellationToken cancellationToken = default) { - return _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync(x => + return _persistMessageDbContext.PersistMessage.FirstOrDefaultAsync(x => x.Id == messageId && x.DeliveryType == MessageDeliveryType.Inbox && x.MessageStatus == MessageStatus.Processed, @@ -73,7 +73,7 @@ public async Task ProcessAsync( CancellationToken cancellationToken = default) { var message = - await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync( + await _persistMessageDbContext.PersistMessage.FirstOrDefaultAsync( x => x.Id == messageId && x.DeliveryType == deliveryType, cancellationToken); if (message is null) @@ -109,7 +109,7 @@ await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync( public async Task ProcessAllAsync(CancellationToken cancellationToken = default) { - var messages = await _persistMessageDbContext.PersistMessages + var messages = await _persistMessageDbContext.PersistMessage .Where(x => x.MessageStatus != MessageStatus.Processed) .ToListAsync(cancellationToken); @@ -121,7 +121,7 @@ public async Task ProcessAllAsync(CancellationToken cancellationToken = default) public async Task ProcessInboxAsync(Guid messageId, CancellationToken cancellationToken = default) { - var message = await _persistMessageDbContext.PersistMessages.FirstOrDefaultAsync( + var message = await _persistMessageDbContext.PersistMessage.FirstOrDefaultAsync( x => x.Id == messageId && x.DeliveryType == MessageDeliveryType.Inbox && x.MessageStatus == MessageStatus.InProgress, @@ -193,7 +193,7 @@ private async Task SavePersistMessageAsync( else id = NewId.NextGuid(); - await _persistMessageDbContext.PersistMessages.AddAsync( + await _persistMessageDbContext.PersistMessage.AddAsync( new PersistMessage( id, messageEnvelope.Message.GetType().ToString(), @@ -215,7 +215,7 @@ private async Task ChangeMessageStatusAsync(PersistMessage message, Cancellation { message.ChangeState(MessageStatus.Processed); - _persistMessageDbContext.PersistMessages.Update(message); + _persistMessageDbContext.PersistMessage.Update(message); await _persistMessageDbContext.SaveChangesAsync(cancellationToken); } diff --git a/src/BuildingBlocks/Swagger/ConfigureSwaggerOptions.cs b/src/BuildingBlocks/Swagger/ConfigureSwaggerOptions.cs deleted file mode 100644 index cee88423..00000000 --- a/src/BuildingBlocks/Swagger/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Text; -using Asp.Versioning; -using Asp.Versioning.ApiExplorer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace BuildingBlocks.Swagger; - -public class ConfigureSwaggerOptions : IConfigureOptions -{ - private readonly IApiVersionDescriptionProvider provider; - private readonly SwaggerOptions? _options; - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IOptions options) - { - this.provider = provider; - _options = options.Value; - } - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); - } - } - - private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - var text = new StringBuilder("An example application with OpenAPI, Swashbuckle, and API versioning."); - var info = new OpenApiInfo - { - Version = description.ApiVersion.ToString(), - Title = _options?.Title ?? "APIs", - Description = "An application with Swagger, Swashbuckle, and API versioning.", - Contact = new OpenApiContact { Name = "", Email = "" }, - License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } - }; - - if (description.IsDeprecated) - { - text.Append("This API version has been deprecated."); - } - - if (description.SunsetPolicy is SunsetPolicy policy) - { - if (policy.Date is DateTimeOffset when) - { - text.Append(" The API will be sunset on ") - .Append(when.Date.ToShortDateString()) - .Append('.'); - } - - if (policy.HasLinks) - { - text.AppendLine(); - - for (var i = 0; i < policy.Links.Count; i++) - { - var link = policy.Links[i]; - - if (link.Type == "text/html") - { - text.AppendLine(); - - if (link.Title.HasValue) - { - text.Append(link.Title.Value).Append(": "); - } - - text.Append(link.LinkTarget.OriginalString); - } - } - } - } - - info.Description = text.ToString(); - - return info; - } -} diff --git a/src/BuildingBlocks/Swagger/ServiceCollectionExtensions.cs b/src/BuildingBlocks/Swagger/ServiceCollectionExtensions.cs deleted file mode 100644 index 44f3b8aa..00000000 --- a/src/BuildingBlocks/Swagger/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; - -namespace BuildingBlocks.Swagger; - -public static class ServiceCollectionExtensions -{ - public const string HeaderName = "X-Api-Key"; - public const string HeaderVersion = "api-version"; - - // https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md - // https://github.com/dotnet/aspnet-api-versioning/tree/88323136a97a59fcee24517a514c1a445530c7e2/examples/AspNetCore/WebApi/MinimalOpenApiExample - public static IServiceCollection AddCustomSwagger(this IServiceCollection services, - IConfiguration configuration, - params Assembly[] assemblies) - { - services.AddTransient, ConfigureSwaggerOptions>(); - services.AddOptions().Bind(configuration.GetSection(nameof(SwaggerOptions))) - .ValidateDataAnnotations(); - - services.AddSwaggerGen( - options => - { - options.OperationFilter(); - - foreach (var assembly in assemblies) - { - var xmlFile = XmlCommentsFilePath(assembly); - - if (File.Exists(xmlFile)) - options.IncludeXmlComments(xmlFile); - } - - options.AddEnumsWithValuesFixFilters(); - - options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - BearerFormat = "JWT", - Scheme = "oauth2", - Name = "Bearer", - In = ParameterLocation.Header - }); - - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type=ReferenceType.SecurityScheme, - Id="Bearer" - } - }, - new string[]{} - } - }); - - - options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); - - ////https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/467 - // options.OperationFilter(); - // options.OperationFilter(); - - // Enables Swagger annotations (SwaggerOperationAttribute, SwaggerParameterAttribute etc.) - // options.EnableAnnotations(); - }); - - // services.Configure(o => o.InferSecuritySchemes = true); - - static string XmlCommentsFilePath(Assembly assembly) - { - var basePath = Path.GetDirectoryName(assembly.Location); - var fileName = assembly.GetName().Name + ".xml"; - return Path.Combine(basePath, fileName); - } - - return services; - } - - public static IApplicationBuilder UseCustomSwagger(this WebApplication app) - { - app.UseSwagger(); - app.UseSwaggerUI( - options => - { - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach (var description in descriptions) - { - var url = $"/swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint(url, name); - } - }); - - return app; - } -} diff --git a/src/BuildingBlocks/Swagger/SwaggerDefaultValues.cs b/src/BuildingBlocks/Swagger/SwaggerDefaultValues.cs deleted file mode 100644 index 42cbaefb..00000000 --- a/src/BuildingBlocks/Swagger/SwaggerDefaultValues.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Linq; -using Humanizer; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; -using Newtonsoft.Json; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace BuildingBlocks.Swagger -{ - public class SwaggerDefaultValues : IOperationFilter - { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - - var apiDescription = context.ApiDescription; - - operation.Deprecated |= apiDescription.IsDeprecated(); - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1752#issue-663991077 - foreach (var responseType in context.ApiDescription.SupportedResponseTypes) - { - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/b7cf75e7905050305b115dd96640ddd6e74c7ac9/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L383-L387 - var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString(); - var response = operation.Responses[responseKey]; - - foreach (var contentType in response.Content.Keys) - { - if (responseType.ApiResponseFormats.All(x => x.MediaType != contentType)) - { - response.Content.Remove(contentType); - } - } - } - - if (operation.Parameters == null) - { - return; - } - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) - { - var description = apiDescription.ParameterDescriptions.FirstOrDefault(p => p.Name == parameter.Name); - - if (description is null) - { - return; - } - - if (parameter.Description == null) - { - parameter.Description = description.ModelMetadata?.Description; - } - - parameter.Name = description.Name.Camelize(); - - if (parameter.Schema.Default == null && description.DefaultValue != null) - { - // REF: https://github.com/Microsoft/aspnet-api-versioning/issues/429#issuecomment-605402330 - var json = JsonConvert.SerializeObject(description.DefaultValue, description.ModelMetadata - .ModelType, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); - parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json); - } - - parameter.Required |= description.IsRequired; - } - } - } -} diff --git a/src/BuildingBlocks/Swagger/SwaggerOptions.cs b/src/BuildingBlocks/Swagger/SwaggerOptions.cs deleted file mode 100644 index 82edaf86..00000000 --- a/src/BuildingBlocks/Swagger/SwaggerOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace BuildingBlocks.Swagger -{ - public class SwaggerOptions - { - public string Title { get; set; } - public string Name { get; set; } - public string Version { get; set; } - } -} diff --git a/src/BuildingBlocks/TestBase/TestBase.cs b/src/BuildingBlocks/TestBase/TestBase.cs index a339b814..4f788832 100644 --- a/src/BuildingBlocks/TestBase/TestBase.cs +++ b/src/BuildingBlocks/TestBase/TestBase.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Net; using System.Security.Claims; using Ardalis.GuardClauses; @@ -9,7 +10,6 @@ using BuildingBlocks.Web; using EasyNetQ.Management.Client; using Grpc.Net.Client; -using MassTransit; using MassTransit.Testing; using MediatR; using Microsoft.AspNetCore.Hosting; @@ -18,10 +18,17 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; using MongoDB.Driver; +using Npgsql; using NSubstitute; using Respawn; using Serilog; +using Testcontainers.EventStoreDb; +using Testcontainers.MongoDb; +using Testcontainers.PostgreSql; +using Testcontainers.RabbitMq; using WebMotions.Fake.Authentication.JwtBearer; using Xunit; using Xunit.Abstractions; @@ -29,12 +36,6 @@ namespace BuildingBlocks.TestBase; -using System.Globalization; -using Npgsql; -using Testcontainers.EventStoreDb; -using Testcontainers.MongoDb; -using Testcontainers.PostgreSql; -using Testcontainers.RabbitMq; public class TestFixture : IAsyncLifetime where TEntryPoint : class @@ -50,8 +51,7 @@ public class TestFixture : IAsyncLifetime public EventStoreDbContainer EventStoreDbTestContainer; public CancellationTokenSource CancellationTokenSource; - public PersistMessageBackgroundService PersistMessageBackgroundService => - ServiceProvider.GetRequiredService(); + public PersistMessageBackgroundService PersistMessageBackgroundService => ServiceProvider.GetRequiredService(); public HttpClient HttpClient { @@ -95,9 +95,17 @@ protected TestFixture() { TestRegistrationServices?.Invoke(services); services.ReplaceSingleton(AddHttpContextAccessorMock); + services.RemoveAll(); services.AddSingleton(); + // Register all ITestDataSeeder implementations dynamically + services.Scan(scan => scan + .FromApplicationDependencies() // Scan the current app and its dependencies + .AddClasses(classes => classes.AssignableTo()) // Find classes that implement ITestDataSeeder + .AsImplementedInterfaces() + .WithScopedLifetime()); + // add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor // https://github.com/webmotions/fake-authentication-jwtbearer // https://github.com/webmotions/fake-authentication-jwtbearer/issues/14 @@ -200,14 +208,8 @@ public async Task WaitForPublishing( var result = await WaitUntilConditionMet( async () => { - var published = - await TestHarness.Published.Any(cancellationToken); - - var faulty = - await TestHarness.Published.Any>( - cancellationToken); - - return published && faulty == false; + var published = await TestHarness.Published.Any(cancellationToken); + return published; }); return result; @@ -224,10 +226,7 @@ public async Task WaitForConsuming( var consumed = await TestHarness.Consumed.Any(cancellationToken); - var faulty = - await TestHarness.Consumed.Any>(cancellationToken); - - return consumed && faulty == false; + return consumed; }); return result; @@ -611,8 +610,6 @@ await Fixture.PersistMessageBackgroundService.StartAsync( _reSpawnerDefaultDb = await Respawner.CreateAsync( DefaultDbConnection, new RespawnerOptions { DbAdapter = DbAdapter.Postgres }); - - await SeedDataAsync(); } } @@ -680,18 +677,6 @@ private async Task ResetRabbitMqAsync(CancellationToken cancellationToken = defa protected virtual void RegisterTestsServices(IServiceCollection services) { } - - private async Task SeedDataAsync() - { - using var scope = Fixture.ServiceProvider.CreateScope(); - - var seeders = scope.ServiceProvider.GetServices(); - - foreach (var seeder in seeders) - { - await seeder.SeedAllAsync(); - } - } } public abstract class TestReadBase : TestFixtureCore diff --git a/src/Services/Booking/src/Booking/Booking.csproj b/src/Services/Booking/src/Booking/Booking.csproj index 74b242f2..6f6d9128 100644 --- a/src/Services/Booking/src/Booking/Booking.csproj +++ b/src/Services/Booking/src/Booking/Booking.csproj @@ -5,7 +5,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Services/Booking/src/Booking/Extensions/Infrastructure/InfrastructureExtensions.cs b/src/Services/Booking/src/Booking/Extensions/Infrastructure/InfrastructureExtensions.cs index c3d82f1d..56c4b155 100644 --- a/src/Services/Booking/src/Booking/Extensions/Infrastructure/InfrastructureExtensions.cs +++ b/src/Services/Booking/src/Booking/Extensions/Infrastructure/InfrastructureExtensions.cs @@ -8,9 +8,10 @@ using BuildingBlocks.Mapster; using BuildingBlocks.MassTransit; using BuildingBlocks.Mongo; +using BuildingBlocks.OpenApi; using BuildingBlocks.OpenTelemetry; using BuildingBlocks.PersistMessageProcessor; -using BuildingBlocks.Swagger; +using BuildingBlocks.ProblemDetails; using BuildingBlocks.Web; using Figgle; using FluentValidation; @@ -19,13 +20,10 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Prometheus; using Serilog; namespace Booking.Extensions.Infrastructure; -using BuildingBlocks.ProblemDetails; - public static class InfrastructureExtensions { public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder) @@ -67,7 +65,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder.AddCustomSerilog(env); builder.Services.AddJwt(); builder.Services.AddHttpContextAccessor(); - builder.Services.AddCustomSwagger(configuration, typeof(BookingRoot).Assembly); + builder.Services.AddAspnetOpenApi(); builder.Services.AddCustomVersioning(); builder.Services.AddCustomMediatR(); builder.Services.AddValidatorsFromAssembly(typeof(BookingRoot).Assembly); @@ -106,7 +104,7 @@ public static WebApplication UseInfrastructure(this WebApplication app) if (env.IsDevelopment()) { - app.UseCustomSwagger(); + app.UseAspnetOpenApi(); } return app; diff --git a/src/Services/Booking/tests/IntegrationTest/Integration.Test.csproj b/src/Services/Booking/tests/IntegrationTest/Integration.Test.csproj index 29cb4ccc..660ce90c 100644 --- a/src/Services/Booking/tests/IntegrationTest/Integration.Test.csproj +++ b/src/Services/Booking/tests/IntegrationTest/Integration.Test.csproj @@ -7,8 +7,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Services/Flight/src/Flight/Data/Migrations/FlightDbContextModelSnapshot.cs b/src/Services/Flight/src/Flight/Data/Migrations/FlightDbContextModelSnapshot.cs index 26192d89..930ce7a0 100644 --- a/src/Services/Flight/src/Flight/Data/Migrations/FlightDbContextModelSnapshot.cs +++ b/src/Services/Flight/src/Flight/Data/Migrations/FlightDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("ProductVersion", "9.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); diff --git a/src/Services/Flight/src/Flight/Data/Seed/FlightDataSeeder.cs b/src/Services/Flight/src/Flight/Data/Seed/FlightDataSeeder.cs index 977199da..3307ed89 100644 --- a/src/Services/Flight/src/Flight/Data/Seed/FlightDataSeeder.cs +++ b/src/Services/Flight/src/Flight/Data/Seed/FlightDataSeeder.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; -using System.Threading.Tasks; using BuildingBlocks.EFCore; using Flight.Aircrafts.Models; using Flight.Airports.Models; +using Flight.Flights.Models; using Flight.Seats.Models; using MapsterMapper; using Microsoft.EntityFrameworkCore; @@ -11,23 +10,12 @@ namespace Flight.Data.Seed; -using Flights.Models; - -public class FlightDataSeeder : IDataSeeder +public class FlightDataSeeder( + FlightDbContext flightDbContext, + FlightReadDbContext flightReadDbContext, + IMapper mapper +) : IDataSeeder { - private readonly FlightDbContext _flightDbContext; - private readonly FlightReadDbContext _flightReadDbContext; - private readonly IMapper _mapper; - - public FlightDataSeeder(FlightDbContext flightDbContext, - FlightReadDbContext flightReadDbContext, - IMapper mapper) - { - _flightDbContext = flightDbContext; - _flightReadDbContext = flightReadDbContext; - _mapper = mapper; - } - public async Task SeedAllAsync() { await SeedAirportAsync(); @@ -38,28 +26,28 @@ public async Task SeedAllAsync() private async Task SeedAirportAsync() { - if (!await _flightDbContext.Airports.AnyAsync()) + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports)) { - await _flightDbContext.Airports.AddRangeAsync(InitialData.Airports); - await _flightDbContext.SaveChangesAsync(); + await flightDbContext.Airports.AddRangeAsync(InitialData.Airports); + await flightDbContext.SaveChangesAsync(); - if (!await _flightReadDbContext.Airport.AsQueryable().AnyAsync()) + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable())) { - await _flightReadDbContext.Airport.InsertManyAsync(_mapper.Map>(InitialData.Airports)); + await flightReadDbContext.Airport.InsertManyAsync(mapper.Map>(InitialData.Airports)); } } } private async Task SeedAircraftAsync() { - if (!await _flightDbContext.Aircraft.AnyAsync()) + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft)) { - await _flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts); - await _flightDbContext.SaveChangesAsync(); + await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts); + await flightDbContext.SaveChangesAsync(); - if (!await _flightReadDbContext.Aircraft.AsQueryable().AnyAsync()) + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable())) { - await _flightReadDbContext.Aircraft.InsertManyAsync(_mapper.Map>(InitialData.Aircrafts)); + await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map>(InitialData.Aircrafts)); } } } @@ -67,28 +55,28 @@ private async Task SeedAircraftAsync() private async Task SeedSeatAsync() { - if (!await _flightDbContext.Seats.AnyAsync()) + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats)) { - await _flightDbContext.Seats.AddRangeAsync(InitialData.Seats); - await _flightDbContext.SaveChangesAsync(); + await flightDbContext.Seats.AddRangeAsync(InitialData.Seats); + await flightDbContext.SaveChangesAsync(); - if (!await _flightReadDbContext.Seat.AsQueryable().AnyAsync()) + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable())) { - await _flightReadDbContext.Seat.InsertManyAsync(_mapper.Map>(InitialData.Seats)); + await flightReadDbContext.Seat.InsertManyAsync(mapper.Map>(InitialData.Seats)); } } } private async Task SeedFlightAsync() { - if (!await _flightDbContext.Flights.AnyAsync()) + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights)) { - await _flightDbContext.Flights.AddRangeAsync(InitialData.Flights); - await _flightDbContext.SaveChangesAsync(); + await flightDbContext.Flights.AddRangeAsync(InitialData.Flights); + await flightDbContext.SaveChangesAsync(); - if (!await _flightReadDbContext.Flight.AsQueryable().AnyAsync()) + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable())) { - await _flightReadDbContext.Flight.InsertManyAsync(_mapper.Map>(InitialData.Flights)); + await flightReadDbContext.Flight.InsertManyAsync(mapper.Map>(InitialData.Flights)); } } } diff --git a/src/Services/Flight/src/Flight/Extensions/Infrastructure/InfrastructureExtensions.cs b/src/Services/Flight/src/Flight/Extensions/Infrastructure/InfrastructureExtensions.cs index 77f040e6..e75c24d3 100644 --- a/src/Services/Flight/src/Flight/Extensions/Infrastructure/InfrastructureExtensions.cs +++ b/src/Services/Flight/src/Flight/Extensions/Infrastructure/InfrastructureExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Threading.RateLimiting; using BuildingBlocks.Core; using BuildingBlocks.EFCore; @@ -9,9 +8,10 @@ using BuildingBlocks.Mapster; using BuildingBlocks.MassTransit; using BuildingBlocks.Mongo; +using BuildingBlocks.OpenApi; using BuildingBlocks.OpenTelemetry; using BuildingBlocks.PersistMessageProcessor; -using BuildingBlocks.Swagger; +using BuildingBlocks.ProblemDetails; using BuildingBlocks.Web; using Figgle; using Flight.Data; @@ -27,7 +27,6 @@ namespace Flight.Extensions.Infrastructure; -using BuildingBlocks.ProblemDetails; public static class InfrastructureExtensions { @@ -38,6 +37,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.Configure(options => { @@ -72,7 +72,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder.Services.AddEndpointsApiExplorer(); builder.AddCustomSerilog(env); builder.Services.AddJwt(); - builder.Services.AddCustomSwagger(configuration, typeof(FlightRoot).Assembly); + builder.Services.AddAspnetOpenApi(); builder.Services.AddCustomVersioning(); builder.Services.AddValidatorsFromAssembly(typeof(FlightRoot).Assembly); builder.Services.AddCustomMapster(typeof(FlightRoot).Assembly); @@ -105,7 +105,7 @@ public static WebApplication UseInfrastructure(this WebApplication app) options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest; }); app.UseCorrelationId(); - app.UseMigration(env); + app.UseMigration(); app.UseCustomHealthCheck(); app.MapGrpcService(); app.UseRateLimiter(); @@ -113,7 +113,7 @@ public static WebApplication UseInfrastructure(this WebApplication app) if (env.IsDevelopment()) { - app.UseCustomSwagger(); + app.UseAspnetOpenApi(); } return app; diff --git a/src/Services/Flight/src/Flight/Flight.csproj b/src/Services/Flight/src/Flight/Flight.csproj index bc49ff99..77a2826c 100644 --- a/src/Services/Flight/src/Flight/Flight.csproj +++ b/src/Services/Flight/src/Flight/Flight.csproj @@ -1,13 +1,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs b/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs index ead41c2f..3f82d405 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs @@ -1,38 +1,44 @@ -namespace Flight.Flights.Features.DeletingFlight.V1; - -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Ardalis.GuardClauses; using BuildingBlocks.Core.CQRS; using BuildingBlocks.Core.Event; using BuildingBlocks.Web; -using Data; using Duende.IdentityServer.EntityFramework.Entities; -using Exceptions; +using Flight.Data; +using Flight.Flights.Exceptions; using FluentValidation; using MediatR; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; -using MongoDB.Driver.Linq; -using ValueObjects; + +namespace Flight.Flights.Features.DeletingFlight.V1; public record DeleteFlight(Guid Id) : ICommand, IInternalCommand; public record DeleteFlightResult(Guid Id); -public record FlightDeletedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate, - Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes, - DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent; +public record FlightDeletedDomainEvent( + Guid Id, + string FlightNumber, + Guid AircraftId, + DateTime DepartureDate, + Guid DepartureAirportId, + DateTime ArriveDate, + Guid ArriveAirportId, + decimal DurationMinutes, + DateTime FlightDate, + Enums.FlightStatus Status, + decimal Price, + bool IsDeleted +) : IDomainEvent; public class DeleteFlightEndpoint : IMinimalEndpoint { public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder) { - builder.MapDelete($"{EndpointConfig.BaseApiPath}/flight/{{id}}", + builder.MapDelete( + $"{EndpointConfig.BaseApiPath}/flight/{{id}}", async (Guid id, IMediator mediator, CancellationToken cancellationToken) => { await mediator.Send(new DeleteFlight(id), cancellationToken); @@ -70,7 +76,10 @@ public DeleteFlightHandler(FlightDbContext flightDbContext) _flightDbContext = flightDbContext; } - public async Task Handle(DeleteFlight request, CancellationToken cancellationToken) + public async Task Handle( + DeleteFlight request, + CancellationToken cancellationToken + ) { Guard.Against.Null(request, nameof(request)); @@ -81,9 +90,18 @@ public async Task Handle(DeleteFlight request, CancellationT throw new FlightNotFountException(); } - flight.Delete(flight.Id, flight.FlightNumber, flight.AircraftId, flight.DepartureAirportId, - flight.DepartureDate, flight.ArriveDate, flight.ArriveAirportId, flight.DurationMinutes, - flight.FlightDate, flight.Status, flight.Price); + flight.Delete( + flight.Id, + flight.FlightNumber, + flight.AircraftId, + flight.DepartureAirportId, + flight.DepartureDate, + flight.ArriveDate, + flight.ArriveAirportId, + flight.DurationMinutes, + flight.FlightDate, + flight.Status, + flight.Price); var deleteFlight = _flightDbContext.Flights.Update(flight).Entity; diff --git a/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs b/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs index 5a27210b..73d435e4 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs @@ -1,3 +1,5 @@ +using MongoDB.Driver.Linq; + namespace Flight.Flights.Features.GettingAvailableFlights.V1; using System; diff --git a/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs b/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs index ea6bf120..5f01b14b 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs @@ -76,9 +76,9 @@ public async Task Handle(GetFlightById request, Cancellatio { Guard.Against.Null(request, nameof(request)); - var flight = - await _flightReadDbContext.Flight.AsQueryable().SingleOrDefaultAsync(x => x.FlightId == request.Id && - !x.IsDeleted, cancellationToken); + var flight = await _flightReadDbContext.Flight.AsQueryable().SingleOrDefaultAsync( + x => x.FlightId == request.Id && + !x.IsDeleted, cancellationToken); if (flight is null) { diff --git a/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs b/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs index 24b0f562..2470176c 100644 --- a/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs +++ b/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs @@ -1,3 +1,5 @@ +using MongoDB.Driver.Linq; + namespace Flight.Seats.Features.GettingAvailableSeats.V1; using System; diff --git a/src/Services/Flight/tests/EndToEndTest/EndToEnd.Test.csproj b/src/Services/Flight/tests/EndToEndTest/EndToEnd.Test.csproj index 6e00b08a..571041a3 100644 --- a/src/Services/Flight/tests/EndToEndTest/EndToEnd.Test.csproj +++ b/src/Services/Flight/tests/EndToEndTest/EndToEnd.Test.csproj @@ -7,8 +7,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Services/Flight/tests/EndToEndTest/FlightTestDataSeeder.cs b/src/Services/Flight/tests/EndToEndTest/FlightTestDataSeeder.cs new file mode 100644 index 00000000..862356f9 --- /dev/null +++ b/src/Services/Flight/tests/EndToEndTest/FlightTestDataSeeder.cs @@ -0,0 +1,85 @@ +using BuildingBlocks.EFCore; +using Flight.Aircrafts.Models; +using Flight.Airports.Models; +using Flight.Data; +using Flight.Data.Seed; +using Flight.Flights.Models; +using Flight.Seats.Models; +using MapsterMapper; +using Microsoft.EntityFrameworkCore; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace EndToEnd.Test; + +public class FlightTestDataSeeder( + FlightDbContext flightDbContext, + FlightReadDbContext flightReadDbContext, + IMapper mapper +) : ITestDataSeeder +{ + public async Task SeedAllAsync() + { + await SeedAirportAsync(); + await SeedAircraftAsync(); + await SeedFlightAsync(); + await SeedSeatAsync(); + } + + private async Task SeedAirportAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports)) + { + await flightDbContext.Airports.AddRangeAsync(InitialData.Airports); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable())) + { + await flightReadDbContext.Airport.InsertManyAsync(mapper.Map>(InitialData.Airports)); + } + } + } + + private async Task SeedAircraftAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft)) + { + await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable())) + { + await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map>(InitialData.Aircrafts)); + } + } + } + + + private async Task SeedSeatAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats)) + { + await flightDbContext.Seats.AddRangeAsync(InitialData.Seats); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable())) + { + await flightReadDbContext.Seat.InsertManyAsync(mapper.Map>(InitialData.Seats)); + } + } + } + + private async Task SeedFlightAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights)) + { + await flightDbContext.Flights.AddRangeAsync(InitialData.Flights); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable())) + { + await flightReadDbContext.Flight.InsertManyAsync(mapper.Map>(InitialData.Flights)); + } + } + } +} diff --git a/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs b/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs index d7b05761..87dacc88 100644 --- a/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs +++ b/src/Services/Flight/tests/IntegrationTest/Flight/Features/CreateFlightTests.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using BuildingBlocks.Contracts.EventBus.Messages; using BuildingBlocks.TestBase; using Flight.Api; diff --git a/src/Services/Flight/tests/IntegrationTest/FlightTestDataSeeder.cs b/src/Services/Flight/tests/IntegrationTest/FlightTestDataSeeder.cs new file mode 100644 index 00000000..3121315e --- /dev/null +++ b/src/Services/Flight/tests/IntegrationTest/FlightTestDataSeeder.cs @@ -0,0 +1,85 @@ +using BuildingBlocks.EFCore; +using Flight.Aircrafts.Models; +using Flight.Airports.Models; +using Flight.Data; +using Flight.Data.Seed; +using Flight.Flights.Models; +using Flight.Seats.Models; +using MapsterMapper; +using Microsoft.EntityFrameworkCore; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace Integration.Test; + +public class FlightTestDataSeeder( + FlightDbContext flightDbContext, + FlightReadDbContext flightReadDbContext, + IMapper mapper +) : ITestDataSeeder +{ + public async Task SeedAllAsync() + { + await SeedAirportAsync(); + await SeedAircraftAsync(); + await SeedFlightAsync(); + await SeedSeatAsync(); + } + + private async Task SeedAirportAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Airports)) + { + await flightDbContext.Airports.AddRangeAsync(InitialData.Airports); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Airport.AsQueryable())) + { + await flightReadDbContext.Airport.InsertManyAsync(mapper.Map>(InitialData.Airports)); + } + } + } + + private async Task SeedAircraftAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Aircraft)) + { + await flightDbContext.Aircraft.AddRangeAsync(InitialData.Aircrafts); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Aircraft.AsQueryable())) + { + await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map>(InitialData.Aircrafts)); + } + } + } + + + private async Task SeedSeatAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Seats)) + { + await flightDbContext.Seats.AddRangeAsync(InitialData.Seats); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Seat.AsQueryable())) + { + await flightReadDbContext.Seat.InsertManyAsync(mapper.Map>(InitialData.Seats)); + } + } + } + + private async Task SeedFlightAsync() + { + if (!await EntityFrameworkQueryableExtensions.AnyAsync(flightDbContext.Flights)) + { + await flightDbContext.Flights.AddRangeAsync(InitialData.Flights); + await flightDbContext.SaveChangesAsync(); + + if (!await MongoQueryable.AnyAsync(flightReadDbContext.Flight.AsQueryable())) + { + await flightReadDbContext.Flight.InsertManyAsync(mapper.Map>(InitialData.Flights)); + } + } + } +} diff --git a/src/Services/Flight/tests/IntegrationTest/Integration.Test.csproj b/src/Services/Flight/tests/IntegrationTest/Integration.Test.csproj index 6e00b08a..571041a3 100644 --- a/src/Services/Flight/tests/IntegrationTest/Integration.Test.csproj +++ b/src/Services/Flight/tests/IntegrationTest/Integration.Test.csproj @@ -7,8 +7,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Services/Flight/tests/UnitTest/Unit.Test.csproj b/src/Services/Flight/tests/UnitTest/Unit.Test.csproj index 6e00b08a..571041a3 100644 --- a/src/Services/Flight/tests/UnitTest/Unit.Test.csproj +++ b/src/Services/Flight/tests/UnitTest/Unit.Test.csproj @@ -7,8 +7,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Services/Identity/src/Identity/Extensions/Infrastructure/InfrastructureExtensions.cs b/src/Services/Identity/src/Identity/Extensions/Infrastructure/InfrastructureExtensions.cs index 38dc7e84..b4efd409 100644 --- a/src/Services/Identity/src/Identity/Extensions/Infrastructure/InfrastructureExtensions.cs +++ b/src/Services/Identity/src/Identity/Extensions/Infrastructure/InfrastructureExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Threading.RateLimiting; using BuildingBlocks.Core; using BuildingBlocks.EFCore; @@ -6,27 +5,26 @@ using BuildingBlocks.Logging; using BuildingBlocks.Mapster; using BuildingBlocks.MassTransit; +using BuildingBlocks.OpenApi; using BuildingBlocks.OpenTelemetry; using BuildingBlocks.PersistMessageProcessor; -using BuildingBlocks.Swagger; +using BuildingBlocks.ProblemDetails; using BuildingBlocks.Web; using Figgle; using FluentValidation; +using Identity.Configurations; using Identity.Data; using Identity.Data.Seed; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Prometheus; using Serilog; namespace Identity.Extensions.Infrastructure; -using BuildingBlocks.ProblemDetails; -using Configurations; -using Microsoft.AspNetCore.HttpOverrides; public static class InfrastructureExtensions { @@ -67,7 +65,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder.Services.AddCustomDbContext(); builder.Services.AddScoped(); builder.AddCustomSerilog(env); - builder.Services.AddCustomSwagger(configuration, typeof(IdentityRoot).Assembly); + builder.Services.AddAspnetOpenApi(); builder.Services.AddCustomVersioning(); builder.Services.AddCustomMediatR(); builder.Services.AddValidatorsFromAssembly(typeof(IdentityRoot).Assembly); @@ -104,8 +102,8 @@ public static WebApplication UseInfrastructure(this WebApplication app) { options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest; }); - app.UseMigration(env); app.UseCorrelationId(); + app.UseMigration(); app.UseCustomHealthCheck(); app.UseIdentityServer(); @@ -113,7 +111,7 @@ public static WebApplication UseInfrastructure(this WebApplication app) if (env.IsDevelopment()) { - app.UseCustomSwagger(); + app.UseAspnetOpenApi(); } return app; diff --git a/src/Services/Identity/src/Identity/Identity.csproj b/src/Services/Identity/src/Identity/Identity.csproj index 4e816923..2ac9aa6f 100644 --- a/src/Services/Identity/src/Identity/Identity.csproj +++ b/src/Services/Identity/src/Identity/Identity.csproj @@ -1,7 +1,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Services/Identity/tests/IntegrationTest/IdentityTestDataSeeder.cs b/src/Services/Identity/tests/IntegrationTest/IdentityTestDataSeeder.cs new file mode 100644 index 00000000..2d2a7bf2 --- /dev/null +++ b/src/Services/Identity/tests/IntegrationTest/IdentityTestDataSeeder.cs @@ -0,0 +1,63 @@ +using BuildingBlocks.Contracts.EventBus.Messages; +using BuildingBlocks.Core; +using BuildingBlocks.EFCore; +using Identity.Data.Seed; +using Identity.Identity.Constants; +using Identity.Identity.Models; +using Microsoft.AspNetCore.Identity; + +namespace Integration.Test; + +public class IdentityDataSeeder( + UserManager userManager, + RoleManager roleManager, + IEventDispatcher eventDispatcher +) + : IDataSeeder +{ + public async Task SeedAllAsync() + { + await SeedRoles(); + await SeedUsers(); + } + + private async Task SeedRoles() + { + if (await roleManager.RoleExistsAsync(Constants.Role.Admin) == false) + { + await roleManager.CreateAsync(new Role { Name = Constants.Role.Admin }); + } + + if (await roleManager.RoleExistsAsync(Constants.Role.User) == false) + { + await roleManager.CreateAsync(new Role { Name = Constants.Role.User }); + } + } + + private async Task SeedUsers() + { + if (await userManager.FindByNameAsync("samh") == null) + { + var result = await userManager.CreateAsync(InitialData.Users.First(), "Admin@123456"); + + if (result.Succeeded) + { + await userManager.AddToRoleAsync(InitialData.Users.First(), Constants.Role.Admin); + + await eventDispatcher.SendAsync(new UserCreated(InitialData.Users.First().Id, InitialData.Users.First().FirstName + " " + InitialData.Users.First().LastName, InitialData.Users.First().PassPortNumber)); + } + } + + if (await userManager.FindByNameAsync("meysamh2") == null) + { + var result = await userManager.CreateAsync(InitialData.Users.Last(), "User@123456"); + + if (result.Succeeded) + { + await userManager.AddToRoleAsync(InitialData.Users.Last(), Constants.Role.User); + + await eventDispatcher.SendAsync(new UserCreated(InitialData.Users.Last().Id, InitialData.Users.Last().FirstName + " " + InitialData.Users.Last().LastName, InitialData.Users.Last().PassPortNumber)); + } + } + } +} diff --git a/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj b/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj index 360c1055..e3230589 100644 --- a/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj +++ b/src/Services/Identity/tests/IntegrationTest/Integration.Test.csproj @@ -7,8 +7,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Services/Passenger/src/Passenger/Extensions/Infrastructure/InfrastructureExtensions.cs b/src/Services/Passenger/src/Passenger/Extensions/Infrastructure/InfrastructureExtensions.cs index ef9bd451..024250eb 100644 --- a/src/Services/Passenger/src/Passenger/Extensions/Infrastructure/InfrastructureExtensions.cs +++ b/src/Services/Passenger/src/Passenger/Extensions/Infrastructure/InfrastructureExtensions.cs @@ -8,9 +8,10 @@ using BuildingBlocks.Mapster; using BuildingBlocks.MassTransit; using BuildingBlocks.Mongo; +using BuildingBlocks.OpenApi; using BuildingBlocks.OpenTelemetry; using BuildingBlocks.PersistMessageProcessor; -using BuildingBlocks.Swagger; +using BuildingBlocks.ProblemDetails; using BuildingBlocks.Web; using Figgle; using FluentValidation; @@ -25,7 +26,6 @@ namespace Passenger.Extensions.Infrastructure; -using BuildingBlocks.ProblemDetails; public static class InfrastructureExtensions { @@ -67,7 +67,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder builder.AddCustomSerilog(env); builder.Services.AddJwt(); builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddCustomSwagger(configuration, typeof(PassengerRoot).Assembly); + builder.Services.AddAspnetOpenApi(); builder.Services.AddCustomVersioning(); builder.Services.AddCustomMediatR(); builder.Services.AddValidatorsFromAssembly(typeof(PassengerRoot).Assembly); @@ -98,15 +98,15 @@ public static WebApplication UseInfrastructure(this WebApplication app) { options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest; }); - app.UseMigration(env); app.UseCorrelationId(); + app.UseMigration(); app.UseCustomHealthCheck(); app.MapGrpcService(); app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name)); if (env.IsDevelopment()) { - app.UseCustomSwagger(); + app.UseAspnetOpenApi(); } return app; diff --git a/src/Services/Passenger/src/Passenger/Passenger.csproj b/src/Services/Passenger/src/Passenger/Passenger.csproj index 015938e8..db24d609 100644 --- a/src/Services/Passenger/src/Passenger/Passenger.csproj +++ b/src/Services/Passenger/src/Passenger/Passenger.csproj @@ -1,8 +1,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj b/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj index 0ae4574d..ac65886b 100644 --- a/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj +++ b/src/Services/Passenger/tests/IntegrationTest/Integration.Test.csproj @@ -7,8 +7,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all