Added MidrandShop feature and spl;it extensions and healthchecks
continuous-integration/drone/pr Build is failing
continuous-integration/drone/pr Build is failing
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
using LiteCharms.Features.Shop.Products;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.Tests;
|
||||||
|
|
||||||
|
public class ProductsFeatureTests(CommonFixture fixture, ITestOutputHelper output) : IClassFixture<CommonFixture>
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task GetProductsAsync_ReturnsProducts()
|
||||||
|
{
|
||||||
|
var productService = fixture.Services.GetRequiredService<ProductService>();
|
||||||
|
|
||||||
|
var result = await productService.GetProductsAsync();
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.NotNull(result.Value);
|
||||||
|
|
||||||
|
output.WriteLine($"Retrieved {result.Value.Length} products.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Features.Shop;
|
using LiteCharms.Features.Shop;
|
||||||
using LiteCharms.Features.Shop.Notifications;
|
using LiteCharms.Features.Shop.Notifications;
|
||||||
using static LiteCharms.Features.Email.Extensions.Constants;
|
using static LiteCharms.Features.Extensions.Email;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email.Events.Handlers;
|
namespace LiteCharms.Features.Email.Events.Handlers;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Extensions;
|
|
||||||
|
|
||||||
public static class Constants
|
|
||||||
{
|
|
||||||
public const string ShopSchedulerName = "shop";
|
|
||||||
public const string ShopEmailFromName = "Khongisa Shop";
|
|
||||||
public const string ShopEmailFromAddress = "shop@litecharms.co.za";
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,10 @@ using LiteCharms.Features.Email.Configuration;
|
|||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Features.Extensions;
|
||||||
|
|
||||||
public static class Email
|
public static class Email
|
||||||
{
|
{
|
||||||
|
public const string ShopEmailFromName = "Khongisa Shop";
|
||||||
|
public const string ShopEmailFromAddress = "shop@litecharms.co.za";
|
||||||
|
|
||||||
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
||||||
|
|||||||
@@ -1,19 +1,34 @@
|
|||||||
using LiteCharms.Features.HealthChecks;
|
using LiteCharms.Features.HealthChecks;
|
||||||
|
using static LiteCharms.Features.Extensions.Postgres;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Features.Extensions;
|
||||||
|
|
||||||
public static class HealthChecks
|
public static class HealthChecks
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddQuartzHealtchCheck(this IServiceCollection services)
|
public static IServiceCollection AddShopQuartzHealthCheck(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddHealthChecks().AddCheck<QuartzHealthCheck>("Quartz");
|
services.AddHealthChecks().AddCheck<ShopQuartzHealthCheck>("ShopQuartz");
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddPostgresHealtchCheck(this IServiceCollection services)
|
public static IServiceCollection AddMidrandShopQuartzHealthCheck(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddHealthChecks().AddCheck<PostgresHealthCheck>("PostgreSQL");
|
services.AddHealthChecks().AddCheck<MidrandShopQuartzHealthCheck>("MidrandShopQuartz");
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddShopPostgresHealthCheck(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHealthChecks().AddCheck<PostgresShopHealthCheck>(ShopDbConfigName);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMidrandShopPostgresHealthCheck(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHealthChecks().AddCheck<PostgresMidrandShopHealthCheck>(MidrandShopDbConfigName);
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Features.MidrandShop.Postgres;
|
||||||
|
using LiteCharms.Features.Shop.Postgres;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Features.Extensions;
|
||||||
|
|
||||||
public static class Postgres
|
public static class Postgres
|
||||||
{
|
{
|
||||||
|
public const string MidrandShopDbConfigName = "PostgresMidrandShop";
|
||||||
|
public const string ShopDbConfigName = "PostgresShop";
|
||||||
|
public const string SchedulerDbConfigName = "PostgresScheduler";
|
||||||
|
|
||||||
public static IServiceCollection AddShopDatabase(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddShopDatabase(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddPooledDbContextFactory<ShopDbContext>(options =>
|
services.AddPooledDbContextFactory<ShopDbContext>(options =>
|
||||||
options.UseNpgsql(configuration.GetConnectionString("PostgresShop")));
|
options.UseNpgsql(configuration.GetConnectionString(ShopDbConfigName)));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMidrandShopDatabase(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddPooledDbContextFactory<MidrandShopDbContext>(options =>
|
||||||
|
options.UseNpgsql(configuration.GetConnectionString(MidrandShopDbConfigName)));
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using LiteCharms.Features.Quartz;
|
using LiteCharms.Features.Quartz;
|
||||||
using LiteCharms.Features.Quartz.Abstractions;
|
using LiteCharms.Features.Quartz.Abstractions;
|
||||||
|
using static LiteCharms.Features.Extensions.Postgres;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Features.Extensions;
|
||||||
|
|
||||||
public static class Quartz
|
public static class Quartz
|
||||||
{
|
{
|
||||||
private const string databaseConfigName = "PostgresScheduler";
|
public const string ShopSchedulerName = "shop";
|
||||||
|
public const string MidrandShopSchedulerName = "midrandshop";
|
||||||
|
|
||||||
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var connectionString = configuration.GetConnectionString(databaseConfigName);
|
var connectionString = configuration.GetConnectionString(SchedulerDbConfigName);
|
||||||
|
|
||||||
services.ConfigureCommon();
|
services.ConfigureCommon();
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ public static class Quartz
|
|||||||
|
|
||||||
public static IServiceCollection AddQuartzScheduler(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
public static IServiceCollection AddQuartzScheduler(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var connectionString = configuration.GetConnectionString(databaseConfigName);
|
var connectionString = configuration.GetConnectionString(SchedulerDbConfigName);
|
||||||
|
|
||||||
services.ConfigureCommon();
|
services.ConfigureCommon();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using static LiteCharms.Features.Extensions.Quartz;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.HealthChecks;
|
||||||
|
|
||||||
|
public class MidrandShopQuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck
|
||||||
|
{
|
||||||
|
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scheduler = await schedulerFactory.GetScheduler(MidrandShopSchedulerName, cancellationToken);
|
||||||
|
|
||||||
|
if(scheduler == null)
|
||||||
|
return HealthCheckResult.Unhealthy($"Scheduler with name '{MidrandShopSchedulerName}' not found.");
|
||||||
|
|
||||||
|
if (!scheduler.IsStarted)
|
||||||
|
return HealthCheckResult.Unhealthy($"{MidrandShopSchedulerName} Quartz scheduler is not running");
|
||||||
|
|
||||||
|
await scheduler.CheckExists(new JobKey(Guid.NewGuid().ToString()), cancellationToken);
|
||||||
|
|
||||||
|
return HealthCheckResult.Healthy($"{MidrandShopSchedulerName} Quartz scheduler is ready");
|
||||||
|
}
|
||||||
|
catch (SchedulerException)
|
||||||
|
{
|
||||||
|
return HealthCheckResult.Unhealthy($"{MidrandShopSchedulerName} Quartz scheduler cannot connect to the store");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using static LiteCharms.Features.Extensions.Postgres;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.HealthChecks;
|
||||||
|
|
||||||
|
public class PostgresMidrandShopHealthCheck(IConfiguration configuration) : IHealthCheck
|
||||||
|
{
|
||||||
|
private readonly string connectionString = configuration.GetConnectionString(MidrandShopDbConfigName)!;
|
||||||
|
|
||||||
|
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var dataSource = NpgsqlDataSource.Create(connectionString);
|
||||||
|
await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);
|
||||||
|
|
||||||
|
await using var command = connection.CreateCommand();
|
||||||
|
command.CommandText = "SELECT 1";
|
||||||
|
|
||||||
|
await command.ExecuteScalarAsync(cancellationToken);
|
||||||
|
|
||||||
|
return HealthCheckResult.Healthy($"{MidrandShopDbConfigName} is responsive.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return HealthCheckResult.Unhealthy($"{MidrandShopDbConfigName} is unreachable.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-5
@@ -1,8 +1,10 @@
|
|||||||
namespace LiteCharms.Features.HealthChecks;
|
using static LiteCharms.Features.Extensions.Postgres;
|
||||||
|
|
||||||
public class PostgresHealthCheck(IConfiguration configuration) : IHealthCheck
|
namespace LiteCharms.Features.HealthChecks;
|
||||||
|
|
||||||
|
public class PostgresShopHealthCheck(IConfiguration configuration) : IHealthCheck
|
||||||
{
|
{
|
||||||
private readonly string connectionString = configuration.GetConnectionString("PostgresShop")!;
|
private readonly string connectionString = configuration.GetConnectionString(ShopDbConfigName)!;
|
||||||
|
|
||||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
@@ -16,11 +18,11 @@ public class PostgresHealthCheck(IConfiguration configuration) : IHealthCheck
|
|||||||
|
|
||||||
await command.ExecuteScalarAsync(cancellationToken);
|
await command.ExecuteScalarAsync(cancellationToken);
|
||||||
|
|
||||||
return HealthCheckResult.Healthy("PostgreSQL is responsive.");
|
return HealthCheckResult.Healthy($"{ShopDbConfigName} is responsive.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return HealthCheckResult.Unhealthy("PostgreSQL is unreachable.", ex);
|
return HealthCheckResult.Unhealthy($"{ShopDbConfigName} is unreachable.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
namespace LiteCharms.Features.HealthChecks;
|
|
||||||
|
|
||||||
public class QuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck
|
|
||||||
{
|
|
||||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var scheduler = await schedulerFactory.GetScheduler(cancellationToken);
|
|
||||||
|
|
||||||
if (!scheduler.IsStarted)
|
|
||||||
return HealthCheckResult.Unhealthy("Quartz scheduler is not running");
|
|
||||||
|
|
||||||
await scheduler.CheckExists(new JobKey(Guid.NewGuid().ToString()), cancellationToken);
|
|
||||||
|
|
||||||
return HealthCheckResult.Healthy("Quartz scheduler is ready");
|
|
||||||
}
|
|
||||||
catch (SchedulerException)
|
|
||||||
{
|
|
||||||
return HealthCheckResult.Unhealthy("Quartz scheduler cannot connect to the store");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using static LiteCharms.Features.Extensions.Quartz;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.HealthChecks;
|
||||||
|
|
||||||
|
public class ShopQuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck
|
||||||
|
{
|
||||||
|
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scheduler = await schedulerFactory.GetScheduler(ShopSchedulerName, cancellationToken);
|
||||||
|
|
||||||
|
if(scheduler == null)
|
||||||
|
return HealthCheckResult.Unhealthy($"Scheduler with name '{ShopSchedulerName}' not found.");
|
||||||
|
|
||||||
|
if (!scheduler.IsStarted)
|
||||||
|
return HealthCheckResult.Unhealthy($"{ShopSchedulerName} Quartz scheduler is not running");
|
||||||
|
|
||||||
|
await scheduler.CheckExists(new JobKey(Guid.NewGuid().ToString()), cancellationToken);
|
||||||
|
|
||||||
|
return HealthCheckResult.Healthy($"{ShopSchedulerName} Quartz scheduler is ready");
|
||||||
|
}
|
||||||
|
catch (SchedulerException)
|
||||||
|
{
|
||||||
|
return HealthCheckResult.Unhealthy($"{ShopSchedulerName} Quartz scheduler cannot connect to the store");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace LiteCharms.Features.MidrandShop.Postgres;
|
||||||
|
|
||||||
|
public class MidrandShopDbContext(DbContextOptions<MidrandShopDbContext> options) : DbContext(options)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using static LiteCharms.Features.Extensions.Postgres;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.MidrandShop.Postgres;
|
||||||
|
|
||||||
|
public class MidrandShopDbContextFactory : IDesignTimeDbContextFactory<MidrandShopDbContext>
|
||||||
|
{
|
||||||
|
public MidrandShopDbContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
|
.AddUserSecrets(typeof(MidrandShopDbContext).Assembly)
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<MidrandShopDbContext>();
|
||||||
|
optionsBuilder.UseNpgsql(configuration.GetConnectionString(MidrandShopDbConfigName));
|
||||||
|
|
||||||
|
return new MidrandShopDbContext(optionsBuilder.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace LiteCharms.Features.Shop.Postgres;
|
using static LiteCharms.Features.Extensions.Postgres;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.Shop.Postgres;
|
||||||
|
|
||||||
public class ShopDbContextFactory : IDesignTimeDbContextFactory<ShopDbContext>
|
public class ShopDbContextFactory : IDesignTimeDbContextFactory<ShopDbContext>
|
||||||
{
|
{
|
||||||
@@ -12,7 +14,7 @@ public class ShopDbContextFactory : IDesignTimeDbContextFactory<ShopDbContext>
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<ShopDbContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<ShopDbContext>();
|
||||||
optionsBuilder.UseNpgsql(configuration.GetConnectionString("PostgresShop"));
|
optionsBuilder.UseNpgsql(configuration.GetConnectionString(ShopDbConfigName));
|
||||||
|
|
||||||
return new ShopDbContext(optionsBuilder.Options);
|
return new ShopDbContext(optionsBuilder.Options);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user