diff --git a/.editorconfig b/.editorconfig
index 6a3f617b..873d72fa 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -248,6 +248,8 @@ resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning
+# https://www.jetbrains.com/help/rider/ClassNeverInstantiated.Global.html
+resharper_class_never_instantiated_global_highlighting = none
##################################################################################
## https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation
@@ -375,6 +377,8 @@ dotnet_diagnostic.sa1101.severity = None
# The keywords within the declaration of an element do not follow a standard ordering scheme.
dotnet_diagnostic.SA1206.severity = None
+dotnet_diagnostic.SA1404.severity = None
+
##################################################################################
## https://github.com/meziantou/Meziantou.Analyzer/tree/main/docs
## Meziantou.Analyzer
diff --git a/.husky/pre-commit b/.husky/pre-commit
index c94e823b..77b60d99 100644
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -3,3 +3,4 @@
# dotnet format --verbosity diagnostic
dotnet csharpier . && git add -A .
+dotnet csharpier --check .
\ No newline at end of file
diff --git a/src/ApiGateway/FoodDelivery.ApiGateway/FoodDelivery.ApiGateway.csproj b/src/ApiGateway/FoodDelivery.ApiGateway/FoodDelivery.ApiGateway.csproj
index 5e936e21..c450b44e 100644
--- a/src/ApiGateway/FoodDelivery.ApiGateway/FoodDelivery.ApiGateway.csproj
+++ b/src/ApiGateway/FoodDelivery.ApiGateway/FoodDelivery.ApiGateway.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/src/ApiGateway/FoodDelivery.ApiGateway/Program.cs b/src/ApiGateway/FoodDelivery.ApiGateway/Program.cs
index a7f811ae..65174962 100644
--- a/src/ApiGateway/FoodDelivery.ApiGateway/Program.cs
+++ b/src/ApiGateway/FoodDelivery.ApiGateway/Program.cs
@@ -1,21 +1,16 @@
using System.IdentityModel.Tokens.Jwt;
-using BuildingBlocks.Core.Messaging;
using BuildingBlocks.Logging;
using MassTransit;
-using Microsoft.IdentityModel.Logging;
using Serilog;
using Serilog.Events;
-using Serilog.Sinks.SpectreConsole;
+using Serilog.Sinks.Spectre;
using Yarp.ReverseProxy.Transforms;
using MessageHeaders = BuildingBlocks.Core.Messaging.MessageHeaders;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
- .WriteTo.SpectreConsole(
- "{Timestamp:HH:mm:ss} [{Level:u4}] {Message:lj}{NewLine}{Exception}",
- LogEventLevel.Information
- )
+ .WriteTo.Spectre("{Timestamp:HH:mm:ss} [{Level:u4}] {Message:lj}{NewLine}{Exception}", LogEventLevel.Information)
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
diff --git a/src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/ITestDataSeeder.cs b/src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/ITestDataSeeder.cs
new file mode 100644
index 00000000..ee2bc7ac
--- /dev/null
+++ b/src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/ITestDataSeeder.cs
@@ -0,0 +1,7 @@
+namespace BuildingBlocks.Abstractions.Persistence;
+
+public interface ITestDataSeeder
+{
+ int Order { get; }
+ Task SeedAllAsync();
+}
diff --git a/src/BuildingBlocks/BuildingBlocks.Core/Extensions/QueryableExtensions.cs b/src/BuildingBlocks/BuildingBlocks.Core/Extensions/QueryableExtensions.cs
index 0cf76152..9a629bb2 100644
--- a/src/BuildingBlocks/BuildingBlocks.Core/Extensions/QueryableExtensions.cs
+++ b/src/BuildingBlocks/BuildingBlocks.Core/Extensions/QueryableExtensions.cs
@@ -3,6 +3,7 @@
using AutoMapper.QueryableExtensions;
using BuildingBlocks.Abstractions.Core.Paging;
using BuildingBlocks.Core.Paging;
+using Microsoft.EntityFrameworkCore;
using Sieve.Models;
using Sieve.Services;
@@ -29,10 +30,13 @@ CancellationToken cancellationToken
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
+#pragma warning disable AsyncFixer02
+ // The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.
var total = result.Count();
+#pragma warning restore AsyncFixer02
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false);
- var items = await result.ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
+ var items = await result.AsNoTracking().ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
return PageList.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
}
@@ -57,12 +61,18 @@ CancellationToken cancellationToken
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
+#pragma warning disable AsyncFixer02
+ // The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.
var total = result.Count();
+#pragma warning restore AsyncFixer02
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false); // Only applies pagination
var projectedQuery = result.ProjectTo(configurationProvider);
- var items = await projectedQuery.ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
+ var items = await projectedQuery
+ .AsNoTracking()
+ .ToAsyncEnumerable()
+ .ToListAsync(cancellationToken: cancellationToken);
return PageList.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
}
@@ -87,12 +97,18 @@ CancellationToken cancellationToken
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
+#pragma warning disable AsyncFixer02
+ // The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.
var total = result.Count();
+#pragma warning restore AsyncFixer02
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false); // Only applies pagination
var projectedQuery = projectionFunc(result);
- var items = await projectedQuery.ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
+ var items = await projectedQuery
+ .AsNoTracking()
+ .ToAsyncEnumerable()
+ .ToListAsync(cancellationToken: cancellationToken);
return PageList.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
}
@@ -120,12 +136,7 @@ public static async Task> ApplyPagingAsync(
- pageRequest,
- sieveProcessor,
- projectionFunc,
- cancellationToken
- );
+ return await query.ApplyPagingAsync(pageRequest, sieveProcessor, projectionFunc, cancellationToken);
}
public static async Task> ApplyPagingAsync(
@@ -148,11 +159,15 @@ CancellationToken cancellationToken
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
+#pragma warning disable AsyncFixer02
+ // The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.
var total = result.Count();
+#pragma warning restore AsyncFixer02
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false); // Only applies pagination
var items = await result
.Select(x => map(x))
+ .AsNoTracking()
.ToAsyncEnumerable()
.ToListAsync(cancellationToken: cancellationToken);
diff --git a/src/BuildingBlocks/BuildingBlocks.Core/Messaging/MessagingConstants.cs b/src/BuildingBlocks/BuildingBlocks.Core/Messaging/MessagingConstants.cs
index 13360a9a..2f757094 100644
--- a/src/BuildingBlocks/BuildingBlocks.Core/Messaging/MessagingConstants.cs
+++ b/src/BuildingBlocks/BuildingBlocks.Core/Messaging/MessagingConstants.cs
@@ -2,5 +2,5 @@ namespace BuildingBlocks.Core.Messaging;
public static class MessagingConstants
{
- public const string PrimaryExchangePostfix = "_primary_exchange";
+ public const string PrimaryExchangePostfix = ".primary_exchange";
}
diff --git a/src/BuildingBlocks/BuildingBlocks.Core/Persistence/Extensions/DependencyInjectionExtensions.cs b/src/BuildingBlocks/BuildingBlocks.Core/Persistence/Extensions/DependencyInjectionExtensions.cs
index f07ed726..cb1459db 100644
--- a/src/BuildingBlocks/BuildingBlocks.Core/Persistence/Extensions/DependencyInjectionExtensions.cs
+++ b/src/BuildingBlocks/BuildingBlocks.Core/Persistence/Extensions/DependencyInjectionExtensions.cs
@@ -3,7 +3,7 @@
namespace BuildingBlocks.Core.Persistence.Extensions;
-internal static class DependencyInjectionExtensions
+public static class DependencyInjectionExtensions
{
internal static IServiceCollection AddPersistenceCore(
this IServiceCollection services,
@@ -12,9 +12,24 @@ params Assembly[] assembliesToScan
{
services.ScanAndRegisterDbExecutors(assembliesToScan);
+ services.RegisterDataSeeders(assembliesToScan);
+
services.AddHostedService();
services.AddScoped();
return services;
}
+
+ public static void RegisterDataSeeders(this IServiceCollection services, Assembly[] assembliesToScan)
+ {
+ services.Scan(scan =>
+ scan.FromAssemblies(assembliesToScan)
+ .AddClasses(classes => classes.AssignableTo())
+ .AsImplementedInterfaces()
+ .WithScopedLifetime()
+ .AddClasses(classes => classes.AssignableTo())
+ .AsImplementedInterfaces()
+ .WithScopedLifetime()
+ );
+ }
}
diff --git a/src/BuildingBlocks/BuildingBlocks.Core/Persistence/SeedWorker.cs b/src/BuildingBlocks/BuildingBlocks.Core/Persistence/SeedWorker.cs
index b9e63191..6213479c 100644
--- a/src/BuildingBlocks/BuildingBlocks.Core/Persistence/SeedWorker.cs
+++ b/src/BuildingBlocks/BuildingBlocks.Core/Persistence/SeedWorker.cs
@@ -1,4 +1,5 @@
using BuildingBlocks.Abstractions.Persistence;
+using BuildingBlocks.Core.Web.Extensions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -15,15 +16,25 @@ IWebHostEnvironment webHostEnvironment
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- if (!webHostEnvironment.IsEnvironment("test"))
- {
- logger.LogInformation("Seed worker started");
+ logger.LogInformation("Seed worker started");
- // https://stackoverflow.com/questions/38238043/how-and-where-to-call-database-ensurecreated-and-database-migrate
- // https://www.michalbialecki.com/2020/07/20/adding-entity-framework-core-5-migrations-to-net-5-project/
- using var serviceScope = serviceScopeFactory.CreateScope();
- var seeders = serviceScope.ServiceProvider.GetServices();
+ using var serviceScope = serviceScopeFactory.CreateScope();
+ // https://stackoverflow.com/questions/38238043/how-and-where-to-call-database-ensurecreated-and-database-migrate
+ // https://www.michalbialecki.com/2020/07/20/adding-entity-framework-core-5-migrations-to-net-5-project/
+ var testSeeders = serviceScope.ServiceProvider.GetServices();
+ var seeders = serviceScope.ServiceProvider.GetServices();
+ if (webHostEnvironment.IsTest())
+ {
+ foreach (var testDataSeeder in testSeeders.OrderBy(x => x.Order))
+ {
+ logger.LogInformation("Seeding '{Seed}' started...", testDataSeeder.GetType().Name);
+ await testDataSeeder.SeedAllAsync();
+ logger.LogInformation("Seeding '{Seed}' ended...", testDataSeeder.GetType().Name);
+ }
+ }
+ else
+ {
foreach (var seeder in seeders.OrderBy(x => x.Order))
{
logger.LogInformation("Seeding '{Seed}' started...", seeder.GetType().Name);
@@ -35,10 +46,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
public override Task StopAsync(CancellationToken cancellationToken)
{
- if (!webHostEnvironment.IsEnvironment("test"))
- {
- logger.LogInformation("Seed worker stopped");
- }
+ logger.LogInformation("Seed worker stopped");
return base.StopAsync(cancellationToken);
}
diff --git a/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/CustomEntityNameFormatter.cs b/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/CustomEntityNameFormatter.cs
index cca72ce0..8f795783 100644
--- a/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/CustomEntityNameFormatter.cs
+++ b/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/CustomEntityNameFormatter.cs
@@ -27,3 +27,23 @@ public string FormatEntityName()
return $"{typeof(T).Name.Underscore()}{MessagingConstants.PrimaryExchangePostfix}";
}
}
+
+public class CustomEntityNameFormatter : IMessageEntityNameFormatter
+ where TMessage : class
+{
+ public string FormatEntityName()
+ {
+ // Check if T implements IEventEnvelope
+ if (typeof(IEventEnvelope).IsAssignableFrom(typeof(TMessage)))
+ {
+ var messageProperty = typeof(TMessage).GetProperty(nameof(IEventEnvelope.Message));
+ if (typeof(IMessage).IsAssignableFrom(messageProperty!.PropertyType))
+ {
+ return $"{messageProperty.PropertyType.Name.Underscore()}{MessagingConstants.PrimaryExchangePostfix}";
+ }
+ }
+
+ // Return a default value if T does not implement IEventEnvelop or Message property is not found
+ return $"{typeof(TMessage).Name.Underscore()}{MessagingConstants.PrimaryExchangePostfix}";
+ }
+}
diff --git a/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/DependencyInjectionExtensions.cs b/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/DependencyInjectionExtensions.cs
index f632170d..e3c49b61 100644
--- a/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/DependencyInjectionExtensions.cs
+++ b/src/BuildingBlocks/BuildingBlocks.Integration.MassTransit/DependencyInjectionExtensions.cs
@@ -12,6 +12,7 @@
using Humanizer;
using MassTransit;
using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using RabbitMQ.Client;
using IExternalEventBus = BuildingBlocks.Abstractions.Messaging.IExternalEventBus;
@@ -20,9 +21,18 @@ namespace BuildingBlocks.Integration.MassTransit;
public static class DependencyInjectionExtensions
{
+ ///
+ /// Add Masstransit.
+ ///
+ ///
+ /// All configurations related to message topology, publish configuration and recive endpoint configurations.
+ ///
+ ///
+ ///
+ ///
public static WebApplicationBuilder AddCustomMassTransit(
this WebApplicationBuilder builder,
- Action? configureReceiveEndpoints = null,
+ Action? configureMessagesTopologies = null,
Action? configureBusRegistration = null,
Action? configureMessagingOptions = null,
params Assembly[] scanAssemblies
@@ -50,12 +60,21 @@ params Assembly[] scanAssemblies
void ConfiguratorAction(IBusRegistrationConfigurator busRegistrationConfigurator)
{
+ // https://masstransit.io/documentation/configuration#health-check-options
+ busRegistrationConfigurator.ConfigureHealthCheckOptions(options =>
+ {
+ options.Name = "masstransit";
+ options.MinimalFailureStatus = HealthStatus.Unhealthy;
+ options.Tags.Add("health");
+ });
+
configureBusRegistration?.Invoke(busRegistrationConfigurator);
// https://masstransit-project.com/usage/configuration.html#receive-endpoints
busRegistrationConfigurator.AddConsumers(assemblies);
- // exclude namespace for the messages
+ // https://masstransit.io/documentation/configuration#endpoint-name-formatters
+ // uses by `ConfigureEndpoints` for naming `queues` and `receive endpoint` names
busRegistrationConfigurator.SetEndpointNameFormatter(new SnakeCaseEndpointNameFormatter(false));
// Ref: https://masstransit-project.com/advanced/topology/rabbitmq.html
@@ -72,11 +91,20 @@ void ConfiguratorAction(IBusRegistrationConfigurator busRegistrationConfigurator
{
cfg.UseConsumeFilter(typeof(CorrelationConsumeFilter<>), context);
- cfg.PublishTopology.BrokerTopologyOptions = PublishBrokerTopologyOptions.FlattenHierarchy;
+ // https: // github.com/MassTransit/MassTransit/issues/2018
+ // https://github.com/MassTransit/MassTransit/issues/4831
+ cfg.Publish(p => p.Exclude = true);
+ cfg.Publish(p => p.Exclude = true);
+ cfg.Publish(p => p.Exclude = true);
+ cfg.Publish(p => p.Exclude = true);
+ cfg.Publish(p => p.Exclude = true);
+ cfg.Publish(p => p.Exclude = true);
if (messagingOptions.AutoConfigEndpoints)
{
- // https://masstransit-project.com/usage/consumers.html#consumer
+ // https://masstransit.io/documentation/configuration#consumer-registration
+ // https://masstransit.io/documentation/configuration#configure-endpoints
+ // MassTransit is able to configure receive endpoints for all registered consumer types. Receive endpoint names are generated using an endpoint name formatter
cfg.ConfigureEndpoints(context);
}
@@ -95,32 +123,39 @@ void ConfiguratorAction(IBusRegistrationConfigurator busRegistrationConfigurator
);
// for setting exchange name for message type as default. masstransit by default uses fully message type name for primary exchange name.
+ // https://masstransit.io/documentation/configuration/topology/message
cfg.MessageTopology.SetEntityNameFormatter(new CustomEntityNameFormatter());
ApplyMessagesPublishTopology(cfg.PublishTopology, assemblies);
ApplyMessagesConsumeTopology(cfg, context, assemblies);
ApplyMessagesSendTopology(cfg.SendTopology, assemblies);
- configureReceiveEndpoints?.Invoke(context, cfg);
+ configureMessagesTopologies?.Invoke(context, cfg);
// https://masstransit-project.com/usage/exceptions.html#retry
// https://markgossa.com/2022/06/masstransit-exponential-back-off.html
cfg.UseMessageRetry(r => AddRetryConfiguration(r));
-
- // cfg.UseInMemoryOutbox();
-
- // https: // github.com/MassTransit/MassTransit/issues/2018
- // https://github.com/MassTransit/MassTransit/issues/4831
- cfg.Publish(p => p.Exclude = true);
- cfg.Publish(p => p.Exclude = true);
- cfg.Publish(p => p.Exclude = true);
- cfg.Publish(p => p.Exclude = true);
- cfg.Publish(p => p.Exclude = true);
- cfg.Publish(p => p.Exclude = true);
}
);
}
+ builder
+ .Services.AddOptions()
+ .Configure(options =>
+ {
+ options.WaitUntilStarted = true;
+ options.StartTimeout = TimeSpan.FromSeconds(30);
+ options.StopTimeout = TimeSpan.FromSeconds(60);
+ });
+
+ builder
+ .Services.AddOptions()
+ .Configure(options =>
+ {
+ options.StartupTimeout = TimeSpan.FromSeconds(60);
+ options.ShutdownTimeout = TimeSpan.FromSeconds(60);
+ });
+
builder.Services.AddTransient();
builder.Services.AddTransient();
@@ -150,6 +185,9 @@ Assembly[] assemblies
var eventEnvelopeInterfaceConfigurator = consumeTopology.GetMessageTopology(
eventEnvelopeInterfaceMessageType
);
+
+ // indicate whether the topic or exchange for the message type should be created and subscribed to the queue when consumed on a reception endpoint.
+ // // with setting `ConfigureConsumeTopology` to `false`, we should create `primary exchange` and its bounded exchange manually with using `re.Bind` otherwise with `ConfigureConsumeTopology=true` it get publish topology for message type `T` with `_publishTopology.GetMessageTopology()` and use its ExchangeType and ExchangeName based ofo default EntityFormatter
eventEnvelopeInterfaceConfigurator.ConfigureConsumeTopology = true;
// none event-envelope message types
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Brands/Configs.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Brands/Configs.cs
index 01ff5a29..e21235b2 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Brands/Configs.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Brands/Configs.cs
@@ -1,7 +1,5 @@
-using BuildingBlocks.Abstractions.Persistence;
using BuildingBlocks.Abstractions.Web.Module;
using FoodDelivery.Services.Catalogs.Brands.Contracts;
-using FoodDelivery.Services.Catalogs.Brands.Data;
using FoodDelivery.Services.Catalogs.Brands.Services;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -11,7 +9,6 @@ internal class Configs : IModuleConfiguration
{
public WebApplicationBuilder AddModuleServices(WebApplicationBuilder builder)
{
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
return builder;
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Categories/Configs.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Categories/Configs.cs
index 24e01613..4b7e2ec5 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Categories/Configs.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Categories/Configs.cs
@@ -1,7 +1,5 @@
-using BuildingBlocks.Abstractions.Persistence;
using BuildingBlocks.Abstractions.Web.Module;
using FoodDelivery.Services.Catalogs.Categories.Contracts;
-using FoodDelivery.Services.Catalogs.Categories.Data;
using FoodDelivery.Services.Catalogs.Categories.Services;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -11,7 +9,6 @@ internal class Configs : IModuleConfiguration
{
public WebApplicationBuilder AddModuleServices(WebApplicationBuilder builder)
{
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
return builder;
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/MassTransitExtensions.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/MassTransitExtensions.cs
index a76afc52..64ed9d0c 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/MassTransitExtensions.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/MassTransitExtensions.cs
@@ -1,37 +1,84 @@
+using BuildingBlocks.Abstractions.Events;
+using BuildingBlocks.Integration.MassTransit;
using FoodDelivery.Services.Shared.Catalogs.Products.Events.V1.Integration;
using Humanizer;
using MassTransit;
-using RabbitMQ.Client;
namespace FoodDelivery.Services.Catalogs.Products;
public static class MassTransitExtensions
{
- internal static void AddProductPublishers(this IRabbitMqBusFactoryConfigurator cfg)
+ internal static void ConfigureProductMessagesTopology(this IRabbitMqBusFactoryConfigurator cfg)
{
- cfg.Message(e => e.SetEntityName($"{nameof(ProductCreatedV1).Underscore()}.input_exchange")); // name of the primary exchange
- cfg.Publish(e => e.ExchangeType = ExchangeType.Direct); // primary exchange type
- cfg.Send(e =>
+ // https://masstransit.io/documentation/transports/rabbitmq
+ cfg.Message>(e =>
+ {
+ // https://masstransit.io/documentation/configuration/topology/message
+ // Name of the `primary exchange` for type based message publishing and sending
+ // e.SetEntityName($"{nameof(ProductCreatedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
+ });
+
+ // configuration for MessagePublishTopologyConfiguration and using IPublishEndpoint
+ cfg.Publish>(e =>
+ {
+ // we configured some shared settings for all publish message in masstransit publish topologies
+
+ // // setup primary exchange
+ // e.Durable = true;
+ // e.ExchangeType = ExchangeType.Direct;
+ });
+
+ cfg.Send>(e =>
{
// route by message type to binding fanout exchange (exchange to exchange binding)
e.UseRoutingKeyFormatter(context => context.Message.GetType().Name.Underscore());
});
- cfg.Message(e =>
- e.SetEntityName($"{nameof(ProductStockDebitedV1).Underscore()}.input_exchange")
- ); // name of the primary exchange
- cfg.Publish(e => e.ExchangeType = ExchangeType.Direct); // primary exchange type
- cfg.Send(e =>
+ // https://masstransit.io/documentation/transports/rabbitmq
+ cfg.Message>(e =>
+ {
+ // https://masstransit.io/documentation/configuration/topology/message
+ // Name of the `primary exchange` for type based message publishing and sending
+ // e.SetEntityName($"{nameof(ProductStockDebitedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
+ });
+
+ // configuration for MessagePublishTopologyConfiguration and using IPublishEndpoint
+ cfg.Publish>(e =>
+ {
+ // we configured some shared settings for all publish message in masstransit publish topologies
+
+ // // setup primary exchange
+ // e.Durable = true;
+ // e.ExchangeType = ExchangeType.Direct;
+ });
+
+ cfg.Send>(e =>
{
// route by message type to binding fanout exchange (exchange to exchange binding)
e.UseRoutingKeyFormatter(context => context.Message.GetType().Name.Underscore());
});
- cfg.Message(e =>
- e.SetEntityName($"{nameof(ProductStockReplenishedV1).Underscore()}.input_exchange")
- ); // name of the primary exchange
- cfg.Publish(e => e.ExchangeType = ExchangeType.Direct); // primary exchange type
- cfg.Send(e =>
+ // https://masstransit.io/documentation/transports/rabbitmq
+ cfg.Message>(e =>
+ {
+ // https://masstransit.io/documentation/configuration/topology/message
+ // Name of the `primary exchange` for type based message publishing and sending
+ // e.SetEntityName($"{nameof(ProductStockDebitedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
+ });
+
+ cfg.Publish>(e =>
+ {
+ // we configured some shared settings for all publish message in masstransit publish topologies
+
+ // // setup primary exchange
+ // e.Durable = true;
+ // e.ExchangeType = ExchangeType.Direct;
+ });
+
+ cfg.Send>(e =>
{
// route by message type to binding fanout exchange (exchange to exchange binding)
e.UseRoutingKeyFormatter(context => context.Message.GetType().Name.Underscore());
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/ProductsConfigs.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/ProductsConfigs.cs
index ff87822e..58e58b3f 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/ProductsConfigs.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/ProductsConfigs.cs
@@ -20,7 +20,6 @@ internal class ProductsConfigs : IModuleConfiguration
public WebApplicationBuilder AddModuleServices(WebApplicationBuilder builder)
{
- builder.Services.TryAddScoped();
builder.Services.TryAddSingleton();
return builder;
@@ -38,10 +37,10 @@ public IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoints)
var routeCategoryName = Tag;
var products = endpoints.NewVersionedApi(name: routeCategoryName).WithTags(Tag);
- // create a new sub group for each version
+ // create a new subgroup for each version
var productsV1 = products.MapGroup(ProductsPrefixUri).HasDeprecatedApiVersion(0.9).HasApiVersion(1.0);
- // create a new sub group for each version
+ // create a new subgroup for each version
var productsV2 = products.MapGroup(ProductsPrefixUri).HasApiVersion(2.0);
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-7.0#route-groups
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
index 29a1f165..bb4873d8 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
@@ -100,7 +100,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder
builder.AddCustomMassTransit(
(busRegistrationContext, busFactoryConfigurator) =>
{
- busFactoryConfigurator.AddProductPublishers();
+ busFactoryConfigurator.ConfigureProductMessagesTopology();
}
);
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs
index aaf55857..05af7fc2 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs
@@ -36,8 +36,6 @@ private static void AddPostgresWriteStorage(IServiceCollection services, IConfig
{
services.AddPostgresDbContext(configuration);
- // add migrations and seeders dependencies, or we could add seeders inner each modules
- services.TryAddScoped();
services.TryAddScoped();
}
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Configs.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Configs.cs
index 51185476..1b6eacf2 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Configs.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Configs.cs
@@ -1,7 +1,5 @@
-using BuildingBlocks.Abstractions.Persistence;
using BuildingBlocks.Abstractions.Web.Module;
using FoodDelivery.Services.Catalogs.Suppliers.Contracts;
-using FoodDelivery.Services.Catalogs.Suppliers.Data;
using FoodDelivery.Services.Catalogs.Suppliers.Services;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -11,7 +9,6 @@ internal class Configs : IModuleConfiguration
{
public WebApplicationBuilder AddModuleServices(WebApplicationBuilder builder)
{
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
return builder;
diff --git a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Services/SupplierChecker.cs b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Services/SupplierChecker.cs
index eb9944c1..10afeee7 100644
--- a/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Services/SupplierChecker.cs
+++ b/src/Services/Catalogs/FoodDelivery.Services.Catalogs/Suppliers/Services/SupplierChecker.cs
@@ -5,19 +5,12 @@
namespace FoodDelivery.Services.Catalogs.Suppliers.Services;
-public class SupplierChecker : ISupplierChecker
+public class SupplierChecker(ICatalogDbContext catalogDbContext) : ISupplierChecker
{
- private readonly ICatalogDbContext _catalogDbContext;
-
- public SupplierChecker(ICatalogDbContext catalogDbContext)
- {
- _catalogDbContext = catalogDbContext;
- }
-
public bool SupplierExists(SupplierId supplierId)
{
supplierId.NotBeNull();
- var category = _catalogDbContext.FindSupplierById(supplierId);
+ var category = catalogDbContext.FindSupplierById(supplierId);
return category is not null;
}
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/Customers/CustomersConfigs.cs b/src/Services/Customers/FoodDelivery.Services.Customers/Customers/CustomersConfigs.cs
index 0329706c..90d5f371 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/Customers/CustomersConfigs.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/Customers/CustomersConfigs.cs
@@ -1,9 +1,6 @@
using Asp.Versioning.Builder;
-using BuildingBlocks.Abstractions.Persistence;
using BuildingBlocks.Abstractions.Web.Module;
-using FoodDelivery.Services.Customers.Customers.Data;
using FoodDelivery.Services.Customers.Shared;
-using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FoodDelivery.Services.Customers.Customers;
@@ -15,8 +12,6 @@ internal class CustomersConfigs : IModuleConfiguration
public WebApplicationBuilder AddModuleServices(WebApplicationBuilder builder)
{
- builder.Services.TryAddScoped();
-
//// we could add event mappers manually, also they can find automatically by scanning assemblies
// builder.Services.TryAddSingleton();
return builder;
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/Customers/MassTransitExtensions.cs b/src/Services/Customers/FoodDelivery.Services.Customers/Customers/MassTransitExtensions.cs
index c75814c2..770ee030 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/Customers/MassTransitExtensions.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/Customers/MassTransitExtensions.cs
@@ -1,5 +1,6 @@
using BuildingBlocks.Abstractions.Events;
using BuildingBlocks.Core.Messaging;
+using BuildingBlocks.Integration.MassTransit;
using FoodDelivery.Services.Shared.Customers.Customers.Events.V1.Integration;
using Humanizer;
using MassTransit;
@@ -8,13 +9,15 @@ namespace FoodDelivery.Services.Customers.Customers;
internal static class MassTransitExtensions
{
- internal static void AddCustomerPublishers(this IRabbitMqBusFactoryConfigurator cfg)
+ internal static void ConfigureCustomerMessagesTopology(this IRabbitMqBusFactoryConfigurator cfg)
{
// https://masstransit.io/documentation/transports/rabbitmq
cfg.Message>(e =>
{
- // name of the `primary exchange` for type based message publishing and sending
- e.SetEntityName($"{nameof(CustomerCreatedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ // https://masstransit.io/documentation/configuration/topology/message
+ // Name of the `primary exchange` for type based message publishing and sending
+ // e.SetEntityName($"{nameof(CustomerCreatedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
});
// configuration for MessagePublishTopologyConfiguration and using IPublishEndpoint
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/Products/MassTransitExtensions.cs b/src/Services/Customers/FoodDelivery.Services.Customers/Products/MassTransitExtensions.cs
index 182e60e4..c53b9719 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/Products/MassTransitExtensions.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/Products/MassTransitExtensions.cs
@@ -10,7 +10,10 @@ namespace FoodDelivery.Services.Customers.Products;
internal static class MassTransitExtensions
{
- internal static void AddProductEndpoints(this IRabbitMqBusFactoryConfigurator cfg, IBusRegistrationContext context)
+ internal static void ConfigureProductMessagesTopology(
+ this IRabbitMqBusFactoryConfigurator cfg,
+ IBusRegistrationContext context
+ )
{
// we configured some shared settings for all receive endpoint message in masstransit consuming topologies
@@ -33,11 +36,12 @@ internal static void AddProductEndpoints(this IRabbitMqBusFactoryConfigurator cf
// re.SetQuorumQueue();
//
// // with setting `ConfigureConsumeTopology` to `false`, we should create `primary exchange` and its bounded exchange manually with using `re.Bind` otherwise with `ConfigureConsumeTopology=true` it get publish topology for message type `T` with `_publishTopology.GetMessageTopology()` and use its ExchangeType and ExchangeName based ofo default EntityFormatter
- // re.ConfigureConsumeTopology = false;
+ // // indicate whether the topic or exchange for the message type should be created and subscribed to the queue when consumed on a reception endpoint.
+ // re.ConfigureConsumeTopology = false;
//
// // https://spring.io/blog/2011/04/01/routing-topologies-for-performance-and-scalability-with-rabbitmq
// // masstransit uses `wire-tapping` pattern for defining exchanges. Primary exchange will send the message to intermediary fanout exchange
- // // setup primary exchange and its type
+ // // setup primary exchange and its type from message type and receive-endpoint formatter
// re.Bind>(e =>
// {
// e.RoutingKey = nameof(ProductStockReplenishedV1).Underscore();
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/RestockSubscriptions/MassTransitExtensions.cs b/src/Services/Customers/FoodDelivery.Services.Customers/RestockSubscriptions/MassTransitExtensions.cs
index bf5f9a69..f0b2a6db 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/RestockSubscriptions/MassTransitExtensions.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/RestockSubscriptions/MassTransitExtensions.cs
@@ -1,5 +1,6 @@
using BuildingBlocks.Abstractions.Events;
using BuildingBlocks.Core.Messaging;
+using BuildingBlocks.Integration.MassTransit;
using FoodDelivery.Services.Shared.Customers.RestockSubscriptions.Events.V1.Integration;
using Humanizer;
using MassTransit;
@@ -8,16 +9,18 @@ namespace FoodDelivery.Services.Customers.RestockSubscriptions;
internal static class MassTransitExtensions
{
- internal static void AddRestockSubscriptionPublishers(this IRabbitMqBusFactoryConfigurator cfg)
+ internal static void ConfigureRestockSubscriptionMessagesTopology(this IRabbitMqBusFactoryConfigurator cfg)
{
cfg.Message>(e =>
{
// we configured some shared settings for all publish message in masstransit publish topologies
// name of the primary exchange
- e.SetEntityName(
- $"{nameof(RestockSubscriptionCreatedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}"
- );
+ // https://masstransit.io/documentation/configuration/topology/message
+ // e.SetEntityName(
+ // $"{nameof(RestockSubscriptionCreatedV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}"
+ // );
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
});
cfg.Publish>(e =>
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Data/Extensions/WebApplicationBuilderExtensions.DependencyInjection.cs b/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Data/Extensions/WebApplicationBuilderExtensions.DependencyInjection.cs
index abe18ee2..ece57253 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Data/Extensions/WebApplicationBuilderExtensions.DependencyInjection.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Data/Extensions/WebApplicationBuilderExtensions.DependencyInjection.cs
@@ -35,9 +35,6 @@ private static void AddPostgresWriteStorage(IServiceCollection services, IConfig
else
{
services.AddPostgresDbContext(configuration);
-
- // add migrations and seeders dependencies, or we could add seeders inner each modules
- services.TryAddScoped();
services.TryAddScoped();
}
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Extensions/WebApplicationBuilderExtensions/WebApplicationBuilderExtensions.Infrastructure.cs b/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Extensions/WebApplicationBuilderExtensions/WebApplicationBuilderExtensions.Infrastructure.cs
index 17bc609d..68a33b63 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Extensions/WebApplicationBuilderExtensions/WebApplicationBuilderExtensions.Infrastructure.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/Shared/Extensions/WebApplicationBuilderExtensions/WebApplicationBuilderExtensions.Infrastructure.cs
@@ -136,13 +136,13 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder
builder.AddCustomRateLimit();
builder.AddCustomMassTransit(
- configureReceiveEndpoints: (context, cfg) =>
+ configureMessagesTopologies: (context, cfg) =>
{
- cfg.AddUsersEndpoints(context);
- cfg.AddProductEndpoints(context);
+ cfg.ConfigureUsersMessagesTopology(context);
+ cfg.ConfigureProductMessagesTopology(context);
- cfg.AddCustomerPublishers();
- cfg.AddRestockSubscriptionPublishers();
+ cfg.ConfigureCustomerMessagesTopology();
+ cfg.ConfigureRestockSubscriptionMessagesTopology();
},
configureMessagingOptions: msgCfg =>
{
diff --git a/src/Services/Customers/FoodDelivery.Services.Customers/Users/MassTransitExtensions.cs b/src/Services/Customers/FoodDelivery.Services.Customers/Users/MassTransitExtensions.cs
index 4f54b506..d0e7c6c3 100644
--- a/src/Services/Customers/FoodDelivery.Services.Customers/Users/MassTransitExtensions.cs
+++ b/src/Services/Customers/FoodDelivery.Services.Customers/Users/MassTransitExtensions.cs
@@ -9,7 +9,10 @@ namespace FoodDelivery.Services.Customers.Users;
internal static class MassTransitExtensions
{
- internal static void AddUsersEndpoints(this IRabbitMqBusFactoryConfigurator cfg, IBusRegistrationContext context)
+ internal static void ConfigureUsersMessagesTopology(
+ this IRabbitMqBusFactoryConfigurator cfg,
+ IBusRegistrationContext context
+ )
{
// we configured some shared settings for all publish message in masstransit publish topologies
@@ -32,11 +35,12 @@ internal static void AddUsersEndpoints(this IRabbitMqBusFactoryConfigurator cfg,
// re.SetQuorumQueue();
//
// // with setting `ConfigureConsumeTopology` to `false`, we should create `primary exchange` and its bounded exchange manually with using `re.Bind` otherwise with `ConfigureConsumeTopology=true` it get publish topology for message type `T` with `_publishTopology.GetMessageTopology()` and use its ExchangeType and ExchangeName based ofo default EntityFormatter
+ // // indicate whether the topic or exchange for the message type should be created and subscribed to the queue when consumed on a reception endpoint.
// re.ConfigureConsumeTopology = true;
//
// // // https://spring.io/blog/2011/04/01/routing-topologies-for-performance-and-scalability-with-rabbitmq
// // // masstransit uses `wire-tapping` pattern for defining exchanges. Primary exchange will send the message to intermediary fanout exchange
- // // // setup primary exchange and its type
+ // // // setup primary exchange and its type from message type and receive-endpoint formatter
// // re.Bind>(e =>
// // {
// // e.RoutingKey = nameof(UserRegisteredV1).Underscore();
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Identity/Data/IdentityDataSeeder.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Identity/Data/IdentityDataSeeder.cs
index a3aadb42..fb8c3c14 100644
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Identity/Data/IdentityDataSeeder.cs
+++ b/src/Services/Identity/FoodDelivery.Services.Identity/Identity/Data/IdentityDataSeeder.cs
@@ -4,17 +4,9 @@
namespace FoodDelivery.Services.Identity.Identity.Data;
-public class IdentityDataSeeder : IDataSeeder
+public class IdentityDataSeeder(UserManager userManager, RoleManager roleManager)
+ : IDataSeeder
{
- private readonly RoleManager _roleManager;
- private readonly UserManager _userManager;
-
- public IdentityDataSeeder(UserManager userManager, RoleManager roleManager)
- {
- _userManager = userManager;
- _roleManager = roleManager;
- }
-
public async Task SeedAllAsync()
{
await SeedRoles();
@@ -25,16 +17,16 @@ public async Task SeedAllAsync()
private async Task SeedRoles()
{
- if (!await _roleManager.RoleExistsAsync(ApplicationRole.Admin.Name))
- await _roleManager.CreateAsync(ApplicationRole.Admin);
+ if (!await roleManager.RoleExistsAsync(ApplicationRole.Admin.Name))
+ await roleManager.CreateAsync(ApplicationRole.Admin);
- if (!await _roleManager.RoleExistsAsync(ApplicationRole.User.Name))
- await _roleManager.CreateAsync(ApplicationRole.User);
+ if (!await roleManager.RoleExistsAsync(ApplicationRole.User.Name))
+ await roleManager.CreateAsync(ApplicationRole.User);
}
private async Task SeedUsers()
{
- if (await _userManager.FindByEmailAsync("mehdi@test.com") == null)
+ if (await userManager.FindByEmailAsync("mehdi@test.com") == null)
{
var user = new ApplicationUser
{
@@ -44,13 +36,13 @@ private async Task SeedUsers()
Email = "mehdi@test.com",
};
- var result = await _userManager.CreateAsync(user, "123456");
+ var result = await userManager.CreateAsync(user, "123456");
if (result.Succeeded)
- await _userManager.AddToRoleAsync(user, ApplicationRole.Admin.Name);
+ await userManager.AddToRoleAsync(user, ApplicationRole.Admin.Name);
}
- if (await _userManager.FindByEmailAsync("mehdi2@test.com") == null)
+ if (await userManager.FindByEmailAsync("mehdi2@test.com") == null)
{
var user = new ApplicationUser
{
@@ -60,10 +52,10 @@ private async Task SeedUsers()
Email = "mehdi2@test.com"
};
- var result = await _userManager.CreateAsync(user, "123456");
+ var result = await userManager.CreateAsync(user, "123456");
if (result.Succeeded)
- await _userManager.AddToRoleAsync(user, ApplicationRole.User.Name);
+ await userManager.AddToRoleAsync(user, ApplicationRole.User.Name);
}
}
}
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Identity/IdentityConfigs.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Identity/IdentityConfigs.cs
index a8f43039..a485040f 100644
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Identity/IdentityConfigs.cs
+++ b/src/Services/Identity/FoodDelivery.Services.Identity/Identity/IdentityConfigs.cs
@@ -28,8 +28,6 @@ public WebApplicationBuilder AddModuleServices(WebApplicationBuilder builder)
{
builder.AddCustomIdentity(builder.Configuration);
- builder.Services.TryAddScoped();
-
if (builder.Environment.IsTest() == false)
builder.AddCustomIdentityServer();
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Data/IdentityDataSeeder.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Data/IdentityDataSeeder.cs
deleted file mode 100644
index 9fc30de1..00000000
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Data/IdentityDataSeeder.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using BuildingBlocks.Abstractions.Persistence;
-
-namespace FoodDelivery.Services.Identity.Shared.Data;
-
-public class IdentityDataSeeder : IDataSeeder
-{
- public int Order => 2;
-
- public Task SeedAllAsync()
- {
- return Task.CompletedTask;
- }
-}
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Identity.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Identity.cs
index 67951adf..1d36e953 100644
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Identity.cs
+++ b/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Identity.cs
@@ -36,8 +36,6 @@ public static WebApplicationBuilder AddCustomIdentity(
// Postgres
builder.Services.AddPostgresDbContext(configuration);
- // add migrations and seeders dependencies, or we could add seeders inner each modules
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
}
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
index 4082fb44..256e55d7 100644
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
+++ b/src/Services/Identity/FoodDelivery.Services.Identity/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
@@ -114,9 +114,9 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder
builder.AddCustomRateLimit();
builder.AddCustomMassTransit(
- configureReceiveEndpoints: (context, cfg) =>
+ configureMessagesTopologies: (context, cfg) =>
{
- cfg.AddUserPublishers();
+ cfg.ConfigureUserMessagesTopology();
},
configureMessagingOptions: msgCfg =>
{
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Users/Features/RegisteringUser/v1/RegisterUser.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Users/Features/RegisteringUser/v1/RegisterUser.cs
index bb8cd572..caec97ce 100644
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Users/Features/RegisteringUser/v1/RegisterUser.cs
+++ b/src/Services/Identity/FoodDelivery.Services.Identity/Users/Features/RegisteringUser/v1/RegisterUser.cs
@@ -68,8 +68,8 @@ public RegisterUserValidator()
.WithMessage("Phone Number is required.")
.MinimumLength(7)
.WithMessage("PhoneNumber must not be less than 7 characters.")
- .MaximumLength(15)
- .WithMessage("PhoneNumber must not exceed 15 characters.");
+ .MaximumLength(20)
+ .WithMessage("PhoneNumber must not exceed 20 characters.");
RuleFor(v => v.ConfirmPassword)
.Equal(x => x.Password)
.WithMessage("The password and confirmation password do not match.")
diff --git a/src/Services/Identity/FoodDelivery.Services.Identity/Users/MassTransitExtensions.cs b/src/Services/Identity/FoodDelivery.Services.Identity/Users/MassTransitExtensions.cs
index d14610ca..cc79bdbe 100644
--- a/src/Services/Identity/FoodDelivery.Services.Identity/Users/MassTransitExtensions.cs
+++ b/src/Services/Identity/FoodDelivery.Services.Identity/Users/MassTransitExtensions.cs
@@ -1,26 +1,61 @@
+using BuildingBlocks.Abstractions.Events;
+using BuildingBlocks.Integration.MassTransit;
using FoodDelivery.Services.Identity.Users.Features.UpdatingUserState.v1.Events.Integration;
using FoodDelivery.Services.Shared.Identity.Users.Events.V1.Integration;
using Humanizer;
using MassTransit;
-using RabbitMQ.Client;
namespace FoodDelivery.Services.Identity.Users;
internal static class MassTransitExtensions
{
- internal static void AddUserPublishers(this IRabbitMqBusFactoryConfigurator cfg)
+ internal static void ConfigureUserMessagesTopology(this IRabbitMqBusFactoryConfigurator cfg)
{
- cfg.Message(e => e.SetEntityName($"{nameof(UserRegisteredV1).Underscore()}.input_exchange")); // name of the primary exchange
- cfg.Publish(e => e.ExchangeType = ExchangeType.Direct); // primary exchange type
- cfg.Send(e =>
+ // https://masstransit.io/documentation/transports/rabbitmq
+ cfg.Message>(e =>
+ {
+ // https://masstransit.io/documentation/configuration/topology/message
+ // Name of the `primary exchange` for type based message publishing and sending
+ // e.SetEntityName($"{nameof(UserRegisteredV1).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
+ });
+
+ // configuration for MessagePublishTopologyConfiguration and using IPublishEndpoint
+ cfg.Publish>(e =>
+ {
+ // we configured some shared settings for all publish message in masstransit publish topologies
+
+ // // setup primary exchange
+ // e.Durable = true;
+ // e.ExchangeType = ExchangeType.Direct;
+ });
+
+ // configuration for MessageSendTopologyConfiguration and using ISendEndpointProvider
+ cfg.Send>(e =>
{
// route by message type to binding fanout exchange (exchange to exchange binding)
e.UseRoutingKeyFormatter(context => context.Message.GetType().Name.Underscore());
});
- cfg.Message(e => e.SetEntityName($"{nameof(UserStateUpdated).Underscore()}.input_exchange")); // name of the primary exchange
- cfg.Publish(e => e.ExchangeType = ExchangeType.Direct); // primary exchange type
- cfg.Send(e =>
+ cfg.Message>(e =>
+ {
+ // https://masstransit.io/documentation/configuration/topology/message
+ // Name of the `primary exchange` for type based message publishing and sending
+ // e.SetEntityName($"{nameof(UserStateUpdated).Underscore()}{MessagingConstants.PrimaryExchangePostfix}");
+ e.SetEntityNameFormatter(new CustomEntityNameFormatter>());
+ });
+
+ cfg.Publish>(e =>
+ {
+ // we configured some shared settings for all publish message in masstransit publish topologies
+
+ // // setup primary exchange
+ // e.Durable = true;
+ // e.ExchangeType = ExchangeType.Direct;
+ });
+
+ // configuration for MessageSendTopologyConfiguration and using ISendEndpointProvider
+ cfg.Send>(e =>
{
// route by message type to binding fanout exchange (exchange to exchange binding)
e.UseRoutingKeyFormatter(context => context.Message.GetType().Name.Underscore());
diff --git a/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs b/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
index e37bd2b2..4f2b698e 100644
--- a/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
+++ b/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Infrastructure.cs
@@ -134,7 +134,7 @@ public static WebApplicationBuilder AddInfrastructure(this WebApplicationBuilder
});
builder.AddCustomMassTransit(
- configureReceiveEndpoints: (context, cfg) =>
+ configureMessagesTopologies: (context, cfg) =>
{
cfg.AddCustomerEndpoints(context);
},
diff --git a/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs b/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs
index 7cd12501..35b9f083 100644
--- a/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs
+++ b/src/Services/Orders/FoodDelivery.Services.Orders/Shared/Extensions/WebApplicationBuilderExtensions/Persistence.cs
@@ -34,8 +34,6 @@ private static void AddPostgresWriteStorage(IServiceCollection services, IConfig
{
services.AddPostgresDbContext(configuration);
- // add migrations and seeders dependencies, or we could add seeders inner each modules
- services.TryAddScoped();
services.TryAddScoped();
}
diff --git a/tests/Services/Customers/FoodDelivery.Services.Customers.EndToEndTests/CustomerServiceEndToEndTestBase.cs b/tests/Services/Customers/FoodDelivery.Services.Customers.EndToEndTests/CustomerServiceEndToEndTestBase.cs
index b4af2d46..15bab4ec 100644
--- a/tests/Services/Customers/FoodDelivery.Services.Customers.EndToEndTests/CustomerServiceEndToEndTestBase.cs
+++ b/tests/Services/Customers/FoodDelivery.Services.Customers.EndToEndTests/CustomerServiceEndToEndTestBase.cs
@@ -75,10 +75,4 @@ ITestOutputHelper outputHelper
// identityApiOptions.Value.BaseApiAddress = MockServersFixture.IdentityServiceMock.Url!;
// catalogApiOptions.Value.BaseApiAddress = MockServersFixture.CatalogsServiceMock.Url!;
}
-
- protected override void RegisterTestConfigureServices(IServiceCollection services)
- {
- //// here we use same data seeder of service but if we need different data seeder for test for can replace it
- // services.ReplaceScoped();
- }
}
diff --git a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/CustomerServiceIntegrationTestBase.cs b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/CustomerServiceIntegrationTestBase.cs
index 58b20ca4..06323605 100644
--- a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/CustomerServiceIntegrationTestBase.cs
+++ b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/CustomerServiceIntegrationTestBase.cs
@@ -74,10 +74,4 @@ ITestOutputHelper outputHelper
// identityApiOptions.Value.BaseApiAddress = MockServersFixture.IdentityServiceMock.Url!;
// catalogApiOptions.Value.BaseApiAddress = MockServersFixture.CatalogsServiceMock.Url!;
}
-
- protected override void RegisterTestConfigureServices(IServiceCollection services)
- {
- //// here we use same data seeder of service but if we need different data seeder for test for can replace it
- // services.ReplaceScoped();
- }
}
diff --git a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerByCustomerId/v1/GetCustomerByCustomerIdTests.cs b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerByCustomerId/v1/GetCustomerByCustomerIdTests.cs
index 3d0b1454..a5621156 100644
--- a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerByCustomerId/v1/GetCustomerByCustomerIdTests.cs
+++ b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerByCustomerId/v1/GetCustomerByCustomerIdTests.cs
@@ -12,14 +12,11 @@
namespace FoodDelivery.Services.Customers.IntegrationTests.Customers.Features.GettingCustomerByCustomerId.v1;
-public class GetCustomerByCustomerIdTests : CustomerServiceIntegrationTestBase
+public class GetCustomerByCustomerIdTests(
+ SharedFixtureWithEfCoreAndMongo sharedFixture,
+ ITestOutputHelper outputHelper
+) : CustomerServiceIntegrationTestBase(sharedFixture, outputHelper)
{
- public GetCustomerByCustomerIdTests(
- SharedFixtureWithEfCoreAndMongo sharedFixture,
- ITestOutputHelper outputHelper
- )
- : base(sharedFixture, outputHelper) { }
-
[Fact]
[CategoryTrait(TestCategory.Integration)]
internal async Task can_returns_valid_read_customer_model()
diff --git a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerById/v1/GetCustomerByIdTests.cs b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerById/v1/GetCustomerByIdTests.cs
index 429856d1..afdd3659 100644
--- a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerById/v1/GetCustomerByIdTests.cs
+++ b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomerById/v1/GetCustomerByIdTests.cs
@@ -11,14 +11,11 @@
namespace FoodDelivery.Services.Customers.IntegrationTests.Customers.Features.GettingCustomerById.v1;
-public class GetCustomerByIdTests : CustomerServiceIntegrationTestBase
+public class GetCustomerByIdTests(
+ SharedFixtureWithEfCoreAndMongo sharedFixture,
+ ITestOutputHelper outputHelper
+) : CustomerServiceIntegrationTestBase(sharedFixture, outputHelper)
{
- public GetCustomerByIdTests(
- SharedFixtureWithEfCoreAndMongo sharedFixture,
- ITestOutputHelper outputHelper
- )
- : base(sharedFixture, outputHelper) { }
-
[Fact]
[CategoryTrait(TestCategory.Integration)]
internal async Task can_returns_valid_read_customer_model()
diff --git a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomers/v1/GetCustomersTests.cs b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomers/v1/GetCustomersTests.cs
index 7d05bbf4..66e688a8 100644
--- a/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomers/v1/GetCustomersTests.cs
+++ b/tests/Services/Customers/FoodDelivery.Services.Customers.IntegrationTests/Customers/Features/GettingCustomers/v1/GetCustomersTests.cs
@@ -9,14 +9,11 @@
namespace FoodDelivery.Services.Customers.IntegrationTests.Customers.Features.GettingCustomers.v1;
-public class GetCustomersTests : CustomerServiceIntegrationTestBase
+public class GetCustomersTests(
+ SharedFixtureWithEfCoreAndMongo sharedFixture,
+ ITestOutputHelper outputHelper
+) : CustomerServiceIntegrationTestBase(sharedFixture, outputHelper)
{
- public GetCustomersTests(
- SharedFixtureWithEfCoreAndMongo sharedFixture,
- ITestOutputHelper outputHelper
- )
- : base(sharedFixture, outputHelper) { }
-
[Fact]
[CategoryTrait(TestCategory.Integration)]
internal async Task can_get_existing_customers_list_from_db()
diff --git a/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IdentityServiceIntegrationTestBase.cs b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IdentityServiceIntegrationTestBase.cs
new file mode 100644
index 00000000..d4df8048
--- /dev/null
+++ b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IdentityServiceIntegrationTestBase.cs
@@ -0,0 +1,18 @@
+using FoodDelivery.Services.Identity.Api;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Tests.Shared.Fixtures;
+using Tests.Shared.TestBase;
+using Xunit.Abstractions;
+
+namespace FoodDelivery.Services.Identity.IntegrationTests;
+
+//https://stackoverflow.com/questions/43082094/use-multiple-collectionfixture-on-my-test-class-in-xunit-2-x
+// note: each class could have only one collection
+[Collection(IntegrationTestCollection.Name)]
+public class IdentityServiceIntegrationTestBase(
+ SharedFixtureWithEfCore sharedFixture,
+ ITestOutputHelper outputHelper
+) : IntegrationTestBase(sharedFixture, outputHelper)
+{
+ // We don't need to inject `CustomersServiceMockServersFixture` class fixture in the constructor because it initialized by `collection fixture` and its static properties are accessible in the codes
+}
diff --git a/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IdentityTestSeeder.cs b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IdentityTestSeeder.cs
new file mode 100644
index 00000000..41b9f10a
--- /dev/null
+++ b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IdentityTestSeeder.cs
@@ -0,0 +1,45 @@
+using BuildingBlocks.Abstractions.Persistence;
+using FoodDelivery.Services.Identity.Shared.Models;
+using Microsoft.AspNetCore.Identity;
+
+namespace FoodDelivery.Services.Identity.IntegrationTests;
+
+public class IdentityTestSeeder(UserManager userManager, RoleManager roleManager)
+ : ITestDataSeeder
+{
+ public int Order => 1;
+
+ public async Task SeedAllAsync()
+ {
+ await SeedRoles();
+ await SeedUsers();
+ }
+
+ private async Task SeedRoles()
+ {
+ if (!await roleManager.RoleExistsAsync(ApplicationRole.Admin.Name))
+ await roleManager.CreateAsync(ApplicationRole.Admin);
+
+ if (!await roleManager.RoleExistsAsync(ApplicationRole.User.Name))
+ await roleManager.CreateAsync(ApplicationRole.User);
+ }
+
+ private async Task SeedUsers()
+ {
+ if (await userManager.FindByEmailAsync("mehdi@test.com") == null)
+ {
+ var user = new ApplicationUser
+ {
+ UserName = "mehdi",
+ FirstName = "Mehdi",
+ LastName = "test",
+ Email = "mehdi@test.com",
+ };
+
+ var result = await userManager.CreateAsync(user, "123456");
+
+ if (result.Succeeded)
+ await userManager.AddToRoleAsync(user, ApplicationRole.Admin.Name);
+ }
+ }
+}
diff --git a/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IntegrationTestCollection.cs b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IntegrationTestCollection.cs
new file mode 100644
index 00000000..84869951
--- /dev/null
+++ b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/IntegrationTestCollection.cs
@@ -0,0 +1,14 @@
+using FoodDelivery.Services.Identity.Api;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Tests.Shared.Fixtures;
+
+namespace FoodDelivery.Services.Identity.IntegrationTests;
+
+// https://stackoverflow.com/questions/43082094/use-multiple-collectionfixture-on-my-test-class-in-xunit-2-x
+// note: each class could have only one collection, but it can implement multiple ICollectionFixture in its definitions
+[CollectionDefinition(Name)]
+public class IntegrationTestCollection
+ : ICollectionFixture>
+{
+ public const string Name = "Integration Test";
+}
diff --git a/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/Users/Features/RegisteringUser/v1/RegisterUserTests.cs b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/Users/Features/RegisteringUser/v1/RegisterUserTests.cs
index ba05eb83..97d9275d 100644
--- a/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/Users/Features/RegisteringUser/v1/RegisterUserTests.cs
+++ b/tests/Services/Identity/FoodDelivery.Services.Identity.IntegrationTests/Users/Features/RegisteringUser/v1/RegisterUserTests.cs
@@ -1,59 +1,64 @@
+using Bogus;
+using FluentAssertions;
+using FoodDelivery.Services.Identity.Api;
+using FoodDelivery.Services.Identity.Users.Features.GettingUserById.v1;
+using FoodDelivery.Services.Identity.Users.Features.RegisteringUser.v1;
+using FoodDelivery.Services.Shared.Identity.Users.Events.V1.Integration;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Tests.Shared.Fixtures;
+using Xunit.Abstractions;
+
namespace FoodDelivery.Services.Identity.IntegrationTests.Users.Features.RegisteringUser.v1;
+public class RegisterUserTests : IdentityServiceIntegrationTestBase
+{
+ private static RegisterUser _registerUser = default!;
+
+ public RegisterUserTests(
+ SharedFixtureWithEfCore sharedFixture,
+ ITestOutputHelper outputHelper
+ )
+ : base(sharedFixture, outputHelper)
+ {
+ // Arrange
+ _registerUser = new Faker()
+ .CustomInstantiator(faker => new RegisterUser(
+ faker.Person.FirstName,
+ faker.Person.LastName,
+ faker.Person.UserName,
+ faker.Person.Email,
+ faker.Phone.PhoneNumber(),
+ "123456",
+ "123456"
+ ))
+ .Generate();
+ }
+
+ [Fact]
+ public async Task register_new_user_command_should_persist_new_user_in_db()
+ {
+ // Act
+ var result = await SharedFixture.SendAsync(_registerUser, CancellationToken);
+
+ // Assert
+ result.UserIdentity.Should().NotBeNull();
+
+ // var user = await IdentityModule.FindWriteAsync(result.UserIdentity.InternalCommandId);
+ // user.Should().NotBeNull();
+
+ var userByIdResponse = await SharedFixture.QueryAsync(new GetUserById(result.UserIdentity!.Id));
+ userByIdResponse.IdentityUser.Should().NotBeNull();
+ userByIdResponse.IdentityUser.Id.Should().Be(result.UserIdentity.Id);
+ }
+ [Fact]
+ public async Task register_new_user_command_should_publish_message_to_broker()
+ {
+ // Act
+ await SharedFixture.SendAsync(_registerUser, CancellationToken);
-// public class RegisterUserTests : IntegrationTestBase
-// {
-// private static RegisterUser _registerUser;
-//
-// public RegisterUserTests(IntegrationTestFixture integrationTestFixture,
-// ITestOutputHelper outputHelper) : base(integrationTestFixture, outputHelper)
-// {
-// // Arrange
-// _registerUser = new Faker().CustomInstantiator(faker =>
-// new RegisterUser(
-// faker.Person.FirstName,
-// faker.Person.LastName,
-// faker.Person.UserName,
-// faker.Person.Email,
-// "123456",
-// "123456"))
-// .Generate();
-// }
-//
-// protected override void RegisterTestsServices(IServiceCollection services)
-// {
-// base.RegisterTestsServices(services);
-// // services.ReplaceScoped();
-// }
-//
-// [Fact]
-// public async Task register_new_user_command_should_persist_new_user_in_db()
-// {
-// // Act
-// var result = await IntegrationTestFixture.SendAsync(_registerUser, CancellationToken);
-//
-// // Assert
-// result.UserIdentity.Should().NotBeNull();
-//
-// // var user = await IdentityModule.FindWriteAsync(result.UserIdentity.InternalCommandId);
-// // user.Should().NotBeNull();
-//
-// var userByIdResponse =
-// await IntegrationTestFixture.QueryAsync(new GetUserById(result.UserIdentity.Id));
-//
-// userByIdResponse.IdentityUser.Should().NotBeNull();
-// userByIdResponse.IdentityUser.Id.Should().Be(result.UserIdentity.Id);
-// }
-//
-// [Fact]
-// public async Task register_new_user_command_should_publish_message_to_broker()
-// {
-// // Act
-// await IntegrationTestFixture.SendAsync(_registerUser, CancellationToken);
-//
-// // Assert
-// await IntegrationTestFixture.WaitForPublishing();
-// }
-// }
+ // Assert
+ await SharedFixture.WaitForPublishing();
+ }
+}
diff --git a/tests/Shared/Tests.Shared/Factory/CustomWebApplicationFactory.cs b/tests/Shared/Tests.Shared/Factory/CustomWebApplicationFactory.cs
index de651a4f..396d3319 100644
--- a/tests/Shared/Tests.Shared/Factory/CustomWebApplicationFactory.cs
+++ b/tests/Shared/Tests.Shared/Factory/CustomWebApplicationFactory.cs
@@ -1,3 +1,5 @@
+using System.Reflection;
+using BuildingBlocks.Core.Persistence.Extensions;
using BuildingBlocks.Core.Web.Extensions;
using BuildingBlocks.Security.Jwt;
using Microsoft.AspNetCore.Authentication;
diff --git a/tests/Shared/Tests.Shared/Fixtures/SharedFixture.cs b/tests/Shared/Tests.Shared/Fixtures/SharedFixture.cs
index 73bd8e9c..50771a53 100644
--- a/tests/Shared/Tests.Shared/Fixtures/SharedFixture.cs
+++ b/tests/Shared/Tests.Shared/Fixtures/SharedFixture.cs
@@ -1,4 +1,5 @@
using System.Net.Http.Headers;
+using System.Reflection;
using System.Security.Claims;
using AutoBogus;
using BuildingBlocks.Abstractions.Commands;
@@ -9,6 +10,7 @@
using BuildingBlocks.Core.Events;
using BuildingBlocks.Core.Extensions;
using BuildingBlocks.Core.Messaging.MessagePersistence;
+using BuildingBlocks.Core.Persistence.Extensions;
using BuildingBlocks.Core.Types;
using BuildingBlocks.Integration.MassTransit;
using BuildingBlocks.Persistence.EfCore.Postgres;
diff --git a/tests/Shared/Tests.Shared/TestBase/EndToEndTestBase.cs b/tests/Shared/Tests.Shared/TestBase/EndToEndTestBase.cs
index 7a79a66f..3fe632f9 100644
--- a/tests/Shared/Tests.Shared/TestBase/EndToEndTestBase.cs
+++ b/tests/Shared/Tests.Shared/TestBase/EndToEndTestBase.cs
@@ -5,42 +5,28 @@
namespace Tests.Shared.Fixtures;
-public class EndToEndTestTest : IntegrationTest
- where TEntryPoint : class
-{
- public EndToEndTestTest(SharedFixture sharedFixture, ITestOutputHelper outputHelper)
- : base(sharedFixture, outputHelper) { }
-}
+public class EndToEndTestTest(SharedFixture sharedFixture, ITestOutputHelper outputHelper)
+ : IntegrationTest(sharedFixture, outputHelper)
+ where TEntryPoint : class;
-public abstract class EndToEndTestTestBase : EndToEndTestTest
+public abstract class EndToEndTestTestBase(
+ SharedFixtureWithEfCore sharedFixture,
+ ITestOutputHelper outputHelper
+) : EndToEndTestTest(sharedFixture, outputHelper)
where TEntryPoint : class
where TContext : DbContext
{
- protected EndToEndTestTestBase(
- SharedFixtureWithEfCore sharedFixture,
- ITestOutputHelper outputHelper
- )
- : base(sharedFixture, outputHelper)
- {
- SharedFixture = sharedFixture;
- }
-
- public new SharedFixtureWithEfCore SharedFixture { get; }
+ public new SharedFixtureWithEfCore SharedFixture { get; } = sharedFixture;
}
-public abstract class EndToEndTestTestBase : EndToEndTestTest
+public abstract class EndToEndTestTestBase(
+ SharedFixtureWithEfCoreAndMongo sharedFixture,
+ ITestOutputHelper outputHelper
+) : EndToEndTestTest(sharedFixture, outputHelper)
where TEntryPoint : class
where TWContext : DbContext
where TRContext : MongoDbContext
{
- protected EndToEndTestTestBase(
- SharedFixtureWithEfCoreAndMongo sharedFixture,
- ITestOutputHelper outputHelper
- )
- : base(sharedFixture, outputHelper)
- {
- SharedFixture = sharedFixture;
- }
-
- public new SharedFixtureWithEfCoreAndMongo SharedFixture { get; }
+ public new SharedFixtureWithEfCoreAndMongo SharedFixture { get; } =
+ sharedFixture;
}
diff --git a/tests/Shared/Tests.Shared/TestBase/IntegrationTestBase.cs b/tests/Shared/Tests.Shared/TestBase/IntegrationTestBase.cs
index 966fd147..bcbb2104 100644
--- a/tests/Shared/Tests.Shared/TestBase/IntegrationTestBase.cs
+++ b/tests/Shared/Tests.Shared/TestBase/IntegrationTestBase.cs
@@ -1,11 +1,10 @@
-using BuildingBlocks.Abstractions.Persistence;
+using BuildingBlocks.Core.Persistence.Extensions;
using BuildingBlocks.Persistence.Mongo;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Tests.Shared.Fixtures;
-using Tests.Shared.XunitCategories;
namespace Tests.Shared.TestBase;
@@ -34,6 +33,10 @@ protected IntegrationTest(SharedFixture sharedFixture, ITestOutputH
CancellationTokenSource = new(TimeSpan.FromSeconds(Timeout));
CancellationToken.ThrowIfCancellationRequested();
+ SharedFixture.ConfigureTestServices(services =>
+ {
+ services.RegisterDataSeeders([GetType().Assembly]);
+ });
SharedFixture.ConfigureTestServices(RegisterTestConfigureServices);
SharedFixture.ConfigureTestConfigureApp(
@@ -45,10 +48,7 @@ protected IntegrationTest(SharedFixture sharedFixture, ITestOutputH
}
// we use IAsyncLifetime in xunit instead of constructor when we have async operation
- public virtual async Task InitializeAsync()
- {
- await RunSeedAndMigrationAsync();
- }
+ public virtual async Task InitializeAsync() { }
public virtual async Task DisposeAsync()
{
@@ -61,31 +61,6 @@ public virtual async Task DisposeAsync()
Scope.Dispose();
}
- private async Task RunSeedAndMigrationAsync()
- {
- var migrations = Scope.ServiceProvider.GetServices();
- var seeders = Scope.ServiceProvider.GetServices();
-
- if (!SharedFixture.AlreadyMigrated)
- {
- foreach (var migration in migrations)
- {
- SharedFixture.Logger.Information("Migration '{Migration}' started...", migrations.GetType().Name);
- await migration.ExecuteAsync(CancellationToken);
- SharedFixture.Logger.Information("Migration '{Migration}' ended...", migration.GetType().Name);
- }
-
- SharedFixture.AlreadyMigrated = true;
- }
-
- foreach (var seeder in seeders)
- {
- SharedFixture.Logger.Information("Seeder '{Seeder}' started...", seeder.GetType().Name);
- await seeder.SeedAllAsync();
- SharedFixture.Logger.Information("Seeder '{Seeder}' ended...", seeder.GetType().Name);
- }
- }
-
protected virtual void RegisterTestConfigureServices(IServiceCollection services) { }
protected virtual void RegisterTestAppConfigurations(