diff --git a/src/BuildingBlocks/BuildingBlocks.csproj b/src/BuildingBlocks/BuildingBlocks.csproj
index 6d5cf38..e3c5308 100644
--- a/src/BuildingBlocks/BuildingBlocks.csproj
+++ b/src/BuildingBlocks/BuildingBlocks.csproj
@@ -7,114 +7,117 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
-
-
+
+
diff --git a/src/BuildingBlocks/CAP/Extensions.cs b/src/BuildingBlocks/CAP/Extensions.cs
index cf1c6fa..db8c03f 100644
--- a/src/BuildingBlocks/CAP/Extensions.cs
+++ b/src/BuildingBlocks/CAP/Extensions.cs
@@ -1,6 +1,7 @@
using System.Text.Encodings.Web;
using System.Text.Unicode;
using BuildingBlocks.Utils;
+using BuildingBlocks.Web;
using DotNetCore.CAP;
using DotNetCore.CAP.Messages;
using Microsoft.EntityFrameworkCore;
diff --git a/src/BuildingBlocks/Domain/Model/Entity.cs b/src/BuildingBlocks/Domain/Model/Auditable.cs
similarity index 100%
rename from src/BuildingBlocks/Domain/Model/Entity.cs
rename to src/BuildingBlocks/Domain/Model/Auditable.cs
diff --git a/src/BuildingBlocks/Domain/Model/IAuditable.cs b/src/BuildingBlocks/Domain/Model/IAuditable.cs
index 15dadf9..348941b 100644
--- a/src/BuildingBlocks/Domain/Model/IAuditable.cs
+++ b/src/BuildingBlocks/Domain/Model/IAuditable.cs
@@ -1,6 +1,6 @@
namespace BuildingBlocks.Domain.Model;
-public interface IAuditable
+public interface IAuditable : IEntity
{
public DateTime? CreatedAt { get; set; }
public long? CreatedBy { get; set; }
diff --git a/src/BuildingBlocks/Domain/Model/IEntity.cs b/src/BuildingBlocks/Domain/Model/IEntity.cs
index c7b88a5..7cc60fd 100644
--- a/src/BuildingBlocks/Domain/Model/IEntity.cs
+++ b/src/BuildingBlocks/Domain/Model/IEntity.cs
@@ -2,7 +2,6 @@ namespace BuildingBlocks.Domain.Model;
public interface IEntity
{
- long Id { get; }
}
public interface IEntity
diff --git a/src/BuildingBlocks/EFCore/AppDbContextBase.cs b/src/BuildingBlocks/EFCore/AppDbContextBase.cs
index b43409a..3b5788b 100644
--- a/src/BuildingBlocks/EFCore/AppDbContextBase.cs
+++ b/src/BuildingBlocks/EFCore/AppDbContextBase.cs
@@ -44,7 +44,7 @@ public abstract class AppDbContextBase : DbContext, IDbContext
await SaveChangesAsync(cancellationToken);
await _currentTransaction?.CommitAsync(cancellationToken)!;
}
- catch(System.Exception ex)
+ catch
{
await RollbackTransactionAsync(cancellationToken);
throw;
diff --git a/src/BuildingBlocks/EFCore/Extensions.cs b/src/BuildingBlocks/EFCore/Extensions.cs
index 31daaba..30ace5f 100644
--- a/src/BuildingBlocks/EFCore/Extensions.cs
+++ b/src/BuildingBlocks/EFCore/Extensions.cs
@@ -1,4 +1,3 @@
-using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -9,16 +8,15 @@ public static class Extensions
{
public static IServiceCollection AddCustomDbContext(
this IServiceCollection services,
- IConfiguration configuration,
- Assembly migrationAssembly)
+ IConfiguration configuration)
where TContext : AppDbContextBase
{
- services.AddScoped(provider => provider.GetService());
-
services.AddDbContext(options =>
options.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
- x => x.MigrationsAssembly(migrationAssembly.GetName().Name)));
+ x => x.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name)));
+
+ services.AddScoped(provider => provider.GetService());
return services;
}
diff --git a/src/BuildingBlocks/Jwt/JwtExtensions.cs b/src/BuildingBlocks/Jwt/JwtExtensions.cs
index f903cc7..e526541 100644
--- a/src/BuildingBlocks/Jwt/JwtExtensions.cs
+++ b/src/BuildingBlocks/Jwt/JwtExtensions.cs
@@ -1,4 +1,5 @@
using BuildingBlocks.Utils;
+using BuildingBlocks.Web;
using Duende.IdentityServer.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
diff --git a/src/BuildingBlocks/MassTransit/Extensions.cs b/src/BuildingBlocks/MassTransit/Extensions.cs
index 28c92a5..bce3f2e 100644
--- a/src/BuildingBlocks/MassTransit/Extensions.cs
+++ b/src/BuildingBlocks/MassTransit/Extensions.cs
@@ -1,6 +1,7 @@
using System.Reflection;
using BuildingBlocks.Domain.Event;
using BuildingBlocks.Utils;
+using BuildingBlocks.Web;
using Humanizer;
using MassTransit;
using Microsoft.AspNetCore.Hosting;
diff --git a/src/BuildingBlocks/Utils/EnumExtensions.cs b/src/BuildingBlocks/Utils/EnumExtensions.cs
deleted file mode 100644
index ec35658..0000000
--- a/src/BuildingBlocks/Utils/EnumExtensions.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using System.ComponentModel;
-
-namespace BuildingBlocks.Utils
-{
- //https://stackoverflow.com/a/19621488/581476
- public static class EnumExtensions
- {
- // This extension method is broken out so you can use a similar pattern with
- // other MetaData elements in the future. This is your base method for each.
- public static T GetAttribute(this Enum value) where T : Attribute {
- var type = value.GetType();
- var memberInfo = type.GetMember(value.ToString());
- var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
- return attributes.Length > 0
- ? (T)attributes[0]
- : null;
- }
-
- // This method creates a specific call to the above method, requesting the
- // Description MetaData attribute.
- public static string ToName(this Enum value) {
- var attribute = value.GetAttribute();
- return attribute == null ? value.ToString() : attribute.Description;
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/BuildingBlocks/Utils/ObjectExtensions.cs b/src/BuildingBlocks/Utils/ObjectExtensions.cs
deleted file mode 100644
index 4c26baf..0000000
--- a/src/BuildingBlocks/Utils/ObjectExtensions.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.Linq;
-using System.Web;
-
-namespace BuildingBlocks.Utils
-{
- public static class ObjectExtensions
- {
- public static string GetQueryString(this object obj)
- {
- var properties = from p in obj.GetType().GetProperties()
- where p.GetValue(obj, null) != null
- select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
-
- return String.Join("&", properties.ToArray());
- }
- }
-}
\ No newline at end of file
diff --git a/src/BuildingBlocks/Utils/ReflectionHelpers.cs b/src/BuildingBlocks/Utils/ReflectionHelpers.cs
deleted file mode 100644
index f4e8e16..0000000
--- a/src/BuildingBlocks/Utils/ReflectionHelpers.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-using System.Reflection;
-
-namespace BuildingBlocks.Utils
-{
- public static class ReflectionHelpers
- {
- private static readonly ConcurrentDictionary TypeCacheKeys = new();
- private static readonly ConcurrentDictionary PrettyPrintCache = new();
-
- public static string GetCacheKey(this Type type)
- {
- return TypeCacheKeys.GetOrAdd(type, t => $"{t.PrettyPrint()}");
- }
- public static string PrettyPrint(this Type type)
- {
- return PrettyPrintCache.GetOrAdd(
- type,
- t =>
- {
- try
- {
- return PrettyPrintRecursive(t, 0);
- }
- catch (System.Exception)
- {
- return t.Name;
- }
- });
- }
-
- public static bool IsActionDelegate(this Type sourceType)
- {
- if (sourceType.IsSubclassOf(typeof(MulticastDelegate)) &&
- sourceType.GetMethod("Invoke").ReturnType == typeof(void))
- return true;
- return false;
- }
- private static string PrettyPrintRecursive(Type type, int depth)
- {
- if (depth > 3)
- {
- return type.Name;
- }
-
- var nameParts = type.Name.Split('`');
- if (nameParts.Length == 1)
- {
- return nameParts[0];
- }
-
- var genericArguments = type.GetTypeInfo().GetGenericArguments();
- return !type.IsConstructedGenericType
- ? $"{nameParts[0]}<{new string(',', genericArguments.Length - 1)}>"
- : $"{nameParts[0]}<{string.Join(",", genericArguments.Select(t => PrettyPrintRecursive(t, depth + 1)))}>";
- }
- }
-}
\ No newline at end of file
diff --git a/src/BuildingBlocks/Web/SlugifyParameterTransformer.cs b/src/BuildingBlocks/Utils/SlugifyParameterTransformer.cs
similarity index 92%
rename from src/BuildingBlocks/Web/SlugifyParameterTransformer.cs
rename to src/BuildingBlocks/Utils/SlugifyParameterTransformer.cs
index 9aecf77..e0a7612 100644
--- a/src/BuildingBlocks/Web/SlugifyParameterTransformer.cs
+++ b/src/BuildingBlocks/Utils/SlugifyParameterTransformer.cs
@@ -1,7 +1,7 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Routing;
-namespace BuildingBlocks.Web;
+namespace BuildingBlocks.Utils;
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
diff --git a/src/BuildingBlocks/Web/ApiVersioningExtensions.cs b/src/BuildingBlocks/Web/ApiVersioningExtensions.cs
new file mode 100644
index 0000000..a893b44
--- /dev/null
+++ b/src/BuildingBlocks/Web/ApiVersioningExtensions.cs
@@ -0,0 +1,34 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Versioning;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BuildingBlocks.Web;
+
+public static class ApiVersioningExtensions
+{
+ public static void AddCustomVersioning(this IServiceCollection services,
+ Action configurator = null)
+ {
+ //https://www.meziantou.net/versioning-an-asp-net-core-api.htm
+ //https://exceptionnotfound.net/overview-of-api-versioning-in-asp-net-core-3-0/
+ services.AddApiVersioning(options =>
+ {
+ // Add the headers "api-supported-versions" and "api-deprecated-versions"
+ // This is better for discoverability
+ options.ReportApiVersions = true;
+
+ // AssumeDefaultVersionWhenUnspecified should only be enabled when supporting legacy services that did not previously
+ // support API versioning. Forcing existing clients to specify an explicit API version for an
+ // existing service introduces a breaking change. Conceptually, clients in this situation are
+ // bound to some API version of a service, but they don't know what it is and never explicit request it.
+ options.AssumeDefaultVersionWhenUnspecified = true;
+ options.DefaultApiVersion = new ApiVersion(1, 0);
+
+ // // Defines how an API version is read from the current HTTP request
+ options.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("api-version"),
+ new UrlSegmentApiVersionReader());
+
+ configurator?.Invoke(options);
+ });
+ }
+}
diff --git a/src/BuildingBlocks/Utils/ConfigurationExtensions.cs b/src/BuildingBlocks/Web/ConfigurationExtensions.cs
similarity index 96%
rename from src/BuildingBlocks/Utils/ConfigurationExtensions.cs
rename to src/BuildingBlocks/Web/ConfigurationExtensions.cs
index ace0c7b..76c437e 100644
--- a/src/BuildingBlocks/Utils/ConfigurationExtensions.cs
+++ b/src/BuildingBlocks/Web/ConfigurationExtensions.cs
@@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-namespace BuildingBlocks.Utils;
+namespace BuildingBlocks.Web;
public static class ConfigurationExtensions
{
diff --git a/src/BuildingBlocks/Utils/ConfigurationHelper.cs b/src/BuildingBlocks/Web/ConfigurationHelper.cs
similarity index 95%
rename from src/BuildingBlocks/Utils/ConfigurationHelper.cs
rename to src/BuildingBlocks/Web/ConfigurationHelper.cs
index 908857c..87480fc 100644
--- a/src/BuildingBlocks/Utils/ConfigurationHelper.cs
+++ b/src/BuildingBlocks/Web/ConfigurationHelper.cs
@@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration;
-namespace BuildingBlocks.Utils
+namespace BuildingBlocks.Web
{
public static class ConfigurationHelper
{
@@ -17,4 +17,4 @@ namespace BuildingBlocks.Utils
.Build();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/BuildingBlocks/Web/CorrelationExtensions.cs b/src/BuildingBlocks/Web/CorrelationExtensions.cs
new file mode 100644
index 0000000..64c5347
--- /dev/null
+++ b/src/BuildingBlocks/Web/CorrelationExtensions.cs
@@ -0,0 +1,26 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace BuildingBlocks.Web;
+
+public static class CorrelationExtensions
+{
+ private const string CorrelationId = "correlationId";
+
+ public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app)
+ {
+ return app.Use(async (ctx, next) =>
+ {
+ if (!ctx.Request.Headers.TryGetValue(CorrelationId, out var correlationId))
+ correlationId = Guid.NewGuid().ToString("N");
+
+ ctx.Items[CorrelationId] = correlationId.ToString();
+ await next();
+ });
+ }
+
+ public static string GetCorrelationId(this HttpContext context)
+ {
+ return context.Items.TryGetValue(CorrelationId, out var correlationId) ? correlationId as string : null;
+ }
+}
diff --git a/src/BuildingBlocks/Web/Extensions.cs b/src/BuildingBlocks/Web/Extensions.cs
deleted file mode 100644
index 88fc73d..0000000
--- a/src/BuildingBlocks/Web/Extensions.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Linq;
-using System.Net;
-using System.Net.Http.Headers;
-using JetBrains.Annotations;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Versioning;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace BuildingBlocks.Web
-{
- public static class Extensions
- {
- private const string CorrelationId = "correlationId";
-
- public static void AddCustomVersioning(this IServiceCollection services,
- Action configurator = null)
- {
- //https://www.meziantou.net/versioning-an-asp-net-core-api.htm
- //https://exceptionnotfound.net/overview-of-api-versioning-in-asp-net-core-3-0/
- services.AddApiVersioning(options =>
- {
- // Add the headers "api-supported-versions" and "api-deprecated-versions"
- // This is better for discoverability
- options.ReportApiVersions = true;
-
- // AssumeDefaultVersionWhenUnspecified should only be enabled when supporting legacy services that did not previously
- // support API versioning. Forcing existing clients to specify an explicit API version for an
- // existing service introduces a breaking change. Conceptually, clients in this situation are
- // bound to some API version of a service, but they don't know what it is and never explicit request it.
- options.AssumeDefaultVersionWhenUnspecified = true;
- options.DefaultApiVersion = new ApiVersion(1, 0);
-
- // // Defines how an API version is read from the current HTTP request
- options.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("api-version"),
- new UrlSegmentApiVersionReader());
-
- configurator?.Invoke(options);
- });
- }
-
- public static IApplicationBuilder UseCorrelationId(this IApplicationBuilder app)
- => app.Use(async (ctx, next) =>
- {
- if (!ctx.Request.Headers.TryGetValue(CorrelationId, out var correlationId))
- {
- correlationId = Guid.NewGuid().ToString("N");
- }
-
- ctx.Items[CorrelationId] = correlationId.ToString();
- await next();
- });
-
- public static string GetCorrelationId(this HttpContext context)
- {
- return context.Items.TryGetValue(CorrelationId, out var correlationId) ? correlationId as string : null;
- }
- }
-}
diff --git a/src/BuildingBlocks/Web/ServiceCollectionExtensions.cs b/src/BuildingBlocks/Web/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..74f723d
--- /dev/null
+++ b/src/BuildingBlocks/Web/ServiceCollectionExtensions.cs
@@ -0,0 +1,60 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BuildingBlocks.Web;
+
+public static class ServiceCollectionExtensions
+{
+ public static void ReplaceScoped(this IServiceCollection services)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ services.Unregister();
+ services.AddScoped();
+ }
+
+ public static void ReplaceScoped(this IServiceCollection services,
+ Func implementationFactory)
+ where TService : class
+ {
+ services.Unregister();
+ services.AddScoped(implementationFactory);
+ }
+
+ public static void ReplaceTransient(this IServiceCollection services)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ services.Unregister();
+ services.AddTransient();
+ }
+
+ public static void ReplaceTransient(this IServiceCollection services,
+ Func implementationFactory)
+ where TService : class
+ {
+ services.Unregister();
+ services.AddTransient(implementationFactory);
+ }
+
+ public static void ReplaceSingleton(this IServiceCollection services)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ services.Unregister();
+ services.AddSingleton();
+ }
+
+ public static void ReplaceSingleton(this IServiceCollection services,
+ Func implementationFactory)
+ where TService : class
+ {
+ services.Unregister();
+ services.AddSingleton(implementationFactory);
+ }
+
+ public static void Unregister(this IServiceCollection services)
+ {
+ var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(TService));
+ services.Remove(descriptor);
+ }
+}
diff --git a/src/Services/Booking/src/Booking.Api/Program.cs b/src/Services/Booking/src/Booking.Api/Program.cs
index a7b301d..2c8555e 100644
--- a/src/Services/Booking/src/Booking.Api/Program.cs
+++ b/src/Services/Booking/src/Booking.Api/Program.cs
@@ -31,7 +31,7 @@ builder.Services.Configure(options => configuration.GetSection("Grp
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
builder.Services.AddTransient();
-builder.Services.AddCustomDbContext(configuration, typeof(BookingRoot).Assembly);
+builder.Services.AddCustomDbContext(configuration);
builder.AddCustomSerilog();
builder.Services.AddJwt();
diff --git a/src/Services/Flight/src/Flight.Api/Program.cs b/src/Services/Flight/src/Flight.Api/Program.cs
index 7379335..0891ca6 100644
--- a/src/Services/Flight/src/Flight.Api/Program.cs
+++ b/src/Services/Flight/src/Flight.Api/Program.cs
@@ -8,10 +8,8 @@ using BuildingBlocks.Jwt;
using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
-using BuildingBlocks.Mongo;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.Swagger;
-using BuildingBlocks.Utils;
using BuildingBlocks.Web;
using Figgle;
using Flight;
@@ -32,12 +30,10 @@ var env = builder.Environment;
var appOptions = builder.Services.GetOptions("AppOptions");
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
-
builder.Services.AddTransient();
-builder.Services.AddCustomDbContext(configuration, typeof(FlightRoot).Assembly);
-builder.Services.AddMongoDbContext(configuration);
-
+builder.Services.AddCustomDbContext(configuration);
builder.Services.AddScoped();
+
builder.AddCustomSerilog();
builder.Services.AddJwt();
builder.Services.AddControllers();
diff --git a/src/Services/Flight/src/Flight.Api/appsettings.test.json b/src/Services/Flight/src/Flight.Api/appsettings.test.json
index 4b190d6..09c3487 100644
--- a/src/Services/Flight/src/Flight.Api/appsettings.test.json
+++ b/src/Services/Flight/src/Flight.Api/appsettings.test.json
@@ -1,11 +1,19 @@
{
"ConnectionStrings": {
- "DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB;Trusted_Connection=True;MultipleActiveResultSets=true"
+ "DefaultConnection": "Server=.\\sqlexpress;Database=FlightDB_Test;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"RabbitMq": {
- "HostName": "rabbitmq",
+ "HostName": "localhost",
"ExchangeName": "flight",
"UserName": "guest",
"Password": "guest"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft": "Debug",
+ "Microsoft.Hosting.Lifetime": "Debug",
+ "Microsoft.EntityFrameworkCore.Database.Command": "Debug"
+ }
}
}
diff --git a/src/Services/Flight/src/Flight/Data/FlightDbContext.cs b/src/Services/Flight/src/Flight/Data/FlightDbContext.cs
index dae7c09..8793eba 100644
--- a/src/Services/Flight/src/Flight/Data/FlightDbContext.cs
+++ b/src/Services/Flight/src/Flight/Data/FlightDbContext.cs
@@ -6,23 +6,23 @@ using Flight.Seats.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
-namespace Flight.Data
+namespace Flight.Data;
+
+public sealed class FlightDbContext : AppDbContextBase
{
- public sealed class FlightDbContext : AppDbContextBase
+ public FlightDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(
+ options, httpContextAccessor)
{
- public FlightDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options, httpContextAccessor)
- {
- }
+ }
- public DbSet Flights => Set();
- public DbSet Airports => Set();
- public DbSet Aircraft => Set();
- public DbSet Seats => Set();
+ public DbSet Flights => Set();
+ public DbSet Airports => Set();
+ public DbSet Aircraft => Set();
+ public DbSet Seats => Set();
- protected override void OnModelCreating(ModelBuilder builder)
- {
- builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
- base.OnModelCreating(builder);
- }
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
+ base.OnModelCreating(builder);
}
}
diff --git a/src/Services/Flight/src/Flight/Data/Migrations/20220509213249_Init.Designer.cs b/src/Services/Flight/src/Flight/Data/Migrations/20220511215248_Init.Designer.cs
similarity index 99%
rename from src/Services/Flight/src/Flight/Data/Migrations/20220509213249_Init.Designer.cs
rename to src/Services/Flight/src/Flight/Data/Migrations/20220511215248_Init.Designer.cs
index cd10e02..27ccb3a 100644
--- a/src/Services/Flight/src/Flight/Data/Migrations/20220509213249_Init.Designer.cs
+++ b/src/Services/Flight/src/Flight/Data/Migrations/20220511215248_Init.Designer.cs
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Flight.Data.Migrations
{
[DbContext(typeof(FlightDbContext))]
- [Migration("20220509213249_Init")]
+ [Migration("20220511215248_Init")]
partial class Init
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
diff --git a/src/Services/Flight/src/Flight/Data/Migrations/20220509213249_Init.cs b/src/Services/Flight/src/Flight/Data/Migrations/20220511215248_Init.cs
similarity index 100%
rename from src/Services/Flight/src/Flight/Data/Migrations/20220509213249_Init.cs
rename to src/Services/Flight/src/Flight/Data/Migrations/20220511215248_Init.cs
diff --git a/src/Services/Flight/tests/DeleteTests.cs b/src/Services/Flight/tests/DeleteTests.cs
index 749f9ca..6ee4486 100644
--- a/src/Services/Flight/tests/DeleteTests.cs
+++ b/src/Services/Flight/tests/DeleteTests.cs
@@ -1,4 +1,8 @@
using System.Threading.Tasks;
+using BuildingBlocks.Contracts.EventBus.Messages;
+using Flight.Airports.Models;
+using Flight.Flights.Features.GetFlightById;
+using MassTransit.Testing;
using Xunit;
namespace Integration.Test;
@@ -8,12 +12,16 @@ public class DeleteTests
{
private readonly TestFixture _fixture;
- public DeleteTests(TestFixture fixture) => _fixture = fixture;
-
- [Fact]
- public Task Should_delete_flight()
+ public DeleteTests(TestFixture fixture)
{
- return Task.CompletedTask;
+ _fixture = fixture;
}
+ [Fact]
+ public async Task Should_get_flight()
+ {
+ var query = new GetFlightByIdQuery(1);
+ var flight = await _fixture.SendAsync(query);
+ var airport = await _fixture.FindAsync(1);
+ }
}
diff --git a/src/Services/Flight/tests/TestFixture.cs b/src/Services/Flight/tests/TestFixture.cs
index 4a5ac72..7de9a4f 100644
--- a/src/Services/Flight/tests/TestFixture.cs
+++ b/src/Services/Flight/tests/TestFixture.cs
@@ -1,21 +1,24 @@
using System;
-using System.Linq;
+using System.Net.Http;
using System.Threading.Tasks;
using BuildingBlocks.Domain.Model;
+using BuildingBlocks.EFCore;
+using BuildingBlocks.MassTransit;
+using BuildingBlocks.Web;
using Flight.Data;
+using Flight.Data.Seed;
using MassTransit;
using MassTransit.Testing;
using MediatR;
-using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.AspNetCore.TestHost;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using Moq;
+using NSubstitute;
using Respawn;
using Xunit;
@@ -26,114 +29,84 @@ public class TestFixtureCollection : ICollectionFixture
{
}
+// ref: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0
// ref: https://github.com/jbogard/ContosoUniversityDotNetCore-Pages/blob/master/ContosoUniversity.IntegrationTests/SliceFixture.cs
-// ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs
-// ref: https://wrapt.dev/blog/building-an-event-driven-dotnet-application-integration-testing
+// ref: https://github.com/jasontaylordev/CleanArchitecture/blob/main/tests/Application.IntegrationTests/Testing.cs
public class TestFixture : IAsyncLifetime
{
- private readonly Checkpoint _checkpoint;
- private readonly IConfiguration _configuration;
- private readonly WebApplicationFactory _factory;
- private readonly IServiceScopeFactory _scopeFactory;
- private static InMemoryTestHarness _harness;
+ private Checkpoint _checkpoint;
+ private HttpClient _client;
+ private IConfiguration _configuration;
+ private WebApplicationFactory _factory;
+ private ITestHarness _harness;
+ private IServiceScopeFactory _scopeFactory;
- public TestFixture()
+ public ILogger Logger =>
+ _scopeFactory.CreateScope().ServiceProvider.GetRequiredService>();
+
+
+ public async Task InitializeAsync()
{
- _factory = FlightTestApplicationFactory();
+ Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test");
+
+ _factory = new WebApplicationFactory()
+ .WithWebHostBuilder(builder => builder.ConfigureServices(services =>
+ {
+ services.RemoveAll(typeof(IHostedService));
+ services.ReplaceSingleton(AddHttpContextAccessorMock);
+ services.ReplaceScoped();
+ services.AddMassTransitTestHarness(x =>
+ {
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ var rabbitMqOptions = services.GetOptions("RabbitMq");
+ var host = rabbitMqOptions.HostName;
+
+ cfg.Host(host, h =>
+ {
+ h.Username(rabbitMqOptions.UserName);
+ h.Password(rabbitMqOptions.Password);
+ });
+ cfg.ConfigureEndpoints(context);
+ });
+ });
+ }));
+
+ _harness = _factory.Services.GetTestHarness();
+
+ await _harness.Start();
_configuration = _factory.Services.GetRequiredService();
_scopeFactory = _factory.Services.GetRequiredService();
- _checkpoint = new Checkpoint();
- }
+ _client = _factory.CreateClient(new WebApplicationFactoryClientOptions {AllowAutoRedirect = false});
- public WebApplicationFactory FlightTestApplicationFactory()
- {
- Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test");
+ _checkpoint = new Checkpoint {TablesToIgnore = new[] {"__EFMigrationsHistory"}};
- return new WebApplicationFactory()
- .WithWebHostBuilder(builder =>
- {
- builder.ConfigureTestServices((services) =>
- {
- services.RemoveAll(typeof(IHostedService));
- });
-
- builder.ConfigureServices(services =>
- {
- builder.ConfigureLogging(logging =>
- {
- logging.ClearProviders(); // Remove other loggers
- });
-
- var httpContextAccessorService = services.FirstOrDefault(d =>
- d.ServiceType == typeof(IHttpContextAccessor));
-
- services.Remove(httpContextAccessorService);
- services.AddSingleton(_ => Mock.Of());
-
- services.AddScoped();
- var provider = services.BuildServiceProvider();
- var serviceScopeFactory = provider.GetService();
-
- // MassTransit Start Setup -- Do Not Delete Comment
- _harness = serviceScopeFactory?.CreateScope().ServiceProvider.GetRequiredService();
- _harness?.Start().GetAwaiter().GetResult();
- });
- });
- }
-
- public Task InitializeAsync()
- {
- return _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection"));
+ await EnsureDatabaseAsync();
}
public async Task DisposeAsync()
{
- await _harness.Stop();
+ _harness.Cancel();
await _factory.DisposeAsync();
+ await _checkpoint.Reset(_configuration.GetConnectionString("DefaultConnection"));
}
+
public async Task ExecuteScopeAsync(Func action)
{
using var scope = _scopeFactory.CreateScope();
- var dbContext = scope.ServiceProvider.GetRequiredService();
-
- try
- {
- await dbContext.BeginTransactionAsync();
-
- await action(scope.ServiceProvider);
-
- await dbContext.CommitTransactionAsync();
- }
- catch (Exception)
- {
- await dbContext.RollbackTransactionAsync();
- throw;
- }
+ await action(scope.ServiceProvider);
}
public async Task ExecuteScopeAsync(Func> action)
{
using var scope = _scopeFactory.CreateScope();
- var dbContext = scope.ServiceProvider.GetRequiredService();
- try
- {
- await dbContext.BeginTransactionAsync();
+ var result = await action(scope.ServiceProvider);
- var result = await action(scope.ServiceProvider);
-
- await dbContext.CommitTransactionAsync();
-
- return result;
- }
- catch (Exception)
- {
- await dbContext.RollbackTransactionAsync();
- throw;
- }
+ return result;
}
public Task ExecuteDbContextAsync(Func action)
@@ -171,6 +144,7 @@ public class TestFixture : IAsyncLifetime
return ExecuteDbContextAsync(db =>
{
foreach (var entity in entities) db.Set().Add(entity);
+
return db.SaveChangesAsync();
});
}
@@ -231,7 +205,7 @@ public class TestFixture : IAsyncLifetime
});
}
- public Task FindAsync(int id)
+ public Task FindAsync(long id)
where T : class, IEntity
{
return ExecuteDbContextAsync(db => db.Set().FindAsync(id).AsTask());
@@ -258,13 +232,15 @@ public class TestFixture : IAsyncLifetime
}
- // MassTransit Methods -- Do Not Delete Comment
+ // ref: https://github.com/MassTransit/MassTransit/blob/00d6992286911a437b63b93c89a56e920b053c11/src/MassTransit.TestFramework/InMemoryTestFixture.cs
+ // ref: https://wrapt.dev/blog/building-an-event-driven-dotnet-application-integration-testing
+
///
/// Publishes a message to the bus, and waits for the specified response.
///
/// The message that should be published.
/// The message that should be published.
- public static async Task PublishMessage(object message)
+ public async Task PublishMessage(object message)
where TMessage : class
{
await _harness.Bus.Publish(message);
@@ -317,4 +293,34 @@ public class TestFixture : IAsyncLifetime
var consumerHarness = scope.ServiceProvider.GetRequiredService>();
return consumerHarness.Consumed.Any();
}
+
+ private async Task EnsureDatabaseAsync()
+ {
+ using var scope = _scopeFactory.CreateScope();
+
+ var context = scope.ServiceProvider.GetRequiredService();
+ var seeders = scope.ServiceProvider.GetServices();
+
+ await context.Database.MigrateAsync();
+
+ foreach (var seeder in seeders) await seeder.SeedAllAsync();
+ }
+
+ // private async Task AddInMemoryHarnessAsync()
+ // {
+ // _harness = _factory.Services.GetRequiredService();
+ // await _harness.Start();
+ // }
+
+ private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider)
+ {
+ var httpContextAccessorMock = Substitute.For();
+ using var scope = serviceProvider.CreateScope();
+ httpContextAccessorMock.HttpContext = new DefaultHttpContext {RequestServices = scope.ServiceProvider};
+
+ httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 5000);
+ httpContextAccessorMock.HttpContext.Request.Scheme = "http";
+
+ return httpContextAccessorMock;
+ }
}
diff --git a/src/Services/Passenger/src/Passenger.Api/Program.cs b/src/Services/Passenger/src/Passenger.Api/Program.cs
index dd4afc3..9bc1bc5 100644
--- a/src/Services/Passenger/src/Passenger.Api/Program.cs
+++ b/src/Services/Passenger/src/Passenger.Api/Program.cs
@@ -28,7 +28,7 @@ var appOptions = builder.Services.GetOptions("AppOptions");
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
builder.Services.AddTransient();
-builder.Services.AddCustomDbContext(configuration, typeof(PassengerRoot).Assembly);
+builder.Services.AddCustomDbContext(configuration);
builder.AddCustomSerilog();
builder.Services.AddJwt();
builder.Services.AddControllers();
@@ -41,7 +41,6 @@ builder.Services.AddCustomMapster(typeof(PassengerRoot).Assembly);
builder.Services.AddHttpContextAccessor();
builder.Services.AddTransient();
-builder.Services.AddTransient();
builder.Services.AddCustomMassTransit(typeof(PassengerRoot).Assembly, env);
builder.Services.AddCustomOpenTelemetry();