mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-14 20:54:05 +08:00
feat: update dotnet to version 9
This commit is contained in:
parent
42c300fd96
commit
4785a680da
@ -10,11 +10,11 @@
|
||||
"rollForward": false
|
||||
},
|
||||
"dotnet-ef": {
|
||||
"version": "8.0.8",
|
||||
"version": "9.0.0",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
@ -10,22 +10,22 @@
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" Version="1.1.118">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.163">
|
||||
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.182">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.12.5">
|
||||
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.12.9">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.12.5">
|
||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.12.9">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.12.5">
|
||||
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.12.9">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.11.20">
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.12.19">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="8.0.0">
|
||||
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="9.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="all" Version="0.15.0">
|
||||
|
||||
25
README.md
25
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.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.401",
|
||||
"version": "9.0.100",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ardalis.GuardClauses" Version="4.6.0" />
|
||||
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
|
||||
<PackageReference Include="Asp.Versioning.Abstractions" Version="8.1.0" />
|
||||
<PackageReference Include="Asp.Versioning.Http" Version="8.1.0" />
|
||||
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
|
||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" />
|
||||
@ -19,41 +20,42 @@
|
||||
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.0" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||
<PackageReference Include="Figgle" Version="0.5.1" />
|
||||
<PackageReference Include="FluentValidation" Version="11.10.0" />
|
||||
<PackageReference Include="FluentValidation" Version="11.11.0" />
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="8.0.4" />
|
||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="9.0.0" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.1" />
|
||||
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.10.0-beta.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.9.0-beta.1" />
|
||||
<PackageReference Include="Polly" Version="8.4.1" />
|
||||
<PackageReference Include="Polly" Version="8.5.0" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="IdGen" Version="3.0.7" />
|
||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
|
||||
<PackageReference Include="MediatR" Version="12.4.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
||||
<PackageReference Include="Scrutor" Version="4.2.2" />
|
||||
<PackageReference Include="Sentry.Serilog" Version="4.10.2" />
|
||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
||||
<PackageReference Include="Scalar.AspNetCore" Version="1.2.64" />
|
||||
<PackageReference Include="Scrutor" Version="5.0.2" />
|
||||
<PackageReference Include="Sentry.Serilog" Version="4.13.0" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
|
||||
@ -63,48 +65,53 @@
|
||||
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
|
||||
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.5" />
|
||||
<PackageReference Include="Sieve" Version="2.5.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.7.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
|
||||
<PackageReference Include="MassTransit" Version="8.2.5" />
|
||||
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.5" />
|
||||
<PackageReference Include="Duende.IdentityServer" Version="7.0.6" />
|
||||
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.0.6" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.0.6" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.1.0" />
|
||||
<PackageReference Include="MassTransit" Version="8.3.3" />
|
||||
<PackageReference Include="MassTransit.RabbitMQ" Version="8.3.3" />
|
||||
<PackageReference Include="Duende.IdentityServer" Version="7.0.8" />
|
||||
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.0.8" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.0.8" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
|
||||
<PackageReference Include="Testcontainers" Version="3.10.0" />
|
||||
<PackageReference Include="Testcontainers.EventStoreDb" Version="3.10.0" />
|
||||
<PackageReference Include="Testcontainers.MongoDb" Version="3.10.0" />
|
||||
<PackageReference Include="Testcontainers.PostgreSql" Version="3.10.0" />
|
||||
<PackageReference Include="Testcontainers.RabbitMq" Version="3.10.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.0.0" />
|
||||
<PackageReference Include="Testcontainers.EventStoreDb" Version="4.0.0" />
|
||||
<PackageReference Include="Testcontainers.MongoDb" Version="4.0.0" />
|
||||
<PackageReference Include="Testcontainers.PostgreSql" Version="4.0.0" />
|
||||
<PackageReference Include="Testcontainers.RabbitMq" Version="4.0.0" />
|
||||
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />
|
||||
<PackageReference Include="Yarp.ReverseProxy" Version="2.2.0" />
|
||||
|
||||
<PackageReference Include="prometheus-net" Version="8.2.1" />
|
||||
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
|
||||
|
||||
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry" Version="1.10.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.6.0-rc.1" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.10.0" />
|
||||
|
||||
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.5" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
||||
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.7" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
|
||||
|
||||
<PackageReference Include="AutoBogus" Version="2.13.1" />
|
||||
<PackageReference Include="Bogus" Version="35.6.1" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||
<PackageReference Include="FluentAssertions" Version="7.0.0" />
|
||||
<PackageReference Include="Respawn" Version="6.2.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
|
||||
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="8.0.1" />
|
||||
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.1" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.65.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.29.1" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.67.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -9,30 +9,17 @@ using MessageEnvelope = BuildingBlocks.Core.Event.MessageEnvelope;
|
||||
|
||||
namespace BuildingBlocks.Core;
|
||||
|
||||
public sealed class EventDispatcher : IEventDispatcher
|
||||
public sealed class EventDispatcher(
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
IEventMapper eventMapper,
|
||||
ILogger<EventDispatcher> logger,
|
||||
IPersistMessageProcessor persistMessageProcessor,
|
||||
IHttpContextAccessor httpContextAccessor
|
||||
)
|
||||
: IEventDispatcher
|
||||
{
|
||||
private readonly IEventMapper _eventMapper;
|
||||
private readonly ILogger<EventDispatcher> _logger;
|
||||
private readonly IPersistMessageProcessor _persistMessageProcessor;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public EventDispatcher(IServiceScopeFactory serviceScopeFactory,
|
||||
IEventMapper eventMapper,
|
||||
ILogger<EventDispatcher> logger,
|
||||
IPersistMessageProcessor persistMessageProcessor,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_eventMapper = eventMapper;
|
||||
_logger = logger;
|
||||
_persistMessageProcessor = persistMessageProcessor;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
|
||||
public async Task SendAsync<T>(IReadOnlyList<T> events, Type type = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
CancellationToken cancellationToken = default)
|
||||
where T : IEvent
|
||||
{
|
||||
if (events.Count > 0)
|
||||
@ -45,7 +32,7 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
{
|
||||
foreach (var integrationEvent in integrationEvents)
|
||||
{
|
||||
await _persistMessageProcessor.PublishMessageAsync(
|
||||
await persistMessageProcessor.PublishMessageAsync(
|
||||
new MessageEnvelope(integrationEvent, SetHeaders()),
|
||||
cancellationToken);
|
||||
}
|
||||
@ -74,7 +61,7 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
|
||||
foreach (var internalMessage in internalMessages)
|
||||
{
|
||||
await _persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
|
||||
await persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,20 +76,20 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
private Task<IReadOnlyList<IIntegrationEvent>> MapDomainEventToIntegrationEventAsync(
|
||||
IReadOnlyList<IDomainEvent> 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<IReadOnlyList<IIntegrationEvent>>(wrappedIntegrationEvents);
|
||||
|
||||
var integrationEvents = new List<IIntegrationEvent>();
|
||||
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 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
integrationEvents.Add(integrationEvent);
|
||||
}
|
||||
|
||||
_logger.LogTrace("Processing integration events done...");
|
||||
logger.LogTrace("Processing integration events done...");
|
||||
|
||||
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(integrationEvents);
|
||||
}
|
||||
@ -119,16 +106,16 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
private Task<IReadOnlyList<IInternalCommand>> MapDomainEventToInternalCommandAsync(
|
||||
IReadOnlyList<IDomainEvent> events)
|
||||
{
|
||||
_logger.LogTrace("Processing internal message start...");
|
||||
logger.LogTrace("Processing internal message start...");
|
||||
|
||||
var internalCommands = new List<IInternalCommand>();
|
||||
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 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
internalCommands.Add(integrationEvent);
|
||||
}
|
||||
|
||||
_logger.LogTrace("Processing internal message done...");
|
||||
logger.LogTrace("Processing internal message done...");
|
||||
|
||||
return Task.FromResult<IReadOnlyList<IInternalCommand>>(internalCommands);
|
||||
}
|
||||
@ -159,9 +146,9 @@ public sealed class EventDispatcher : IEventDispatcher
|
||||
private IDictionary<string, object> SetHeaders()
|
||||
{
|
||||
var headers = new Dictionary<string, object>();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new Exception("try for find IAggregate", ex);
|
||||
throw new System.Exception("try for find IAggregate", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
@ -2,49 +2,38 @@ using System.Text.Json;
|
||||
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<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : notnull, IRequest<TResponse>
|
||||
where TResponse : notnull
|
||||
public class EfTxBehavior<TRequest, TResponse>(
|
||||
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
|
||||
IDbContext dbContextBase,
|
||||
IPersistMessageDbContext persistMessageDbContext,
|
||||
IEventDispatcher eventDispatcher
|
||||
)
|
||||
: IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : notnull, IRequest<TResponse>
|
||||
where TResponse : notnull
|
||||
{
|
||||
private readonly ILogger<EfTxBehavior<TRequest, TResponse>> _logger;
|
||||
private readonly IDbContext _dbContextBase;
|
||||
private readonly IPersistMessageDbContext _persistMessageDbContext;
|
||||
private readonly IEventDispatcher _eventDispatcher;
|
||||
|
||||
public EfTxBehavior(
|
||||
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
|
||||
IDbContext dbContextBase,
|
||||
IPersistMessageDbContext persistMessageDbContext,
|
||||
IEventDispatcher eventDispatcher)
|
||||
{
|
||||
_logger = logger;
|
||||
_dbContextBase = dbContextBase;
|
||||
_persistMessageDbContext = persistMessageDbContext;
|
||||
_eventDispatcher = eventDispatcher;
|
||||
}
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken cancellationToken)
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
logger.LogInformation(
|
||||
"{Prefix} Handled command {MediatrRequest}",
|
||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||
typeof(TRequest).FullName);
|
||||
|
||||
_logger.LogDebug(
|
||||
logger.LogDebug(
|
||||
"{Prefix} Handled command {MediatrRequest} with content {RequestContent}",
|
||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||
typeof(TRequest).FullName,
|
||||
JsonSerializer.Serialize(request));
|
||||
|
||||
_logger.LogInformation(
|
||||
logger.LogInformation(
|
||||
"{Prefix} Open the transaction for {MediatrRequest}",
|
||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||
typeof(TRequest).FullName);
|
||||
@ -56,32 +45,32 @@ public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TRe
|
||||
|
||||
var response = await next();
|
||||
|
||||
_logger.LogInformation(
|
||||
logger.LogInformation(
|
||||
"{Prefix} Executed the {MediatrRequest} request",
|
||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||
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();
|
||||
|
||||
@ -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<TContext>(this IServiceCollection services)
|
||||
@ -36,32 +37,30 @@ public static class Extensions
|
||||
{
|
||||
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<IDbContext>(provider => provider.GetService<TContext>());
|
||||
services.AddScoped<ISeedManager, SeedManager>();
|
||||
services.AddScoped<IDbContext>(sp => sp.GetRequiredService<TContext>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMigration<TContext>(
|
||||
this IApplicationBuilder app,
|
||||
IWebHostEnvironment env
|
||||
)
|
||||
|
||||
public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilder app)
|
||||
where TContext : DbContext, IDbContext
|
||||
{
|
||||
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
|
||||
MigrateAsync<TContext>(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 class Extensions
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task MigrateDatabaseAsync<TContext>(IServiceProvider serviceProvider)
|
||||
private static async Task MigrateAsync<TContext>(IServiceProvider serviceProvider)
|
||||
where TContext : DbContext, IDbContext
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
|
||||
await using var scope = serviceProvider.CreateAsyncScope();
|
||||
var context = scope.ServiceProvider.GetRequiredService<TContext>();
|
||||
await context.Database.MigrateAsync();
|
||||
}
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TContext>>();
|
||||
|
||||
private static async Task SeedDataAsync(IServiceProvider serviceProvider)
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||
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<ISeedManager>();
|
||||
|
||||
await seedersManager.ExecuteAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,4 +4,8 @@ namespace BuildingBlocks.EFCore
|
||||
{
|
||||
Task SeedAllAsync();
|
||||
}
|
||||
|
||||
public interface ITestDataSeeder : IDataSeeder
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
6
src/BuildingBlocks/EFCore/ISeedManager.cs
Normal file
6
src/BuildingBlocks/EFCore/ISeedManager.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace BuildingBlocks.EFCore;
|
||||
|
||||
public interface ISeedManager
|
||||
{
|
||||
Task ExecuteAsync();
|
||||
}
|
||||
39
src/BuildingBlocks/EFCore/SeedManagers.cs
Normal file
39
src/BuildingBlocks/EFCore/SeedManagers.cs
Normal file
@ -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<ILogger<SeedManager>>();
|
||||
var env = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
|
||||
var dataSeeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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<Func<Task>> _commands;
|
||||
private static readonly bool _isSerializerRegisterd;
|
||||
|
||||
static MongoDbContext()
|
||||
{
|
||||
BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));
|
||||
}
|
||||
|
||||
public MongoDbContext(IOptions<MongoOptions> options)
|
||||
{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Linq.Expressions;
|
||||
using BuildingBlocks.Core.Model;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
|
||||
namespace BuildingBlocks.Mongo;
|
||||
|
||||
|
||||
55
src/BuildingBlocks/OpenApi/Extensions.cs
Normal file
55
src/BuildingBlocks/OpenApi/Extensions.cs
Normal file
@ -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<SecuritySchemeDocumentTransformer>();
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace BuildingBlocks.PersistMessageProcessor.Data.Configurations;
|
||||
|
||||
public class PersistMessageConfiguration : IEntityTypeConfiguration<PersistMessage>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PersistMessage> 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));
|
||||
}
|
||||
}
|
||||
@ -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<PersistMessageOptions>();
|
||||
|
||||
services.AddDbContext<PersistMessageDbContext>((sp, options) =>
|
||||
{
|
||||
var persistMessageOptions = sp.GetRequiredService<PersistMessageOptions>();
|
||||
services.AddDbContext<PersistMessageDbContext>(
|
||||
(sp, options) =>
|
||||
{
|
||||
var persistMessageOptions = sp.GetRequiredService<PersistMessageOptions>();
|
||||
|
||||
options.UseNpgsql(persistMessageOptions.ConnectionString,
|
||||
dbOptions =>
|
||||
{
|
||||
dbOptions.MigrationsAssembly(typeof(PersistMessageDbContext).Assembly.GetName().Name);
|
||||
})
|
||||
// https://github.com/efcore/EFCore.NamingConventions
|
||||
.UseSnakeCaseNamingConvention();
|
||||
});
|
||||
options.UseNpgsql(
|
||||
persistMessageOptions.ConnectionString,
|
||||
dbOptions =>
|
||||
{
|
||||
dbOptions.MigrationsAssembly(
|
||||
typeof(PersistMessageDbContext).Assembly.GetName().Name);
|
||||
})
|
||||
// https://github.com/efcore/EFCore.NamingConventions
|
||||
.UseSnakeCaseNamingConvention();
|
||||
|
||||
services.AddScoped<IPersistMessageDbContext>(provider =>
|
||||
{
|
||||
var persistMessageDbContext = provider.GetRequiredService<PersistMessageDbContext>();
|
||||
// Todo: follow up the issues of .net 9 to use better approach taht will provide by .net!
|
||||
options.ConfigureWarnings(
|
||||
w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
|
||||
});
|
||||
|
||||
persistMessageDbContext.Database.EnsureCreated();
|
||||
persistMessageDbContext.CreatePersistMessageTable();
|
||||
services.AddScoped<IPersistMessageDbContext>(
|
||||
provider =>
|
||||
{
|
||||
var persistMessageDbContext =
|
||||
provider.GetRequiredService<PersistMessageDbContext>();
|
||||
|
||||
return persistMessageDbContext;
|
||||
});
|
||||
persistMessageDbContext.Database.EnsureCreated();
|
||||
persistMessageDbContext.CreatePersistMessageTableIfNotExists();
|
||||
|
||||
return persistMessageDbContext;
|
||||
});
|
||||
|
||||
services.AddScoped<IPersistMessageProcessor, PersistMessageProcessor>();
|
||||
|
||||
if (env.EnvironmentName != "test")
|
||||
{
|
||||
services.AddHostedService<PersistMessageBackgroundService>();
|
||||
}
|
||||
services.AddHostedService<PersistMessageBackgroundService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ namespace BuildingBlocks.PersistMessageProcessor;
|
||||
|
||||
public interface IPersistMessageDbContext
|
||||
{
|
||||
DbSet<PersistMessage> PersistMessages { get; }
|
||||
DbSet<PersistMessage> PersistMessage { get; }
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
Task ExecuteTransactionalAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@ namespace BuildingBlocks.PersistMessageProcessor;
|
||||
[Flags]
|
||||
public enum MessageDeliveryType
|
||||
{
|
||||
Unknown = 0,
|
||||
Outbox = 1,
|
||||
Inbox = 2,
|
||||
Internal = 4
|
||||
Internal = 3
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ namespace BuildingBlocks.PersistMessageProcessor;
|
||||
|
||||
public enum MessageStatus
|
||||
{
|
||||
Unknown = 0,
|
||||
InProgress = 1,
|
||||
Processed = 2
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using BuildingBlocks.Core.Model;
|
||||
namespace BuildingBlocks.PersistMessageProcessor;
|
||||
|
||||
using Core.Model;
|
||||
|
||||
public class PersistMessage : IVersion
|
||||
{
|
||||
|
||||
@ -5,27 +5,20 @@ using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BuildingBlocks.PersistMessageProcessor;
|
||||
|
||||
public class PersistMessageBackgroundService : BackgroundService
|
||||
public class PersistMessageBackgroundService(
|
||||
ILogger<PersistMessageBackgroundService> logger,
|
||||
IServiceProvider serviceProvider,
|
||||
IOptions<PersistMessageOptions> options
|
||||
)
|
||||
: BackgroundService
|
||||
{
|
||||
private readonly ILogger<PersistMessageBackgroundService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private PersistMessageOptions _options;
|
||||
private PersistMessageOptions _options = options.Value;
|
||||
|
||||
private Task? _executingTask;
|
||||
|
||||
public PersistMessageBackgroundService(
|
||||
ILogger<PersistMessageBackgroundService> logger,
|
||||
IServiceProvider serviceProvider,
|
||||
IOptions<PersistMessageOptions> 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 @@ public class PersistMessageBackgroundService : BackgroundService
|
||||
|
||||
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 @@ public class PersistMessageBackgroundService : BackgroundService
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await using (var scope = _serviceProvider.CreateAsyncScope())
|
||||
await using (var scope = serviceProvider.CreateAsyncScope())
|
||||
{
|
||||
var service = scope.ServiceProvider.GetRequiredService<IPersistMessageProcessor>();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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<PersistMessageDbContext>? _logger;
|
||||
@ -20,11 +17,10 @@ public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
|
||||
public DbSet<PersistMessage> PersistMessage => Set<PersistMessage>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.ApplyConfiguration(new PersistMessageConfiguration());
|
||||
base.OnModelCreating(builder);
|
||||
builder.ToSnakeCaseTables();
|
||||
}
|
||||
@ -79,13 +75,8 @@ public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new Exception("try for find IVersion", ex);
|
||||
throw new System.Exception("try for find IVersion", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,13 +54,13 @@ public class PersistMessageProcessor : IPersistMessageProcessor
|
||||
public async Task<IReadOnlyList<PersistMessage>> GetByFilterAsync(Expression<Func<PersistMessage, bool>> predicate,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return (await _persistMessageDbContext.PersistMessages.Where(predicate).ToListAsync(cancellationToken))
|
||||
return (await _persistMessageDbContext.PersistMessage.Where(predicate).ToListAsync(cancellationToken))
|
||||
.AsReadOnly();
|
||||
}
|
||||
|
||||
public Task<PersistMessage> 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 class PersistMessageProcessor : IPersistMessageProcessor
|
||||
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 @@ public class PersistMessageProcessor : IPersistMessageProcessor
|
||||
|
||||
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 class PersistMessageProcessor : IPersistMessageProcessor
|
||||
|
||||
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 @@ public class PersistMessageProcessor : IPersistMessageProcessor
|
||||
else
|
||||
id = NewId.NextGuid();
|
||||
|
||||
await _persistMessageDbContext.PersistMessages.AddAsync(
|
||||
await _persistMessageDbContext.PersistMessage.AddAsync(
|
||||
new PersistMessage(
|
||||
id,
|
||||
messageEnvelope.Message.GetType().ToString(),
|
||||
@ -215,7 +215,7 @@ public class PersistMessageProcessor : IPersistMessageProcessor
|
||||
{
|
||||
message.ChangeState(MessageStatus.Processed);
|
||||
|
||||
_persistMessageDbContext.PersistMessages.Update(message);
|
||||
_persistMessageDbContext.PersistMessage.Update(message);
|
||||
|
||||
await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
@ -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<SwaggerGenOptions>
|
||||
{
|
||||
private readonly IApiVersionDescriptionProvider provider;
|
||||
private readonly SwaggerOptions? _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
|
||||
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IOptions<SwaggerOptions> options)
|
||||
{
|
||||
this.provider = provider;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
|
||||
services.AddOptions<SwaggerOptions>().Bind(configuration.GetSection(nameof(SwaggerOptions)))
|
||||
.ValidateDataAnnotations();
|
||||
|
||||
services.AddSwaggerGen(
|
||||
options =>
|
||||
{
|
||||
options.OperationFilter<SwaggerDefaultValues>();
|
||||
|
||||
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<TagByApiExplorerSettingsOperationFilter>();
|
||||
// options.OperationFilter<TagBySwaggerOperationFilter>();
|
||||
|
||||
// Enables Swagger annotations (SwaggerOperationAttribute, SwaggerParameterAttribute etc.)
|
||||
// options.EnableAnnotations();
|
||||
});
|
||||
|
||||
// services.Configure<SwaggerGeneratorOptions>(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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using Ardalis.GuardClauses;
|
||||
@ -9,7 +10,6 @@ using BuildingBlocks.PersistMessageProcessor;
|
||||
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.AspNetCore.Mvc.Testing;
|
||||
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 @@ using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace BuildingBlocks.TestBase;
|
||||
|
||||
using System.Globalization;
|
||||
using Npgsql;
|
||||
using Testcontainers.EventStoreDb;
|
||||
using Testcontainers.MongoDb;
|
||||
using Testcontainers.PostgreSql;
|
||||
using Testcontainers.RabbitMq;
|
||||
|
||||
public class TestFixture<TEntryPoint> : IAsyncLifetime
|
||||
where TEntryPoint : class
|
||||
@ -50,8 +51,7 @@ where TEntryPoint : class
|
||||
public EventStoreDbContainer EventStoreDbTestContainer;
|
||||
public CancellationTokenSource CancellationTokenSource;
|
||||
|
||||
public PersistMessageBackgroundService PersistMessageBackgroundService =>
|
||||
ServiceProvider.GetRequiredService<PersistMessageBackgroundService>();
|
||||
public PersistMessageBackgroundService PersistMessageBackgroundService => ServiceProvider.GetRequiredService<PersistMessageBackgroundService>();
|
||||
|
||||
public HttpClient HttpClient
|
||||
{
|
||||
@ -95,9 +95,17 @@ where TEntryPoint : class
|
||||
{
|
||||
TestRegistrationServices?.Invoke(services);
|
||||
services.ReplaceSingleton(AddHttpContextAccessorMock);
|
||||
services.RemoveAll<IHostedService>();
|
||||
|
||||
services.AddSingleton<PersistMessageBackgroundService>();
|
||||
|
||||
// Register all ITestDataSeeder implementations dynamically
|
||||
services.Scan(scan => scan
|
||||
.FromApplicationDependencies() // Scan the current app and its dependencies
|
||||
.AddClasses(classes => classes.AssignableTo<ITestDataSeeder>()) // 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 @@ where TEntryPoint : class
|
||||
var result = await WaitUntilConditionMet(
|
||||
async () =>
|
||||
{
|
||||
var published =
|
||||
await TestHarness.Published.Any<TMessage>(cancellationToken);
|
||||
|
||||
var faulty =
|
||||
await TestHarness.Published.Any<Fault<TMessage>>(
|
||||
cancellationToken);
|
||||
|
||||
return published && faulty == false;
|
||||
var published = await TestHarness.Published.Any<TMessage>(cancellationToken);
|
||||
return published;
|
||||
});
|
||||
|
||||
return result;
|
||||
@ -224,10 +226,7 @@ where TEntryPoint : class
|
||||
var consumed =
|
||||
await TestHarness.Consumed.Any<TMessage>(cancellationToken);
|
||||
|
||||
var faulty =
|
||||
await TestHarness.Consumed.Any<Fault<TMessage>>(cancellationToken);
|
||||
|
||||
return consumed && faulty == false;
|
||||
return consumed;
|
||||
});
|
||||
|
||||
return result;
|
||||
@ -611,8 +610,6 @@ where TEntryPoint : class
|
||||
_reSpawnerDefaultDb = await Respawner.CreateAsync(
|
||||
DefaultDbConnection,
|
||||
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });
|
||||
|
||||
await SeedDataAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,18 +677,6 @@ where TEntryPoint : class
|
||||
protected virtual void RegisterTestsServices(IServiceCollection services)
|
||||
{
|
||||
}
|
||||
|
||||
private async Task SeedDataAsync()
|
||||
{
|
||||
using var scope = Fixture.ServiceProvider.CreateScope();
|
||||
|
||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||
|
||||
foreach (var seeder in seeders)
|
||||
{
|
||||
await seeder.SeedAllAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class TestReadBase<TEntryPoint, TRContext> : TestFixtureCore<TEntryPoint>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grpc.Tools" Version="2.66.0">
|
||||
<PackageReference Include="Grpc.Tools" Version="2.68.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@ -8,9 +8,10 @@ using BuildingBlocks.Logging;
|
||||
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.Http;
|
||||
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 class InfrastructureExtensions
|
||||
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 class InfrastructureExtensions
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseCustomSwagger();
|
||||
app.UseAspnetOpenApi();
|
||||
}
|
||||
|
||||
return app;
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@ -17,7 +17,7 @@ namespace Flight.Data.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.2")
|
||||
.HasAnnotation("ProductVersion", "9.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
@ -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 @@ using MongoDB.Driver.Linq;
|
||||
|
||||
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 class FlightDataSeeder : IDataSeeder
|
||||
|
||||
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<List<AirportReadModel>>(InitialData.Airports));
|
||||
await flightReadDbContext.Airport.InsertManyAsync(mapper.Map<List<AirportReadModel>>(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<List<AircraftReadModel>>(InitialData.Aircrafts));
|
||||
await flightReadDbContext.Aircraft.InsertManyAsync(mapper.Map<List<AircraftReadModel>>(InitialData.Aircrafts));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,28 +55,28 @@ public class FlightDataSeeder : IDataSeeder
|
||||
|
||||
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<List<SeatReadModel>>(InitialData.Seats));
|
||||
await flightReadDbContext.Seat.InsertManyAsync(mapper.Map<List<SeatReadModel>>(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<List<FlightReadModel>>(InitialData.Flights));
|
||||
await flightReadDbContext.Flight.InsertManyAsync(mapper.Map<List<FlightReadModel>>(InitialData.Flights));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Threading.RateLimiting;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -9,9 +8,10 @@ using BuildingBlocks.Logging;
|
||||
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 @@ using Serilog;
|
||||
|
||||
namespace Flight.Extensions.Infrastructure;
|
||||
|
||||
using BuildingBlocks.ProblemDetails;
|
||||
|
||||
public static class InfrastructureExtensions
|
||||
{
|
||||
@ -38,6 +37,7 @@ public static class InfrastructureExtensions
|
||||
|
||||
builder.Services.AddScoped<ICurrentUserProvider, CurrentUserProvider>();
|
||||
builder.Services.AddScoped<IEventMapper, EventMapper>();
|
||||
|
||||
builder.Services.AddScoped<IEventDispatcher, EventDispatcher>();
|
||||
builder.Services.Configure<ApiBehaviorOptions>(options =>
|
||||
{
|
||||
@ -72,7 +72,7 @@ public static class InfrastructureExtensions
|
||||
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 class InfrastructureExtensions
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
});
|
||||
app.UseCorrelationId();
|
||||
app.UseMigration<FlightDbContext>(env);
|
||||
app.UseMigration<FlightDbContext>();
|
||||
app.UseCustomHealthCheck();
|
||||
app.MapGrpcService<FlightGrpcServices>();
|
||||
app.UseRateLimiter();
|
||||
@ -113,7 +113,7 @@ public static class InfrastructureExtensions
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseCustomSwagger();
|
||||
app.UseAspnetOpenApi();
|
||||
}
|
||||
|
||||
return app;
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.65.0" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.66.0">
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.68.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@ -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<DeleteFlightResult>, 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 @@ internal class DeleteFlightHandler : ICommandHandler<DeleteFlight, DeleteFlightR
|
||||
_flightDbContext = flightDbContext;
|
||||
}
|
||||
|
||||
public async Task<DeleteFlightResult> Handle(DeleteFlight request, CancellationToken cancellationToken)
|
||||
public async Task<DeleteFlightResult> Handle(
|
||||
DeleteFlight request,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
Guard.Against.Null(request, nameof(request));
|
||||
|
||||
@ -81,9 +90,18 @@ internal class DeleteFlightHandler : ICommandHandler<DeleteFlight, DeleteFlightR
|
||||
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;
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using MongoDB.Driver.Linq;
|
||||
|
||||
namespace Flight.Flights.Features.GettingAvailableFlights.V1;
|
||||
|
||||
using System;
|
||||
|
||||
@ -76,9 +76,9 @@ internal class GetFlightByIdHandler : IQueryHandler<GetFlightById, GetFlightById
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using MongoDB.Driver.Linq;
|
||||
|
||||
namespace Flight.Seats.Features.GettingAvailableSeats.V1;
|
||||
|
||||
using System;
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@ -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<List<AirportReadModel>>(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<List<AircraftReadModel>>(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<List<SeatReadModel>>(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<List<FlightReadModel>>(InitialData.Flights));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.TestBase;
|
||||
using Flight.Api;
|
||||
|
||||
@ -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<List<AirportReadModel>>(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<List<AircraftReadModel>>(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<List<SeatReadModel>>(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<List<FlightReadModel>>(InitialData.Flights));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Threading.RateLimiting;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -6,27 +5,26 @@ using BuildingBlocks.HealthCheck;
|
||||
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 class InfrastructureExtensions
|
||||
builder.Services.AddCustomDbContext<IdentityContext>();
|
||||
builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>();
|
||||
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 class InfrastructureExtensions
|
||||
{
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
});
|
||||
app.UseMigration<IdentityContext>(env);
|
||||
app.UseCorrelationId();
|
||||
app.UseMigration<IdentityContext>();
|
||||
app.UseCustomHealthCheck();
|
||||
app.UseIdentityServer();
|
||||
|
||||
@ -113,7 +111,7 @@ public static class InfrastructureExtensions
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseCustomSwagger();
|
||||
app.UseAspnetOpenApi();
|
||||
}
|
||||
|
||||
return app;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@ -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<User> userManager,
|
||||
RoleManager<Role> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@ -8,9 +8,10 @@ using BuildingBlocks.Logging;
|
||||
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 @@ using Serilog;
|
||||
|
||||
namespace Passenger.Extensions.Infrastructure;
|
||||
|
||||
using BuildingBlocks.ProblemDetails;
|
||||
|
||||
public static class InfrastructureExtensions
|
||||
{
|
||||
@ -67,7 +67,7 @@ public static class InfrastructureExtensions
|
||||
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 class InfrastructureExtensions
|
||||
{
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
});
|
||||
app.UseMigration<PassengerDbContext>(env);
|
||||
app.UseCorrelationId();
|
||||
app.UseMigration<PassengerDbContext>();
|
||||
app.UseCustomHealthCheck();
|
||||
app.MapGrpcService<PassengerGrpcServices>();
|
||||
app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseCustomSwagger();
|
||||
app.UseAspnetOpenApi();
|
||||
}
|
||||
|
||||
return app;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.65.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user