From 902942eee672f7fab6aa8d3d6cb87769f665ca6a Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Wed, 27 May 2026 09:12:04 +0200 Subject: [PATCH] Completed initial database design Sealed qualifying public classes Migrated database changes --- .editorconfig | 39 + .../AuthorBooks/BooksService.cs | 2 +- .../Entities/AuthorBookConfiguration.cs | 2 +- .../Authors/AuthorService.cs | 2 +- .../Authors/Entities/Author.cs | 2 +- .../Authors/Entities/AuthorConfiguration.cs | 2 +- .../Authors/Models/Records.cs | 2 +- .../Customers/CustomerService.cs | 2 +- .../Entities/AddressConfiguration.cs | 2 +- .../Entities/ContactConfiguration.cs | 2 +- .../Entities/CustomerConfiguration.cs | 2 +- .../Customers/Models/Records.cs | 6 +- .../Extensions/Mappers.cs | 45 + .../Extensions/Shop.cs | 23 + .../MidrandShopQuartzHealthCheck.cs | 2 +- .../PostgresMidrandShopHealthCheck.cs | 2 +- .../LiteCharms.Features.MidrandBooks.csproj | 12 +- .../Orders/Entities/Order.cs | 4 +- .../Orders/Entities/OrderConfiguration.cs | 2 +- .../Orders/Entities/OrderItemConfiguration.cs | 4 +- .../Orders/Entities/ShippingConfiguration.cs | 3 +- .../Entities/ShippingProviderConfiguration.cs | 3 +- .../Orders/Models/Records.cs | 10 +- .../Orders/Models/Shipping.cs | 2 + .../Orders/Models/ShippingProvider.cs | 2 + .../Orders/OrderService.cs | 356 ++++++- .../Pages/Entities/BookPageConfiguration.cs | 2 +- .../Pages/Models/UpdateBookPage.cs | 2 +- .../Pages/PageService.cs | 2 +- .../{Orders => Payments}/Entities/Refund.cs | 4 +- .../Entities/RefundConfiguration.cs | 4 +- .../{Orders => Payments}/Models/Refund.cs | 6 +- .../Payments/PaymentService.cs | 7 + .../Postgres/MidrandBooksDbContext.cs | 3 +- .../Postgres/MidrandBooksDbContextFactory.cs | 3 +- .../20260527070840_Init.Designer.cs | 875 ++++++++++++++++++ .../Migrations/20260527070840_Init.cs | 505 ++++++++++ .../MidrandBooksDbContextModelSnapshot.cs | 872 +++++++++++++++++ .../Products/Entities/ProductConfiguration.cs | 2 +- .../Entities/ProductPriceConfiguration.cs | 2 +- .../Products/Models/CreateProductPrice.cs | 2 +- .../Products/Models/Records.cs | 2 +- .../Products/ProductService.cs | 2 +- .../appsettings.json | 22 - ...520191059_AddedProductMetadata.Designer.cs | 1 + .../20260520191059_AddedProductMetadata.cs | 1 + .../Migrations/ShopDbContextModelSnapshot.cs | 1 + .../Products/Models/Records.cs | 4 +- .../Products/ProductService.cs | 3 +- LiteCharms.Features/Abstractions/EventBase.cs | 2 +- .../Email/Configuration/Account.cs | 2 +- .../Email/Configuration/SmtpSettings.cs | 2 +- LiteCharms.Features/Email/EmailService.cs | 77 +- .../Email/Models/Attachment.cs | 2 +- LiteCharms.Features/Email/Models/Body.cs | 2 +- .../Email/Models/BodyProperties.cs | 2 +- LiteCharms.Features/Email/Models/Message.cs | 2 +- LiteCharms.Features/Email/Models/Party.cs | 2 +- LiteCharms.Features/Email/Models/Response.cs | 2 +- LiteCharms.Features/Extensions/Hash.cs | 6 +- LiteCharms.Features/Extensions/Quartz.cs | 2 +- LiteCharms.Features/Extensions/Timezones.cs | 2 +- .../LiteCharms.Features.csproj | 5 + LiteCharms.Features/Models/DateRange.cs | 2 +- LiteCharms.Features/Models/PageReference.cs | 2 +- LiteCharms.Features/Models/ProductFilter.cs | 2 +- LiteCharms.Features/Models/ProductMetadata.cs | 2 +- LiteCharms.Features/Models/SocialMedia.cs | 2 +- LiteCharms.Features/Quartz/JobOrchestrator.cs | 8 +- LiteCharms.Features/Quartz/MediatorJob.cs | 2 +- .../Quartz/RetryJobListener.cs | 2 +- .../S3/Abstractions/S3ServiceBase.cs | 2 +- .../S3/BookshopInvoicesS3Service.cs | 2 +- .../S3/BookshopQuotesS3Service.cs | 2 +- LiteCharms.Features/S3/BookshopS3Service.cs | 2 +- .../S3/Configuration/S3Settings.cs | 2 +- .../ServiceBus/EmailServiceBus.cs | 2 +- .../ServiceBus/Exchanges/EmailExchange.cs | 2 +- .../ServiceBus/Exchanges/GeneralExchange.cs | 2 +- .../ServiceBus/Exchanges/SalesExchange.cs | 2 +- .../ServiceBus/GeneralServiceBus.cs | 2 +- .../ServiceBus/Queues/EmailQueue.cs | 2 +- .../ServiceBus/Queues/GeneralQueue.cs | 2 +- .../ServiceBus/Queues/SalesQueue.cs | 2 +- .../ServiceBus/SalesServiceBus.cs | 2 +- LiteCharmsShared.slnx | 1 + 86 files changed, 2883 insertions(+), 140 deletions(-) create mode 100644 LiteCharms.Features.MidrandBooks/Extensions/Shop.cs rename LiteCharms.Features.MidrandBooks/{Orders => Payments}/Entities/Refund.cs (53%) rename LiteCharms.Features.MidrandBooks/{Orders => Payments}/Entities/RefundConfiguration.cs (84%) rename LiteCharms.Features.MidrandBooks/{Orders => Payments}/Models/Refund.cs (68%) create mode 100644 LiteCharms.Features.MidrandBooks/Payments/PaymentService.cs create mode 100644 LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.Designer.cs create mode 100644 LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.cs create mode 100644 LiteCharms.Features.MidrandBooks/Postgres/Migrations/MidrandBooksDbContextModelSnapshot.cs delete mode 100644 LiteCharms.Features.MidrandBooks/appsettings.json diff --git a/.editorconfig b/.editorconfig index 4a5bdbf..6ab8a85 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,45 @@ root = true # C# files [*.cs] +# IDE0250: Prefer make struct 'readonly' +dotnet_diagnostic.IDE0250.severity = warning + +# IDE0060: Remove unused parameters (Good cleanup pairing) +dotnet_diagnostic.IDE0060.severity = warning + +# CA1852: Seal internal types (Available in modern .NET) +dotnet_diagnostic.CA1852.severity = warning + +# MA0018: Add sealed modifier to types that are never inherited +dotnet_diagnostic.MA0018.severity = warning + +# Enforce that classes should be sealed +dotnet_diagnostic.MA0053.severity = warning + +# CRITICAL: Force the analyzer to also flag PUBLIC classes, not just internal ones +meziantou_analyzer.MA0053.public_class_should_be_sealed = true +MA0053.public_class_should_be_sealed = true + +# Keep the rule active as a warning by default +dotnet_diagnostic.MA0048.severity = warning + +# Specific exclusions for Meziantou.Analyzer MA0048 +# Disable the rule for enums +meziantou_analyzer.MA0048.exclude_enums = true + +# Disable the rule for records +meziantou_analyzer.MA0048.exclude_records = true + +#EXCLUDE specific files that are meant to hold grouped enums/records +dotnet_diagnostic.MA0048.severity = warning + +# Disable the requirement to specify ConfigureAwait(false) +dotnet_diagnostic.MA0004.severity = none + +# ALTERNATIVE: Exclude any file ending with 'Enums.cs' or 'Records.cs' +# (e.g., BillingEnums.cs, CustomerRecords.cs) +[**/*{Enums,Records}.cs] +dotnet_diagnostic.MA0048.severity = none #### Core EditorConfig Options #### diff --git a/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs b/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs index 26a60ec..81ff39f 100644 --- a/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs +++ b/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs @@ -5,7 +5,7 @@ using LiteCharms.Features.MidrandBooks.Postgres; namespace LiteCharms.Features.MidrandBooks.AuthorBooks; -public class BooksService(IDbContextFactory contextFactory) : IService +public sealed class BooksService(IDbContextFactory contextFactory) : IService { public async ValueTask UpdateBookStatusAsync(long bookId, bool isEnabled, CancellationToken cancellationToken) { diff --git a/LiteCharms.Features.MidrandBooks/AuthorBooks/Entities/AuthorBookConfiguration.cs b/LiteCharms.Features.MidrandBooks/AuthorBooks/Entities/AuthorBookConfiguration.cs index 3f852ac..1bc1259 100644 --- a/LiteCharms.Features.MidrandBooks/AuthorBooks/Entities/AuthorBookConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/AuthorBooks/Entities/AuthorBookConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.AuthorBooks.Entities; -public class AuthorBookConfiguration : IEntityTypeConfiguration +public sealed class AuthorBookConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs b/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs index 43ec4aa..2f71d02 100644 --- a/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs +++ b/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs @@ -7,7 +7,7 @@ using LiteCharms.Features.Models; namespace LiteCharms.Features.MidrandBooks.Authors; -public class AuthorService(IDbContextFactory contextFactory) : IService +public sealed class AuthorService(IDbContextFactory contextFactory) : IService { public async ValueTask> GetAuthorBooksAsync(long authorId, CancellationToken cancellationToken) { diff --git a/LiteCharms.Features.MidrandBooks/Authors/Entities/Author.cs b/LiteCharms.Features.MidrandBooks/Authors/Entities/Author.cs index 60b6a2b..28ea8da 100644 --- a/LiteCharms.Features.MidrandBooks/Authors/Entities/Author.cs +++ b/LiteCharms.Features.MidrandBooks/Authors/Entities/Author.cs @@ -3,7 +3,7 @@ namespace LiteCharms.Features.MidrandBooks.Authors.Entities; [EntityTypeConfiguration] -public class Author : Models.Author +public sealed class Author : Models.Author { public ICollection Books { get; set; } = []; } diff --git a/LiteCharms.Features.MidrandBooks/Authors/Entities/AuthorConfiguration.cs b/LiteCharms.Features.MidrandBooks/Authors/Entities/AuthorConfiguration.cs index cd6eda1..0c0af10 100644 --- a/LiteCharms.Features.MidrandBooks/Authors/Entities/AuthorConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Authors/Entities/AuthorConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Authors.Entities; -public class AuthorConfiguration : IEntityTypeConfiguration +public sealed class AuthorConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Authors/Models/Records.cs b/LiteCharms.Features.MidrandBooks/Authors/Models/Records.cs index ba54191..2b6a055 100644 --- a/LiteCharms.Features.MidrandBooks/Authors/Models/Records.cs +++ b/LiteCharms.Features.MidrandBooks/Authors/Models/Records.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.MidrandBooks.Authors.Models; -public record UpdateAuthor : CreateAuthor; +public sealed record UpdateAuthor : CreateAuthor; public record CreateAuthor { diff --git a/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs b/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs index fdb7451..d73292d 100644 --- a/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs +++ b/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs @@ -5,7 +5,7 @@ using LiteCharms.Features.MidrandBooks.Postgres; namespace LiteCharms.Features.MidrandBooks.Customers; -public class CustomerService(IDbContextFactory contextFactory) : IService +public sealed class CustomerService(IDbContextFactory contextFactory) : IService { public async ValueTask> CreateCustomerAsync(CreateCustomer request, CancellationToken cancellationToken = default) { diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs index 6b6997d..4934bac 100644 --- a/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Customers.Entities; -public class AddressConfiguration : IEntityTypeConfiguration
+public sealed class AddressConfiguration : IEntityTypeConfiguration
{ public void Configure(EntityTypeBuilder
builder) { diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs index 8d6fac0..69c931c 100644 --- a/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Customers.Entities; -public class ContactConfiguration : IEntityTypeConfiguration +public sealed class ContactConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs index 2b05c59..f5015a7 100644 --- a/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Customers.Entities; -public class CustomerConfiguration : IEntityTypeConfiguration +public sealed class CustomerConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs b/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs index ceb022c..10c8dae 100644 --- a/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs +++ b/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs @@ -17,7 +17,7 @@ public record CreateCustomer public SocialMedia[]? SocialMedia { get; set; } } -public record UpdateCustomer : CreateCustomer; +public sealed record UpdateCustomer : CreateCustomer; public record CreateCustomerContact { @@ -32,7 +32,7 @@ public record CreateCustomerContact public string? Phone { get; set; } } -public record UpdateCustomerContact : CreateCustomerContact; +public sealed record UpdateCustomerContact : CreateCustomerContact; public record CreateCustomerAddress { @@ -57,4 +57,4 @@ public record CreateCustomerAddress public bool Enabled { get; set; } } -public record UpdateCustomerAddress : CreateCustomerAddress; \ No newline at end of file +public sealed record UpdateCustomerAddress : CreateCustomerAddress; \ No newline at end of file diff --git a/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs b/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs index 1703710..72e57d5 100644 --- a/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs +++ b/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs @@ -1,6 +1,7 @@ using LiteCharms.Features.MidrandBooks.AuthorBooks.Models; using LiteCharms.Features.MidrandBooks.Authors.Models; using LiteCharms.Features.MidrandBooks.Customers.Models; +using LiteCharms.Features.MidrandBooks.Orders.Models; using LiteCharms.Features.MidrandBooks.Pages.Models; using LiteCharms.Features.MidrandBooks.Products.Models; @@ -8,6 +9,50 @@ namespace LiteCharms.Features.MidrandBooks.Extensions; public static class Mappers { + public static ShippingProvider ToModel(this Orders.Entities.ShippingProvider entity) => new() + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + Name = entity.Name, + Type = entity.Type, + Price = entity.Price, + Enabled = entity.Enabled + }; + + public static Shipping ToModel(this Orders.Entities.Shipping entity) => new() + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + OrderId = entity.OrderId, + AddressId = entity.AddressId, + Status = entity.Status, + TrackingNumber = entity.TrackingNumber + }; + + public static OrderItem ToModel(this Orders.Entities.OrderItem entity) => new() + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + OrderId = entity.OrderId, + Quantity = entity.Quantity, + AuthorBookId = entity.AuthorBookId, + ProductPriceId = entity.ProductPriceId + }; + + public static Order ToModel(this Orders.Entities.Order entity) => new() + { + Id = entity.Id, + CustomerId = entity.CustomerId, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + Status = entity.Status, + Total = entity.Total, + InvoiceUrl = entity.InvoiceUrl, + Notes = entity.Notes + }; + public static Customer ToModel(this Customers.Entities.Customer entiry) => new() { Id = entiry.Id, diff --git a/LiteCharms.Features.MidrandBooks/Extensions/Shop.cs b/LiteCharms.Features.MidrandBooks/Extensions/Shop.cs new file mode 100644 index 0000000..43e407e --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Extensions/Shop.cs @@ -0,0 +1,23 @@ +using LiteCharms.Features.MidrandBooks.Abstractions; + +namespace LiteCharms.Features.MidrandBooks.Extensions; + +public static class Shop +{ + public static IServiceCollection AddShopServices(this IServiceCollection services, Assembly assembly, ServiceLifetime serviceLifetime) + { + var serviceType = typeof(IService); + + var implementations = assembly.GetTypes() + .Where(t => serviceType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract); + + foreach (var implementation in implementations) + { + var descriptor = new ServiceDescriptor(serviceType, implementation, serviceLifetime); + + services.Add(descriptor); + } + + return services; + } +} diff --git a/LiteCharms.Features.MidrandBooks/HealthChecks/MidrandShopQuartzHealthCheck.cs b/LiteCharms.Features.MidrandBooks/HealthChecks/MidrandShopQuartzHealthCheck.cs index 23bdf5a..0de8562 100644 --- a/LiteCharms.Features.MidrandBooks/HealthChecks/MidrandShopQuartzHealthCheck.cs +++ b/LiteCharms.Features.MidrandBooks/HealthChecks/MidrandShopQuartzHealthCheck.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.MidrandBooks.HealthChecks; -public class MidrandShopQuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck +public sealed class MidrandShopQuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck { public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { diff --git a/LiteCharms.Features.MidrandBooks/HealthChecks/PostgresMidrandShopHealthCheck.cs b/LiteCharms.Features.MidrandBooks/HealthChecks/PostgresMidrandShopHealthCheck.cs index 5a8b44e..dba8e72 100644 --- a/LiteCharms.Features.MidrandBooks/HealthChecks/PostgresMidrandShopHealthCheck.cs +++ b/LiteCharms.Features.MidrandBooks/HealthChecks/PostgresMidrandShopHealthCheck.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.MidrandBooks.HealthChecks; -public class PostgresMidrandShopHealthCheck(IConfiguration configuration) : IHealthCheck +public sealed class PostgresMidrandShopHealthCheck(IConfiguration configuration) : IHealthCheck { private readonly string connectionString = configuration.GetConnectionString(MidrandBooksDbConfigName)!; diff --git a/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj b/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj index 552ce45..17db6dd 100644 --- a/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj +++ b/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj @@ -31,6 +31,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -143,6 +147,8 @@ + + @@ -157,10 +163,4 @@ - - - PreserveNewest - - - diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/Order.cs b/LiteCharms.Features.MidrandBooks/Orders/Entities/Order.cs index ddc8468..657af6f 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/Order.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Entities/Order.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Features.MidrandBooks.Orders.Entities; +using LiteCharms.Features.MidrandBooks.Payments.Entities; + +namespace LiteCharms.Features.MidrandBooks.Orders.Entities; [EntityTypeConfiguration] public class Order : Models.Order diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderConfiguration.cs b/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderConfiguration.cs index 4063456..c7ae590 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Orders.Entities; -public class OrderConfiguration : IEntityTypeConfiguration +public sealed class OrderConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderItemConfiguration.cs b/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderItemConfiguration.cs index 50ac4da..5a82a1a 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderItemConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Entities/OrderItemConfiguration.cs @@ -1,13 +1,13 @@ namespace LiteCharms.Features.MidrandBooks.Orders.Entities; -public class OrderItemConfiguration : IEntityTypeConfiguration +public sealed class OrderItemConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("OrderItems"); builder.HasKey(oi => oi.Id); - builder.Property(oi => oi.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("new()"); + builder.Property(oi => oi.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); builder.Property(oi => oi.OrderId).IsRequired(); builder.Property(oi => oi.AuthorBookId).IsRequired(); builder.Property(oi => oi.ProductPriceId).IsRequired(); diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingConfiguration.cs b/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingConfiguration.cs index 2da9465..5938ff2 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Orders.Entities; -public class ShippingConfiguration : IEntityTypeConfiguration +public sealed class ShippingConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { @@ -13,6 +13,7 @@ public class ShippingConfiguration : IEntityTypeConfiguration builder.Property(s => s.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); builder.Property(s => s.UpdatedAt).HasDefaultValueSql("now()"); builder.Property(s => s.Status).IsRequired(); + builder.Property(s => s.TrackingNumber).HasMaxLength(255); builder.HasOne(s => s.Order) .WithOne(o => o.Shipping) diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingProviderConfiguration.cs b/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingProviderConfiguration.cs index 003ab98..83b4755 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingProviderConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Entities/ShippingProviderConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Orders.Entities; -public class ShippingProviderConfiguration : IEntityTypeConfiguration +public sealed class ShippingProviderConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { @@ -13,5 +13,6 @@ public class ShippingProviderConfiguration : IEntityTypeConfiguration f.Name).IsRequired().HasMaxLength(100); builder.Property(f => f.Price).IsRequired().HasPrecision(18, 2); builder.Property(f => f.Enabled).HasDefaultValue(true); + builder.Property(f => f.TrackingUrl).HasMaxLength(200); } } diff --git a/LiteCharms.Features.MidrandBooks/Orders/Models/Records.cs b/LiteCharms.Features.MidrandBooks/Orders/Models/Records.cs index a77d3aa..f5453bc 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Models/Records.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Models/Records.cs @@ -1,5 +1,11 @@ namespace LiteCharms.Features.MidrandBooks.Orders.Models; -public record CreateOrder(long CustomerId, decimal TotalPrice, string? Notes); +public sealed record CreateOrder(long CustomerId, decimal TotalPrice, string? Notes); -public record CreateOrderItem(long AuthorBookId, long ProductPriceId, int Quantity); +public sealed record CreateOrderItem(long AuthorBookId, long ProductPriceId, int Quantity); + +public sealed record CreateShipping(long OrderId, long AddressId, long ShippingProviderId, string? TrackingNumber); + +public sealed record CreateShippingProvider(ShippingProviderTypes Type, string Name, decimal Price, string TrackingUrl); + +public sealed record UpdateShippingProvider(long ProviderId, bool Enabled, string Name, decimal Price, string TrackingUrl); diff --git a/LiteCharms.Features.MidrandBooks/Orders/Models/Shipping.cs b/LiteCharms.Features.MidrandBooks/Orders/Models/Shipping.cs index c43bc1f..51491fe 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Models/Shipping.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Models/Shipping.cs @@ -14,5 +14,7 @@ public class Shipping public long ShippingProviderId { get; set; } + public string? TrackingNumber { get; set; } + public ShippingStatuses Status { get; set; } } diff --git a/LiteCharms.Features.MidrandBooks/Orders/Models/ShippingProvider.cs b/LiteCharms.Features.MidrandBooks/Orders/Models/ShippingProvider.cs index 76a396e..a26d934 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Models/ShippingProvider.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/Models/ShippingProvider.cs @@ -14,5 +14,7 @@ public class ShippingProvider public decimal? Price { get; set; } + public string? TrackingUrl { get; set; } + public bool Enabled { get; set; } } diff --git a/LiteCharms.Features.MidrandBooks/Orders/OrderService.cs b/LiteCharms.Features.MidrandBooks/Orders/OrderService.cs index 9e37122..c15b2bf 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/OrderService.cs +++ b/LiteCharms.Features.MidrandBooks/Orders/OrderService.cs @@ -1,10 +1,12 @@ using LiteCharms.Features.MidrandBooks.Abstractions; +using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Orders.Models; using LiteCharms.Features.MidrandBooks.Postgres; +using LiteCharms.Features.Models; namespace LiteCharms.Features.MidrandBooks.Orders; -public class OrderService(IDbContextFactory contextFactory) : IService +public sealed class OrderService(IDbContextFactory contextFactory) : IService { public async ValueTask> CreateOrderAsync(long customerId, CreateOrder request, CancellationToken cancellationToken = default) { @@ -106,4 +108,356 @@ public class OrderService(IDbContextFactory contextFactor return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } + + public async ValueTask RemoveItemFromOrderAsync(long orderId, long orderItemId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var orderItem = await context.OrderItems.FirstOrDefaultAsync(oi => oi.Id == orderItemId && oi.OrderId == orderId, cancellationToken); + + if (orderItem is null) + return Result.Fail("Order item not found."); + + context.OrderItems.Remove(orderItem); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to remove item from order."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask ClearOrderItemasAsync(long orderId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var orderItems = context.OrderItems.Where(oi => oi.OrderId == orderId); + + context.OrderItems.RemoveRange(orderItems); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to clear order items."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask CancelOrderAsync(long orderId, CancellationToken cancellationToken = default) => + await UpdateOrderStatusAsync(orderId, OrderStatus.Cancelled, cancellationToken); + + public async ValueTask> GetOrderAsync(long orderId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var order = await context.Orders.AsNoTracking().FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken); + + return order is not null + ? Result.Ok(order.ToModel()) + : Result.Fail("Order not found."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetOrdersByCustomerAsync(long customerId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if(!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail("Customer not found."); + + var orders = await context.Orders + .AsNoTracking() + .Where(o => o.CustomerId == customerId) + .ToListAsync(cancellationToken); + + return Result.Ok(orders.Select(o => o.ToModel()).ToArray()); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetOrdersAsync(DateRange range, int index, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var orders = await context.Orders + .AsNoTracking() + .Where(o => o.CreatedAt >= range.From.ToDateTime(TimeOnly.MinValue) && o.CreatedAt <= range.To.ToDateTime(TimeOnly.MaxValue)) + .Skip(index * range.MaxRecords) + .ToListAsync(cancellationToken); + + return Result.Ok(orders.Select(o => o.ToModel()).ToArray()); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateOrderStatusAsync(long orderId, OrderStatus newStatus, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var order = await context.Orders.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken); + + if (order is null) + return Result.Fail("Order not found."); + + order.UpdatedAt = DateTime.UtcNow; + order.Status = newStatus; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to update order status."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask AddShippingToOrderAsync(long orderId, CreateShipping request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if(!await context.Orders.AnyAsync(o => o.Id == orderId, cancellationToken)) + return Result.Fail("Order not found."); + + if(!await context.Addresses.AnyAsync(a => a.Id == request.AddressId, cancellationToken)) + return Result.Fail("Address not found."); + + if(!await context.ShippingProviders.AnyAsync(sp => sp.Id == request.ShippingProviderId && sp.Enabled, cancellationToken)) + return Result.Fail("Shipping provider not found or disabled."); + + if(await context.Shippings.AnyAsync(s => s.OrderId == orderId, cancellationToken)) + return Result.Fail("Shipping already exists for this order."); + + var shipping = context.Shippings.Add(new Entities.Shipping + { + OrderId = orderId, + AddressId = request.AddressId, + ShippingProviderId = request.ShippingProviderId, + Status = ShippingStatuses.Pending, + TrackingNumber = request.TrackingNumber + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to add shipping to order."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateShippingStatusAsync(long orderId, ShippingStatuses newStatus, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var shipping = await context.Shippings.FirstOrDefaultAsync(s => s.OrderId == orderId, cancellationToken); + + if (shipping is null) + return Result.Fail("Shipping not found for this order."); + + shipping.UpdatedAt = DateTime.UtcNow; + shipping.Status = newStatus; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to update shipping status."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async Task> GetShippingByOrderIdAsync(long orderId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var shipping = await context.Shippings + .AsNoTracking() + .FirstOrDefaultAsync(s => s.OrderId == orderId, cancellationToken); + + return shipping is not null + ? Result.Ok(shipping.ToModel()) + : Result.Fail("Shipping not found for this order."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async Task RemoveShippingFromOrderAsync(long orderId, long shippingId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if(!await context.Orders.AnyAsync(o => o.Id == orderId, cancellationToken)) + return Result.Fail("Order not found."); + + var shipping = await context.Shippings.AsNoTracking() + .FirstOrDefaultAsync(s => s.OrderId == orderId && s.Id == shippingId, cancellationToken); + + if (shipping is null) + return Result.Fail("Shipping not found for this order."); + + context.Shippings.Remove(shipping); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to remove shipping from order."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateShippingTrackingNumberAsync(long orderId, long shippingId, string trackingNumber, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var shipping = await context.Shippings.FirstOrDefaultAsync(s => s.OrderId == orderId && s.Id == shippingId, cancellationToken); + + if (shipping is null) + return Result.Fail("Shipping not found for this order."); + + shipping.UpdatedAt = DateTime.UtcNow; + shipping.TrackingNumber = trackingNumber; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to update shipping tracking number."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask CreateShippingProviderAsync(CreateShippingProvider request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if(await context.ShippingProviders.AnyAsync(sp => sp.Type == request.Type, cancellationToken)) + return Result.Fail("Shipping provider with the same type already exists."); + + var shippingProvider = context.ShippingProviders.Add(new Entities.ShippingProvider + { + Name = request.Name, + Type = request.Type, + Price = request.Price, + TrackingUrl = request.TrackingUrl + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to create shipping provider."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetShippingProvidersAsync(bool isEnabled, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var providers = await context.ShippingProviders.AsNoTracking().Where(sp => sp.Enabled == isEnabled) + .ToListAsync(cancellationToken); + + return Result.Ok(providers.Select(sp => sp.ToModel()).ToArray()); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetShippingProviderAsync(long providerId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var provider = await context.ShippingProviders.AsNoTracking() + .FirstOrDefaultAsync(sp => sp.Id == providerId, cancellationToken); + + return provider is not null + ? Result.Ok(provider.ToModel()) + : Result.Fail("Shipping provider not found."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateShippingProviderAsync(UpdateShippingProvider request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var provider = await context.ShippingProviders.FirstOrDefaultAsync(sp => sp.Id == request.ProviderId, cancellationToken); + + if (provider is null) + return Result.Fail("Shipping provider not found."); + + provider.UpdatedAt = DateTime.UtcNow; + provider.Enabled = request.Enabled; + provider.Name = request.Name; + provider.Price = request.Price; + provider.TrackingUrl = request.TrackingUrl; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail("Failed to update shipping provider status."); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } } diff --git a/LiteCharms.Features.MidrandBooks/Pages/Entities/BookPageConfiguration.cs b/LiteCharms.Features.MidrandBooks/Pages/Entities/BookPageConfiguration.cs index 48c8196..d6c9fed 100644 --- a/LiteCharms.Features.MidrandBooks/Pages/Entities/BookPageConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Pages/Entities/BookPageConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Pages.Entities; -public class BookPageConfiguration : IEntityTypeConfiguration +public sealed class BookPageConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Pages/Models/UpdateBookPage.cs b/LiteCharms.Features.MidrandBooks/Pages/Models/UpdateBookPage.cs index d78b732..7016067 100644 --- a/LiteCharms.Features.MidrandBooks/Pages/Models/UpdateBookPage.cs +++ b/LiteCharms.Features.MidrandBooks/Pages/Models/UpdateBookPage.cs @@ -1,3 +1,3 @@ namespace LiteCharms.Features.MidrandBooks.Pages.Models; -public class UpdateBookPage : CreateBookPage; +public sealed class UpdateBookPage : CreateBookPage; diff --git a/LiteCharms.Features.MidrandBooks/Pages/PageService.cs b/LiteCharms.Features.MidrandBooks/Pages/PageService.cs index 5c6f98a..80f3a5d 100644 --- a/LiteCharms.Features.MidrandBooks/Pages/PageService.cs +++ b/LiteCharms.Features.MidrandBooks/Pages/PageService.cs @@ -5,7 +5,7 @@ using LiteCharms.Features.MidrandBooks.Postgres; namespace LiteCharms.Features.MidrandBooks.Pages; -public class PageService(IDbContextFactory contextFactory) : IService +public sealed class PageService(IDbContextFactory contextFactory) : IService { public async ValueTask DeleteAllAsync(long authorBookId, CancellationToken cancellationToken = default) { diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/Refund.cs b/LiteCharms.Features.MidrandBooks/Payments/Entities/Refund.cs similarity index 53% rename from LiteCharms.Features.MidrandBooks/Orders/Entities/Refund.cs rename to LiteCharms.Features.MidrandBooks/Payments/Entities/Refund.cs index 3116896..585ee1a 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/Refund.cs +++ b/LiteCharms.Features.MidrandBooks/Payments/Entities/Refund.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Features.MidrandBooks.Orders.Entities; +using LiteCharms.Features.MidrandBooks.Orders.Entities; + +namespace LiteCharms.Features.MidrandBooks.Payments.Entities; [EntityTypeConfiguration] public class Refund : Models.Refund diff --git a/LiteCharms.Features.MidrandBooks/Orders/Entities/RefundConfiguration.cs b/LiteCharms.Features.MidrandBooks/Payments/Entities/RefundConfiguration.cs similarity index 84% rename from LiteCharms.Features.MidrandBooks/Orders/Entities/RefundConfiguration.cs rename to LiteCharms.Features.MidrandBooks/Payments/Entities/RefundConfiguration.cs index 566b779..5227c6e 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Entities/RefundConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Payments/Entities/RefundConfiguration.cs @@ -1,6 +1,6 @@ -namespace LiteCharms.Features.MidrandBooks.Orders.Entities; +namespace LiteCharms.Features.MidrandBooks.Payments.Entities; -public class RefundConfiguration : IEntityTypeConfiguration +public sealed class RefundConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Orders/Models/Refund.cs b/LiteCharms.Features.MidrandBooks/Payments/Models/Refund.cs similarity index 68% rename from LiteCharms.Features.MidrandBooks/Orders/Models/Refund.cs rename to LiteCharms.Features.MidrandBooks/Payments/Models/Refund.cs index 3f5eec4..5e81d3a 100644 --- a/LiteCharms.Features.MidrandBooks/Orders/Models/Refund.cs +++ b/LiteCharms.Features.MidrandBooks/Payments/Models/Refund.cs @@ -1,14 +1,14 @@ -namespace LiteCharms.Features.MidrandBooks.Orders.Models; +namespace LiteCharms.Features.MidrandBooks.Payments.Models; public class Refund { - public Guid Id { get; set; } + public long Id { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } - public Guid OrderId { get; set; } + public long OrderId { get; set; } public RefundTypes Type { get; set; } diff --git a/LiteCharms.Features.MidrandBooks/Payments/PaymentService.cs b/LiteCharms.Features.MidrandBooks/Payments/PaymentService.cs new file mode 100644 index 0000000..ce3668a --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Payments/PaymentService.cs @@ -0,0 +1,7 @@ +using LiteCharms.Features.MidrandBooks.Abstractions; + +namespace LiteCharms.Features.MidrandBooks.Payments; + +public sealed class PaymentService : IService +{ +} diff --git a/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs b/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs index 901933e..a70e1cf 100644 --- a/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs +++ b/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs @@ -3,11 +3,12 @@ using LiteCharms.Features.MidrandBooks.Authors.Entities; using LiteCharms.Features.MidrandBooks.Customers.Entities; using LiteCharms.Features.MidrandBooks.Orders.Entities; using LiteCharms.Features.MidrandBooks.Pages.Entities; +using LiteCharms.Features.MidrandBooks.Payments.Entities; using LiteCharms.Features.MidrandBooks.Products.Entities; namespace LiteCharms.Features.MidrandBooks.Postgres; -public class MidrandBooksDbContext(DbContextOptions options) : DbContext(options) +public sealed class MidrandBooksDbContext(DbContextOptions options) : DbContext(options) { public DbSet Authors => Set(); diff --git a/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContextFactory.cs b/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContextFactory.cs index e518cfd..fe25033 100644 --- a/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContextFactory.cs +++ b/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContextFactory.cs @@ -2,14 +2,13 @@ namespace LiteCharms.Features.MidrandBooks.Postgres; -public class MidrandBooksDbContextFactory : IDesignTimeDbContextFactory +public sealed class MidrandBooksDbContextFactory : IDesignTimeDbContextFactory { public MidrandBooksDbContext CreateDbContext(string[] args) { var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddUserSecrets(typeof(MidrandBooksDbContext).Assembly) - .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build(); diff --git a/LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.Designer.cs b/LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.Designer.cs new file mode 100644 index 0000000..856fd68 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.Designer.cs @@ -0,0 +1,875 @@ +// +using System; +using LiteCharms.Features.MidrandBooks.Postgres; +using LiteCharms.Features.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations +{ + [DbContext(typeof(MidrandBooksDbContext))] + [Migration("20260527070840_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("Ranking") + .HasColumnType("integer"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.HasIndex("ProductId"); + + b.ToTable("Books"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Biography") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ImageUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("PublisherType") + .HasColumnType("integer"); + + b.Property("SocialMedia") + .HasColumnType("jsonb"); + + b.Property("ThumbnailImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("VatNumber") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Website") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.HasKey("Id"); + + b.ToTable("Authors", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuildingType") + .HasColumnType("integer"); + + b.Property("City") + .IsRequired() + .HasColumnType("text"); + + b.Property("Country") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("bigint"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsPrimary") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.Property("Street") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Addresses", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsPrimary") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Contacts", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("SocialMedia") + .HasColumnType("jsonb"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("VatNumber") + .HasColumnType("text"); + + b.Property("Website") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("bigint"); + + b.Property("InvoiceUrl") + .HasColumnType("text"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorBookId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("ProductPriceId") + .HasColumnType("bigint"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AuthorBookId"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductPriceId"); + + b.ToTable("OrderItems", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Shipping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("ShippingProviderId") + .HasColumnType("bigint"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TrackingNumber") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.HasIndex("ShippingProviderId"); + + b.ToTable("Shippings", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.ShippingProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("TrackingUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("ShippingProviders"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Pages.Entities.BookPage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorBookId") + .HasColumnType("bigint"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ContentType") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.PrimitiveCollection("Notes") + .HasColumnType("jsonb"); + + b.Property("Number") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("References") + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("AuthorBookId"); + + b.ToTable("BookPages", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Payments.Entities.Refund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("Refunds", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.PrimitiveCollection("Categories") + .HasColumnType("jsonb"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("ImageUrl") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Metadata") + .HasColumnType("jsonb"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.PrimitiveCollection("ThumbnailUrls") + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.ToTable("Products", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Discount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Models.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Discount") + .HasColumnType("numeric"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductId") + .IsUnique(); + + b.ToTable("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", "Author") + .WithMany("Books") + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Author"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", "Customer") + .WithMany("Addresses") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Contact", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", "Customer") + .WithMany("Contacts") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.OrderItem", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", "AuthorBook") + .WithMany() + .HasForeignKey("AuthorBookId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", "Order") + .WithMany("OrderItems") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AuthorBook"); + + b.Navigation("Order"); + + b.Navigation("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Shipping", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", "Address") + .WithMany() + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", "Order") + .WithOne("Shipping") + .HasForeignKey("LiteCharms.Features.MidrandBooks.Orders.Entities.Shipping", "OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.ShippingProvider", "ShippingProvider") + .WithMany("Shippings") + .HasForeignKey("ShippingProviderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Order"); + + b.Navigation("ShippingProvider"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Pages.Entities.BookPage", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", "Book") + .WithMany("Pages") + .HasForeignKey("AuthorBookId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Payments.Entities.Refund", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", "Order") + .WithMany("Refunds") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", "Product") + .WithMany("Prices") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Models.ProductPrice", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", null) + .WithOne("Price") + .HasForeignKey("LiteCharms.Features.MidrandBooks.Products.Models.ProductPrice", "ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", b => + { + b.Navigation("Pages"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", b => + { + b.Navigation("Books"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", b => + { + b.Navigation("Addresses"); + + b.Navigation("Contacts"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", b => + { + b.Navigation("OrderItems"); + + b.Navigation("Refunds"); + + b.Navigation("Shipping"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.ShippingProvider", b => + { + b.Navigation("Shippings"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.Product", b => + { + b.Navigation("Price"); + + b.Navigation("Prices"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.cs b/LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.cs new file mode 100644 index 0000000..774c77a --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Postgres/Migrations/20260527070840_Init.cs @@ -0,0 +1,505 @@ +using System; +using LiteCharms.Features.Models; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Authors", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + PublisherType = table.Column(type: "integer", nullable: false), + Company = table.Column(type: "text", nullable: true), + VatNumber = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + Name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + LastName = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Biography = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Email = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Website = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + ImageUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: false), + ThumbnailImageUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + SocialMedia = table.Column(type: "jsonb", nullable: true), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Authors", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Customers", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + Company = table.Column(type: "text", nullable: true), + VatNumber = table.Column(type: "text", nullable: true), + Email = table.Column(type: "text", nullable: false), + Website = table.Column(type: "text", nullable: false), + Phone = table.Column(type: "text", nullable: false), + SocialMedia = table.Column(type: "jsonb", nullable: true), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Customers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + CustomerId = table.Column(type: "bigint", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Total = table.Column(type: "numeric(18,2)", nullable: false), + Notes = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + InvoiceUrl = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + Type = table.Column(type: "integer", nullable: false), + Name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Summary = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Description = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + ImageUrl = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + ThumbnailUrls = table.Column(type: "jsonb", nullable: true), + Categories = table.Column(type: "jsonb", nullable: true), + Metadata = table.Column(type: "jsonb", nullable: true), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ShippingProviders", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + Type = table.Column(type: "integer", nullable: false), + Name = table.Column(type: "text", nullable: true), + Price = table.Column(type: "numeric", nullable: true), + TrackingUrl = table.Column(type: "text", nullable: true), + Enabled = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShippingProviders", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Addresses", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CustomerId = table.Column(type: "bigint", nullable: false), + Name = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + Type = table.Column(type: "integer", nullable: false), + BuildingType = table.Column(type: "integer", nullable: false), + Street = table.Column(type: "text", nullable: false), + City = table.Column(type: "text", nullable: false), + State = table.Column(type: "text", nullable: false), + PostalCode = table.Column(type: "text", nullable: false), + Country = table.Column(type: "text", nullable: false), + IsPrimary = table.Column(type: "boolean", nullable: false, defaultValue: false), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Addresses", x => x.Id); + table.ForeignKey( + name: "FK_Addresses_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Contacts", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CustomerId = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + Type = table.Column(type: "integer", nullable: false), + Name = table.Column(type: "text", nullable: false), + LastName = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Phone = table.Column(type: "text", nullable: false), + IsPrimary = table.Column(type: "boolean", nullable: false, defaultValue: false), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Contacts", x => x.Id); + table.ForeignKey( + name: "FK_Contacts_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Refunds", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + OrderId = table.Column(type: "bigint", nullable: false), + Type = table.Column(type: "integer", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Reason = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + Amount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Refunds", x => x.Id); + table.ForeignKey( + name: "FK_Refunds_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Books", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + AuthorId = table.Column(type: "bigint", nullable: false), + ProductId = table.Column(type: "bigint", nullable: false), + Rating = table.Column(type: "integer", nullable: false), + Ranking = table.Column(type: "integer", nullable: false), + Enabled = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Books", x => x.Id); + table.ForeignKey( + name: "FK_Books_Authors_AuthorId", + column: x => x.AuthorId, + principalTable: "Authors", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Books_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Prices", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + ProductId = table.Column(type: "bigint", nullable: false), + Amount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + Discount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Prices", x => x.Id); + table.ForeignKey( + name: "FK_Prices_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "ProductPrice", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + ProductId = table.Column(type: "bigint", nullable: false), + Amount = table.Column(type: "numeric", nullable: false), + Discount = table.Column(type: "numeric", nullable: false), + Enabled = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProductPrice", x => x.Id); + table.ForeignKey( + name: "FK_ProductPrice_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Shippings", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + OrderId = table.Column(type: "bigint", nullable: false), + AddressId = table.Column(type: "bigint", nullable: false), + ShippingProviderId = table.Column(type: "bigint", nullable: false), + TrackingNumber = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + Status = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Shippings", x => x.Id); + table.ForeignKey( + name: "FK_Shippings_Addresses_AddressId", + column: x => x.AddressId, + principalTable: "Addresses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Shippings_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Shippings_ShippingProviders_ShippingProviderId", + column: x => x.ShippingProviderId, + principalTable: "ShippingProviders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "BookPages", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + AuthorBookId = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"), + Type = table.Column(type: "integer", nullable: false), + ContentType = table.Column(type: "integer", nullable: false), + Number = table.Column(type: "integer", nullable: false, defaultValue: 0), + Content = table.Column(type: "bytea", nullable: false), + Notes = table.Column(type: "jsonb", nullable: true), + References = table.Column(type: "jsonb", nullable: true), + Enabled = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_BookPages", x => x.Id); + table.ForeignKey( + name: "FK_BookPages_Books_AuthorBookId", + column: x => x.AuthorBookId, + principalTable: "Books", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "OrderItems", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + OrderId = table.Column(type: "bigint", nullable: false), + AuthorBookId = table.Column(type: "bigint", nullable: false), + ProductPriceId = table.Column(type: "bigint", nullable: false), + Quantity = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderItems", x => x.Id); + table.ForeignKey( + name: "FK_OrderItems_Books_AuthorBookId", + column: x => x.AuthorBookId, + principalTable: "Books", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_OrderItems_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_OrderItems_Prices_ProductPriceId", + column: x => x.ProductPriceId, + principalTable: "Prices", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Addresses_CustomerId", + table: "Addresses", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_BookPages_AuthorBookId", + table: "BookPages", + column: "AuthorBookId"); + + migrationBuilder.CreateIndex( + name: "IX_Books_AuthorId", + table: "Books", + column: "AuthorId"); + + migrationBuilder.CreateIndex( + name: "IX_Books_ProductId", + table: "Books", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_Contacts_CustomerId", + table: "Contacts", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItems_AuthorBookId", + table: "OrderItems", + column: "AuthorBookId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItems_OrderId", + table: "OrderItems", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItems_ProductPriceId", + table: "OrderItems", + column: "ProductPriceId"); + + migrationBuilder.CreateIndex( + name: "IX_Prices_ProductId", + table: "Prices", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_ProductPrice_ProductId", + table: "ProductPrice", + column: "ProductId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Refunds_OrderId", + table: "Refunds", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_Shippings_AddressId", + table: "Shippings", + column: "AddressId"); + + migrationBuilder.CreateIndex( + name: "IX_Shippings_OrderId", + table: "Shippings", + column: "OrderId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Shippings_ShippingProviderId", + table: "Shippings", + column: "ShippingProviderId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "BookPages"); + + migrationBuilder.DropTable( + name: "Contacts"); + + migrationBuilder.DropTable( + name: "OrderItems"); + + migrationBuilder.DropTable( + name: "ProductPrice"); + + migrationBuilder.DropTable( + name: "Refunds"); + + migrationBuilder.DropTable( + name: "Shippings"); + + migrationBuilder.DropTable( + name: "Books"); + + migrationBuilder.DropTable( + name: "Prices"); + + migrationBuilder.DropTable( + name: "Addresses"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "ShippingProviders"); + + migrationBuilder.DropTable( + name: "Authors"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "Customers"); + } + } +} diff --git a/LiteCharms.Features.MidrandBooks/Postgres/Migrations/MidrandBooksDbContextModelSnapshot.cs b/LiteCharms.Features.MidrandBooks/Postgres/Migrations/MidrandBooksDbContextModelSnapshot.cs new file mode 100644 index 0000000..04c9235 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Postgres/Migrations/MidrandBooksDbContextModelSnapshot.cs @@ -0,0 +1,872 @@ +// +using System; +using LiteCharms.Features.MidrandBooks.Postgres; +using LiteCharms.Features.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations +{ + [DbContext(typeof(MidrandBooksDbContext))] + partial class MidrandBooksDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("Ranking") + .HasColumnType("integer"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.HasIndex("ProductId"); + + b.ToTable("Books"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Biography") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ImageUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("PublisherType") + .HasColumnType("integer"); + + b.Property("SocialMedia") + .HasColumnType("jsonb"); + + b.Property("ThumbnailImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("VatNumber") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Website") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.HasKey("Id"); + + b.ToTable("Authors", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuildingType") + .HasColumnType("integer"); + + b.Property("City") + .IsRequired() + .HasColumnType("text"); + + b.Property("Country") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("bigint"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsPrimary") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.Property("Street") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Addresses", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsPrimary") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Contacts", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("SocialMedia") + .HasColumnType("jsonb"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("VatNumber") + .HasColumnType("text"); + + b.Property("Website") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("bigint"); + + b.Property("InvoiceUrl") + .HasColumnType("text"); + + b.Property("Notes") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorBookId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("ProductPriceId") + .HasColumnType("bigint"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AuthorBookId"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductPriceId"); + + b.ToTable("OrderItems", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Shipping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("ShippingProviderId") + .HasColumnType("bigint"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TrackingNumber") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.HasIndex("ShippingProviderId"); + + b.ToTable("Shippings", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.ShippingProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("TrackingUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("ShippingProviders"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Pages.Entities.BookPage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorBookId") + .HasColumnType("bigint"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ContentType") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.PrimitiveCollection("Notes") + .HasColumnType("jsonb"); + + b.Property("Number") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("References") + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("AuthorBookId"); + + b.ToTable("BookPages", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Payments.Entities.Refund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("Reason") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("Refunds", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.PrimitiveCollection("Categories") + .HasColumnType("jsonb"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("ImageUrl") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Metadata") + .HasColumnType("jsonb"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.PrimitiveCollection("ThumbnailUrls") + .HasColumnType("jsonb"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.ToTable("Products", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Discount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("Prices", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Models.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Discount") + .HasColumnType("numeric"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductId") + .IsUnique(); + + b.ToTable("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", "Author") + .WithMany("Books") + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Author"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", "Customer") + .WithMany("Addresses") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Contact", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", "Customer") + .WithMany("Contacts") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.OrderItem", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", "AuthorBook") + .WithMany() + .HasForeignKey("AuthorBookId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", "Order") + .WithMany("OrderItems") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AuthorBook"); + + b.Navigation("Order"); + + b.Navigation("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Shipping", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", "Address") + .WithMany() + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", "Order") + .WithOne("Shipping") + .HasForeignKey("LiteCharms.Features.MidrandBooks.Orders.Entities.Shipping", "OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.ShippingProvider", "ShippingProvider") + .WithMany("Shippings") + .HasForeignKey("ShippingProviderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Order"); + + b.Navigation("ShippingProvider"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Pages.Entities.BookPage", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", "Book") + .WithMany("Pages") + .HasForeignKey("AuthorBookId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Payments.Entities.Refund", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", "Order") + .WithMany("Refunds") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", "Product") + .WithMany("Prices") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Models.ProductPrice", b => + { + b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", null) + .WithOne("Price") + .HasForeignKey("LiteCharms.Features.MidrandBooks.Products.Models.ProductPrice", "ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", b => + { + b.Navigation("Pages"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", b => + { + b.Navigation("Books"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", b => + { + b.Navigation("Addresses"); + + b.Navigation("Contacts"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.Order", b => + { + b.Navigation("OrderItems"); + + b.Navigation("Refunds"); + + b.Navigation("Shipping"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.ShippingProvider", b => + { + b.Navigation("Shippings"); + }); + + modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.Product", b => + { + b.Navigation("Price"); + + b.Navigation("Prices"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiteCharms.Features.MidrandBooks/Products/Entities/ProductConfiguration.cs b/LiteCharms.Features.MidrandBooks/Products/Entities/ProductConfiguration.cs index 71515b2..e360385 100644 --- a/LiteCharms.Features.MidrandBooks/Products/Entities/ProductConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Products/Entities/ProductConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Products.Entities; -public class ProductConfiguration : IEntityTypeConfiguration +public sealed class ProductConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Products/Entities/ProductPriceConfiguration.cs b/LiteCharms.Features.MidrandBooks/Products/Entities/ProductPriceConfiguration.cs index 4705ba5..635ec8c 100644 --- a/LiteCharms.Features.MidrandBooks/Products/Entities/ProductPriceConfiguration.cs +++ b/LiteCharms.Features.MidrandBooks/Products/Entities/ProductPriceConfiguration.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Products.Entities; -public class ProductPriceConfiguration : IEntityTypeConfiguration +public sealed class ProductPriceConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { diff --git a/LiteCharms.Features.MidrandBooks/Products/Models/CreateProductPrice.cs b/LiteCharms.Features.MidrandBooks/Products/Models/CreateProductPrice.cs index 2fec81e..b237f96 100644 --- a/LiteCharms.Features.MidrandBooks/Products/Models/CreateProductPrice.cs +++ b/LiteCharms.Features.MidrandBooks/Products/Models/CreateProductPrice.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.MidrandBooks.Products.Models; -public class CreateProductPrice +public sealed class CreateProductPrice { public long ProductId { get; set; } diff --git a/LiteCharms.Features.MidrandBooks/Products/Models/Records.cs b/LiteCharms.Features.MidrandBooks/Products/Models/Records.cs index 642a739..44786dd 100644 --- a/LiteCharms.Features.MidrandBooks/Products/Models/Records.cs +++ b/LiteCharms.Features.MidrandBooks/Products/Models/Records.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.MidrandBooks.Products.Models; -public record CreateProduct +public sealed record CreateProduct { public required ProductTypes Type { get; set; } diff --git a/LiteCharms.Features.MidrandBooks/Products/ProductService.cs b/LiteCharms.Features.MidrandBooks/Products/ProductService.cs index 0232048..99f1c45 100644 --- a/LiteCharms.Features.MidrandBooks/Products/ProductService.cs +++ b/LiteCharms.Features.MidrandBooks/Products/ProductService.cs @@ -6,7 +6,7 @@ using LiteCharms.Features.Models; namespace LiteCharms.Features.MidrandBooks.Products; -public class ProductService(IDbContextFactory contextFactory) : IService +public sealed class ProductService(IDbContextFactory contextFactory) : IService { public async ValueTask UpdateProductPriceStatusAsync(long productPriceId, bool isEnabled, CancellationToken cancellationToken = default) { diff --git a/LiteCharms.Features.MidrandBooks/appsettings.json b/LiteCharms.Features.MidrandBooks/appsettings.json deleted file mode 100644 index e3261e6..0000000 --- a/LiteCharms.Features.MidrandBooks/appsettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "Email": { - "Credentials": { - "Username": "shop@litecharms.co.za" - }, - "Port": 465, - "Host": "mail.litecharms.co.za", - "UseSsl": true - }, - "Monitoring": { - "ApiKey": "", - "Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889", - "ServiceName": "MidrandBooks" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.Designer.cs b/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.Designer.cs index 82a7a0c..9013654 100644 --- a/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.Designer.cs +++ b/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.Designer.cs @@ -1,5 +1,6 @@ // using System; +using LiteCharms.Features.Models; using LiteCharms.Features.TechShop.Postgres; using LiteCharms.Features.TechShop.Products.Models; using Microsoft.EntityFrameworkCore; diff --git a/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.cs b/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.cs index 64abe3b..06d1891 100644 --- a/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.cs +++ b/LiteCharms.Features.TechShop/Postgres/Migrations/20260520191059_AddedProductMetadata.cs @@ -1,4 +1,5 @@ using System; +using LiteCharms.Features.Models; using LiteCharms.Features.TechShop.Products.Models; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/LiteCharms.Features.TechShop/Postgres/Migrations/ShopDbContextModelSnapshot.cs b/LiteCharms.Features.TechShop/Postgres/Migrations/ShopDbContextModelSnapshot.cs index 7fd9a00..8c1946e 100644 --- a/LiteCharms.Features.TechShop/Postgres/Migrations/ShopDbContextModelSnapshot.cs +++ b/LiteCharms.Features.TechShop/Postgres/Migrations/ShopDbContextModelSnapshot.cs @@ -1,5 +1,6 @@ // using System; +using LiteCharms.Features.Models; using LiteCharms.Features.TechShop.Postgres; using LiteCharms.Features.TechShop.Products.Models; using Microsoft.EntityFrameworkCore; diff --git a/LiteCharms.Features.TechShop/Products/Models/Records.cs b/LiteCharms.Features.TechShop/Products/Models/Records.cs index 22b7dfb..8c7d274 100644 --- a/LiteCharms.Features.TechShop/Products/Models/Records.cs +++ b/LiteCharms.Features.TechShop/Products/Models/Records.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Features.TechShop.Products.Models; +using LiteCharms.Features.Models; + +namespace LiteCharms.Features.TechShop.Products.Models; public record CreateProduct { diff --git a/LiteCharms.Features.TechShop/Products/ProductService.cs b/LiteCharms.Features.TechShop/Products/ProductService.cs index 7c9eb83..c414262 100644 --- a/LiteCharms.Features.TechShop/Products/ProductService.cs +++ b/LiteCharms.Features.TechShop/Products/ProductService.cs @@ -1,4 +1,5 @@ -using LiteCharms.Features.TechShop.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.TechShop.Extensions; using LiteCharms.Features.TechShop.Postgres; using LiteCharms.Features.TechShop.Products.Models; diff --git a/LiteCharms.Features/Abstractions/EventBase.cs b/LiteCharms.Features/Abstractions/EventBase.cs index 32def99..f64d71e 100644 --- a/LiteCharms.Features/Abstractions/EventBase.cs +++ b/LiteCharms.Features/Abstractions/EventBase.cs @@ -7,7 +7,7 @@ public abstract class EventBase { public Guid Id { get; set; } = Guid.CreateVersion7(); - public DateTimeOffset EnqueueAt { get; set; } = SouthAfricanTimeZone.UtcNow(); + public DateTimeOffset EnqueueAt { get; set; } = (DateTimeOffset)SouthAfricanTimeZone.UtcNow(); public string CorrelationId { get; set; } = Guid.CreateVersion7().ToString(); } diff --git a/LiteCharms.Features/Email/Configuration/Account.cs b/LiteCharms.Features/Email/Configuration/Account.cs index e0c65e7..02694d7 100644 --- a/LiteCharms.Features/Email/Configuration/Account.cs +++ b/LiteCharms.Features/Email/Configuration/Account.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Configuration; -public class Account +public sealed class Account { public string? Username { get; set; } diff --git a/LiteCharms.Features/Email/Configuration/SmtpSettings.cs b/LiteCharms.Features/Email/Configuration/SmtpSettings.cs index 78798f2..27ab574 100644 --- a/LiteCharms.Features/Email/Configuration/SmtpSettings.cs +++ b/LiteCharms.Features/Email/Configuration/SmtpSettings.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Configuration; -public class SmtpSettings +public sealed class SmtpSettings { public Account? Credentials { get; set; } diff --git a/LiteCharms.Features/Email/EmailService.cs b/LiteCharms.Features/Email/EmailService.cs index 28c38e0..00d5f63 100644 --- a/LiteCharms.Features/Email/EmailService.cs +++ b/LiteCharms.Features/Email/EmailService.cs @@ -4,7 +4,7 @@ using LiteCharms.Features.Email.Models; namespace LiteCharms.Features.Email; -public class EmailService(IOptions options) : IDisposable +public sealed class EmailService(IOptions options) : IDisposable { private readonly SmtpSettings settings = options.Value; private readonly SmtpClient client = new(); @@ -16,6 +16,7 @@ public class EmailService(IOptions options) : IDisposable public async ValueTask> SendEmailAsync(Message message, CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Send"); + activity?.SetTag("email.recipient", message.Recipient?.Address); try @@ -27,21 +28,7 @@ public class EmailService(IOptions options) : IDisposable return Result.Fail("Smtp service is disconnected."); } - var email = new MimeMessage(); - email.From.Add(new MailboxAddress(message.Sender!.Name, message.Sender.Address!)); - email.To.Add(new MailboxAddress(message.Recipient!.Name, message.Recipient!.Address!)); - email.Subject = message.Subject!; - - var bodyBuilder = new BodyBuilder(); - - if (message.Body!.Properties.HasAttachments) - foreach (var attachment in message.Body?.Attachments!) - bodyBuilder.Attachments.Add(attachment.Name!, attachment.FileStream!, cancellationToken); - - if (!message.Body.Properties.IsHtml) bodyBuilder.TextBody = message.Body.Message; - if (message.Body.Properties.IsHtml) bodyBuilder.HtmlBody = message.Body.Message; - - email.Body = bodyBuilder.ToMessageBody(); + var email = ConstructEmail(message, cancellationToken); var response = await client.SendAsync(email, cancellationToken); @@ -69,21 +56,9 @@ public class EmailService(IOptions options) : IDisposable await DisconnectAsync(cancellationToken); - if (response.Contains("421")) - { - Status = EmailStatuses.TooManyConnections; + var failCheckResult = HandleNegativeResponse(response); - return Result.Fail(response); - } - - if (response.Contains("451")) - { - Status = EmailStatuses.ConnectionAborted; - - return Result.Fail(response); - } - - EmailTelemetry.EmailsFailed.Add(1, new TagList { { "error_message", response } }); + if (failCheckResult.IsFailed) return failCheckResult; Status = EmailStatuses.Disconnected; @@ -100,6 +75,48 @@ public class EmailService(IOptions options) : IDisposable } } + private static MimeMessage ConstructEmail(Message message, CancellationToken cancellationToken) + { + var email = new MimeMessage(); + email.From.Add(new MailboxAddress(message.Sender!.Name, message.Sender.Address!)); + email.To.Add(new MailboxAddress(message.Recipient!.Name, message.Recipient!.Address!)); + email.Subject = message.Subject!; + + var bodyBuilder = new BodyBuilder(); + + if (message.Body!.Properties.HasAttachments) + foreach (var attachment in message.Body?.Attachments!) + bodyBuilder.Attachments.Add(attachment.Name!, attachment.FileStream!, cancellationToken); + + if (!message.Body.Properties.IsHtml) bodyBuilder.TextBody = message.Body.Message; + if (message.Body.Properties.IsHtml) bodyBuilder.HtmlBody = message.Body.Message; + + email.Body = bodyBuilder.ToMessageBody(); + + return email; + } + + private Result HandleNegativeResponse(string response) + { + if (response.Contains("421", StringComparison.Ordinal)) + { + Status = EmailStatuses.TooManyConnections; + + return Result.Fail(response); + } + + if (response.Contains("451", StringComparison.Ordinal)) + { + Status = EmailStatuses.ConnectionAborted; + + return Result.Fail(response); + } + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "error_message", response } }); + + return Result.Fail(response); + } + public async ValueTask> ConnectAsync(CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Connect"); diff --git a/LiteCharms.Features/Email/Models/Attachment.cs b/LiteCharms.Features/Email/Models/Attachment.cs index 6558058..09dbaf8 100644 --- a/LiteCharms.Features/Email/Models/Attachment.cs +++ b/LiteCharms.Features/Email/Models/Attachment.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Models; -public class Attachment +public sealed class Attachment { public string? Name { get; set; } diff --git a/LiteCharms.Features/Email/Models/Body.cs b/LiteCharms.Features/Email/Models/Body.cs index 99156d8..42de5e3 100644 --- a/LiteCharms.Features/Email/Models/Body.cs +++ b/LiteCharms.Features/Email/Models/Body.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Models; -public class Body : IDisposable +public sealed class Body : IDisposable { public string? Message { get; set; } diff --git a/LiteCharms.Features/Email/Models/BodyProperties.cs b/LiteCharms.Features/Email/Models/BodyProperties.cs index 7bdfa01..f3564c3 100644 --- a/LiteCharms.Features/Email/Models/BodyProperties.cs +++ b/LiteCharms.Features/Email/Models/BodyProperties.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Models; -public class BodyProperties +public sealed class BodyProperties { public bool IsHtml { get; set; } diff --git a/LiteCharms.Features/Email/Models/Message.cs b/LiteCharms.Features/Email/Models/Message.cs index 7432545..44d7f3f 100644 --- a/LiteCharms.Features/Email/Models/Message.cs +++ b/LiteCharms.Features/Email/Models/Message.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Models; -public class Message : IDisposable +public sealed class Message : IDisposable { public Party? Sender { get; set; } diff --git a/LiteCharms.Features/Email/Models/Party.cs b/LiteCharms.Features/Email/Models/Party.cs index 65c2e85..6aab9e3 100644 --- a/LiteCharms.Features/Email/Models/Party.cs +++ b/LiteCharms.Features/Email/Models/Party.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Models; -public class Party +public sealed class Party { public string? Name { get; set; } diff --git a/LiteCharms.Features/Email/Models/Response.cs b/LiteCharms.Features/Email/Models/Response.cs index 6bc1c49..5557095 100644 --- a/LiteCharms.Features/Email/Models/Response.cs +++ b/LiteCharms.Features/Email/Models/Response.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Email.Models; -public class Response +public sealed class Response { public int Code { get; set; } diff --git a/LiteCharms.Features/Extensions/Hash.cs b/LiteCharms.Features/Extensions/Hash.cs index d903299..ca24629 100644 --- a/LiteCharms.Features/Extensions/Hash.cs +++ b/LiteCharms.Features/Extensions/Hash.cs @@ -2,12 +2,12 @@ public static class Hash { - public static Func StringToSha256Hash = (input) => + public static readonly Func StringToSha256Hash = (input) => Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input!))); - public static Func StreamToSha256Hash = (stream) => + public static readonly Func StreamToSha256Hash = (stream) => Convert.ToHexString(SHA256.HashData(stream)); - public static Func BytesToSha256Hash = (bytes) => + public static readonly Func BytesToSha256Hash = (bytes) => Convert.ToHexString(SHA256.HashData(bytes)); } diff --git a/LiteCharms.Features/Extensions/Quartz.cs b/LiteCharms.Features/Extensions/Quartz.cs index 6c4c32e..315a973 100644 --- a/LiteCharms.Features/Extensions/Quartz.cs +++ b/LiteCharms.Features/Extensions/Quartz.cs @@ -64,7 +64,7 @@ public static class Quartz config.UseDefaultThreadPool(options => options.MaxConcurrency = 1); config.UseTimeZoneConverter(); - config.SetProperty("quartz.jobStore.misfireThreshold", TimeSpan.FromMinutes(2).TotalMilliseconds.ToString()); + config.SetProperty("quartz.jobStore.misfireThreshold", TimeSpan.FromMinutes(2).TotalMilliseconds.ToString(CultureInfo.InvariantCulture)); config.UsePersistentStore(storage => { diff --git a/LiteCharms.Features/Extensions/Timezones.cs b/LiteCharms.Features/Extensions/Timezones.cs index 3976240..2c2a068 100644 --- a/LiteCharms.Features/Extensions/Timezones.cs +++ b/LiteCharms.Features/Extensions/Timezones.cs @@ -20,7 +20,7 @@ public static class Timezones ? new DateTimeOffset(sourceDateAdjusted.Ticks, SouthAfricanTimeZone.BaseUtcOffset).LocaliseDateTimeOffset(SouthAfricanTimeZone.BaseUtcOffset) : new DateTimeOffset(sourceDateAdjusted.Ticks, timezone!.BaseUtcOffset).LocaliseDateTimeOffset(timezone.BaseUtcOffset); - return DateTimeOffset.Parse(localised!); + return DateTimeOffset.Parse(localised!, CultureInfo.InvariantCulture); } public static DateTime UtcNow(this TimeZoneInfo timezone) => ToDateTimeWithTimeZone(DateTime.Now, timezone).UtcDateTime; diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index fab834b..552ee0a 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -31,6 +31,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -142,6 +146,7 @@ + diff --git a/LiteCharms.Features/Models/DateRange.cs b/LiteCharms.Features/Models/DateRange.cs index c82cba3..a5616b4 100644 --- a/LiteCharms.Features/Models/DateRange.cs +++ b/LiteCharms.Features/Models/DateRange.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Models; -public class DateRange +public sealed class DateRange { public DateOnly From { get; set; } diff --git a/LiteCharms.Features/Models/PageReference.cs b/LiteCharms.Features/Models/PageReference.cs index ab04eb9..12d53cb 100644 --- a/LiteCharms.Features/Models/PageReference.cs +++ b/LiteCharms.Features/Models/PageReference.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Models; -public class PageReference +public sealed class PageReference { public string? Tag { get; set; } diff --git a/LiteCharms.Features/Models/ProductFilter.cs b/LiteCharms.Features/Models/ProductFilter.cs index c6cc275..bfed022 100644 --- a/LiteCharms.Features/Models/ProductFilter.cs +++ b/LiteCharms.Features/Models/ProductFilter.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Models; -public class ProductFilter +public sealed class ProductFilter { public string? Name { get; set; } diff --git a/LiteCharms.Features/Models/ProductMetadata.cs b/LiteCharms.Features/Models/ProductMetadata.cs index ee193fe..d059f36 100644 --- a/LiteCharms.Features/Models/ProductMetadata.cs +++ b/LiteCharms.Features/Models/ProductMetadata.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Models; -public class ProductMetadata +public sealed class ProductMetadata { public string? Manufacturer { get; set; } diff --git a/LiteCharms.Features/Models/SocialMedia.cs b/LiteCharms.Features/Models/SocialMedia.cs index 296d979..b4f10c2 100644 --- a/LiteCharms.Features/Models/SocialMedia.cs +++ b/LiteCharms.Features/Models/SocialMedia.cs @@ -1,7 +1,7 @@  namespace LiteCharms.Features.Models; -public class SocialMedia +public sealed class SocialMedia { public SocialMediaTypes Type { get; set; } diff --git a/LiteCharms.Features/Quartz/JobOrchestrator.cs b/LiteCharms.Features/Quartz/JobOrchestrator.cs index 4929543..fae63c3 100644 --- a/LiteCharms.Features/Quartz/JobOrchestrator.cs +++ b/LiteCharms.Features/Quartz/JobOrchestrator.cs @@ -3,7 +3,7 @@ using LiteCharms.Features.Quartz.Abstractions; namespace LiteCharms.Features.Quartz; -public class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestrator +public sealed class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestrator { public async Task SendAsync(TNotification notification, CancellationToken cancellationToken = default) where TNotification : IEvent @@ -11,7 +11,7 @@ public class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestra var chainedJobGroup = "onetime-jobs"; var scheduler = await schedulerFactory.GetScheduler(cancellationToken); - var jobKey = new JobKey($"{notification.Name.ToLower()}-{notification.CorrelationId.ToLower()}", chainedJobGroup); + var jobKey = new JobKey($"{notification.Name.ToLower(CultureInfo.InvariantCulture)}-{notification.CorrelationId.ToLower(CultureInfo.InvariantCulture)}", chainedJobGroup); var triggerKey = new TriggerKey($"{jobKey.Name}-trigger", chainedJobGroup); var job = JobBuilder.Create>() @@ -35,7 +35,7 @@ public class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestra var chainedJobGroup = "scheduled-jobs"; var scheduler = await schedulerFactory.GetScheduler(cancellationToken); - var jobKey = new JobKey($"{notification.Name.ToLower()}", chainedJobGroup); + var jobKey = new JobKey($"{notification.Name.ToLower(CultureInfo.InvariantCulture)}", chainedJobGroup); var triggerKey = new TriggerKey($"{jobKey.Name}-trigger", chainedJobGroup); var job = JobBuilder.Create>() @@ -53,7 +53,7 @@ public class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestra .WithDescription($"Scheduled via Main Job at {now:g}") .WithCronSchedule(cronExpression, cron => cron .WithMisfireHandlingInstructionIgnoreMisfires()) - .StartAt(now) + .StartAt((DateTimeOffset)now) .Build(); await scheduler.AddJob(job, replace: true, cancellationToken); diff --git a/LiteCharms.Features/Quartz/MediatorJob.cs b/LiteCharms.Features/Quartz/MediatorJob.cs index 9b52972..5fc7649 100644 --- a/LiteCharms.Features/Quartz/MediatorJob.cs +++ b/LiteCharms.Features/Quartz/MediatorJob.cs @@ -4,7 +4,7 @@ using LiteCharms.Features.Mediator; namespace LiteCharms.Features.Quartz; [DisallowConcurrentExecution] -public class MediatorJob(IMediator mediator) : IJob where TNotification : IEvent +public sealed class MediatorJob(IMediator mediator) : IJob where TNotification : IEvent { public async Task Execute(IJobExecutionContext context) { diff --git a/LiteCharms.Features/Quartz/RetryJobListener.cs b/LiteCharms.Features/Quartz/RetryJobListener.cs index 968b8bb..cc12662 100644 --- a/LiteCharms.Features/Quartz/RetryJobListener.cs +++ b/LiteCharms.Features/Quartz/RetryJobListener.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.Quartz; -public class RetryJobListener : IJobListener +public sealed class RetryJobListener : IJobListener { public string Name => "RetryJobListener"; diff --git a/LiteCharms.Features/S3/Abstractions/S3ServiceBase.cs b/LiteCharms.Features/S3/Abstractions/S3ServiceBase.cs index 6679d5d..c1b9ae4 100644 --- a/LiteCharms.Features/S3/Abstractions/S3ServiceBase.cs +++ b/LiteCharms.Features/S3/Abstractions/S3ServiceBase.cs @@ -31,7 +31,7 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3) if(string.IsNullOrWhiteSpace(fileHash)) return Result.Fail("Failed to compute file hash."); - var fileKey = $"{fileHash.ToLower()}{Path.GetExtension(fileName)}"; + var fileKey = $"{fileHash.ToLower(CultureInfo.InvariantCulture)}{Path.GetExtension(fileName)}"; var putRequest = new PutObjectRequest { diff --git a/LiteCharms.Features/S3/BookshopInvoicesS3Service.cs b/LiteCharms.Features/S3/BookshopInvoicesS3Service.cs index 8093591..5f4ddac 100644 --- a/LiteCharms.Features/S3/BookshopInvoicesS3Service.cs +++ b/LiteCharms.Features/S3/BookshopInvoicesS3Service.cs @@ -3,7 +3,7 @@ using static LiteCharms.Features.S3.Constants; namespace LiteCharms.Features.S3; -public class BookshopInvoicesS3Service(IConfiguration configuration, [FromKeyedServices(BookshopInvoicesBucketName)] IAmazonS3 amazonS3) : +public sealed class BookshopInvoicesS3Service(IConfiguration configuration, [FromKeyedServices(BookshopInvoicesBucketName)] IAmazonS3 amazonS3) : S3ServiceBase(amazonS3), IS3Service { protected override string BucketName => configuration.GetSection($"{BookshopInvoicesS3SettingsSection}:BucketName").Value ?? ""; diff --git a/LiteCharms.Features/S3/BookshopQuotesS3Service.cs b/LiteCharms.Features/S3/BookshopQuotesS3Service.cs index 0f87fa0..2362d66 100644 --- a/LiteCharms.Features/S3/BookshopQuotesS3Service.cs +++ b/LiteCharms.Features/S3/BookshopQuotesS3Service.cs @@ -3,7 +3,7 @@ using static LiteCharms.Features.S3.Constants; namespace LiteCharms.Features.S3; -public class BookshopQuotesS3Service(IConfiguration configuration, [FromKeyedServices(BookshopQuotesBucketName)] IAmazonS3 amazonS3) : +public sealed class BookshopQuotesS3Service(IConfiguration configuration, [FromKeyedServices(BookshopQuotesBucketName)] IAmazonS3 amazonS3) : S3ServiceBase(amazonS3), IS3Service { protected override string BucketName => configuration.GetSection($"{BookshopQuotesS3SettingsSection}:BucketName").Value ?? ""; diff --git a/LiteCharms.Features/S3/BookshopS3Service.cs b/LiteCharms.Features/S3/BookshopS3Service.cs index aff9cf5..024b829 100644 --- a/LiteCharms.Features/S3/BookshopS3Service.cs +++ b/LiteCharms.Features/S3/BookshopS3Service.cs @@ -3,7 +3,7 @@ using static LiteCharms.Features.S3.Constants; namespace LiteCharms.Features.S3; -public class BookshopS3Service(IConfiguration configuration, [FromKeyedServices(BookshopBucketName)] IAmazonS3 amazonS3) : +public sealed class BookshopS3Service(IConfiguration configuration, [FromKeyedServices(BookshopBucketName)] IAmazonS3 amazonS3) : S3ServiceBase(amazonS3), IS3Service { protected override string BucketName => configuration.GetSection($"{BookshopS3SettingsSection}:BucketName").Value ?? ""; diff --git a/LiteCharms.Features/S3/Configuration/S3Settings.cs b/LiteCharms.Features/S3/Configuration/S3Settings.cs index 6be7460..72984fa 100644 --- a/LiteCharms.Features/S3/Configuration/S3Settings.cs +++ b/LiteCharms.Features/S3/Configuration/S3Settings.cs @@ -1,6 +1,6 @@ namespace LiteCharms.Features.S3.Configuration; -public class S3Settings +public sealed class S3Settings { public string? ServiceUrl { get; set; } diff --git a/LiteCharms.Features/ServiceBus/EmailServiceBus.cs b/LiteCharms.Features/ServiceBus/EmailServiceBus.cs index c79438e..f83ed8e 100644 --- a/LiteCharms.Features/ServiceBus/EmailServiceBus.cs +++ b/LiteCharms.Features/ServiceBus/EmailServiceBus.cs @@ -4,7 +4,7 @@ using LiteCharms.Features.ServiceBus.Queues; namespace LiteCharms.Features.ServiceBus; -public class EmailServiceBus(EmailQueue messages) : IEventBus +public sealed class EmailServiceBus(EmailQueue messages) : IEventBus { public async Task PublishAsync(TEvent notification, CancellationToken cancellationToken = default) where TEvent : class, IEvent diff --git a/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs b/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs index 5ccae66..5a74145 100644 --- a/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs +++ b/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs @@ -3,7 +3,7 @@ using LiteCharms.Features.ServiceBus.Queues; namespace LiteCharms.Features.ServiceBus.Exchanges; -public class EmailExchange(EmailQueue messages, ILogger logger, IPublisher mediator) : BackgroundService +public sealed class EmailExchange(EmailQueue messages, ILogger logger, IPublisher mediator) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { diff --git a/LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs b/LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs index c94fb5d..32d84f1 100644 --- a/LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs +++ b/LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.ServiceBus.Exchanges; -public class GeneralExchange(GeneralQueue messages) : BackgroundService +public sealed class GeneralExchange(GeneralQueue messages) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { diff --git a/LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs b/LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs index 645ab49..3705993 100644 --- a/LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs +++ b/LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.ServiceBus.Exchanges; -public class SalesExchange(SalesQueue messages) : BackgroundService +public sealed class SalesExchange(SalesQueue messages) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { diff --git a/LiteCharms.Features/ServiceBus/GeneralServiceBus.cs b/LiteCharms.Features/ServiceBus/GeneralServiceBus.cs index 94edb37..0694c5c 100644 --- a/LiteCharms.Features/ServiceBus/GeneralServiceBus.cs +++ b/LiteCharms.Features/ServiceBus/GeneralServiceBus.cs @@ -4,7 +4,7 @@ using LiteCharms.Features.ServiceBus.Queues; namespace LiteCharms.Features.ServiceBus; -public class GeneralServiceBus(GeneralQueue messages) : IEventBus +public sealed class GeneralServiceBus(GeneralQueue messages) : IEventBus { public async Task PublishAsync(TEvent notification, CancellationToken cancellationToken = default) where TEvent : class, IEvent diff --git a/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs b/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs index 508ad5f..700a1f1 100644 --- a/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs +++ b/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs @@ -2,4 +2,4 @@ namespace LiteCharms.Features.ServiceBus.Queues; -public class EmailQueue : EventBusQueueBase, IEventBusQueue; +public sealed class EmailQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs b/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs index 3d79a2f..ef155ec 100644 --- a/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs +++ b/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs @@ -2,4 +2,4 @@ namespace LiteCharms.Features.ServiceBus.Queues; -public class GeneralQueue : EventBusQueueBase, IEventBusQueue; +public sealed class GeneralQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs b/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs index 8dc5601..bb76de8 100644 --- a/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs +++ b/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs @@ -2,4 +2,4 @@ namespace LiteCharms.Features.ServiceBus.Queues; -public class SalesQueue : EventBusQueueBase, IEventBusQueue; +public sealed class SalesQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Features/ServiceBus/SalesServiceBus.cs b/LiteCharms.Features/ServiceBus/SalesServiceBus.cs index 853657b..d30050c 100644 --- a/LiteCharms.Features/ServiceBus/SalesServiceBus.cs +++ b/LiteCharms.Features/ServiceBus/SalesServiceBus.cs @@ -4,7 +4,7 @@ using LiteCharms.Features.ServiceBus.Queues; namespace LiteCharms.Features.ServiceBus; -public class SalesServiceBus(SalesQueue messages) : IEventBus +public sealed class SalesServiceBus(SalesQueue messages) : IEventBus { public async Task PublishAsync(TEvent notification, CancellationToken cancellationToken = default) where TEvent : class, IEvent diff --git a/LiteCharmsShared.slnx b/LiteCharmsShared.slnx index 7b1a9f7..1945799 100644 --- a/LiteCharmsShared.slnx +++ b/LiteCharmsShared.slnx @@ -1,6 +1,7 @@ +