Compare commits
14
Commits
42001998d6
..
1.17.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f6d0ccaa0 | ||
|
|
1acbc4d213 | ||
|
|
8c99668fac | ||
|
|
ad44f46204 | ||
|
|
49d999c1e3 | ||
|
|
9ed4777a18 | ||
|
|
0cf44f68cc | ||
|
|
41ed5a4288 | ||
|
|
bbcba5e06c | ||
|
|
502cc326dd | ||
|
|
4675d4c5fc | ||
|
|
f80bb2fff9 | ||
|
|
6767906b0d | ||
|
|
a344af4498 |
@@ -1,9 +1,11 @@
|
|||||||
namespace LiteCharms.Features.ServiceBus;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
public const int QueueBounds = 100000;
|
public const int QueueBounds = 100000;
|
||||||
|
|
||||||
|
public const string ShopSchedulerName = "shop";
|
||||||
|
|
||||||
public const string EmailServiceBus = nameof(EmailServiceBus);
|
public const string EmailServiceBus = nameof(EmailServiceBus);
|
||||||
public const string GeneralServiceBus = nameof(GeneralServiceBus);
|
public const string GeneralServiceBus = nameof(GeneralServiceBus);
|
||||||
public const string SalesServiceBus = nameof(SalesServiceBus);
|
public const string SalesServiceBus = nameof(SalesServiceBus);
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
namespace LiteCharms.Features.ServiceBus.Abstractions;
|
|
||||||
|
|
||||||
public abstract class EventBusQueueBase
|
public abstract class EventBusQueueBase
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Abstractions;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
public interface IEvent : INotification
|
public interface IEvent : INotification
|
||||||
{
|
{
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
namespace LiteCharms.Features.ServiceBus.Abstractions;
|
|
||||||
|
|
||||||
public interface IEventBus
|
public interface IEventBus
|
||||||
{
|
{
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
namespace LiteCharms.Features.ServiceBus.Abstractions;
|
|
||||||
|
|
||||||
public interface IEventBusQueue
|
public interface IEventBusQueue
|
||||||
{
|
{
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Quartz.Abstractions;
|
|
||||||
|
|
||||||
public interface IJobOrchestrator
|
public interface IJobOrchestrator
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<SignAssembly>True</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Nuget Package Details -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<PackageId>LiteCharms.Abstractions</PackageId>
|
||||||
|
<Version>1.0.20</Version>
|
||||||
|
<Authors>Khwezi Mngoma</Authors>
|
||||||
|
<Company>Lite Charms (PTY) Ltd</Company>
|
||||||
|
<Description>Shared abstractions for Lite Charms applications.</Description>
|
||||||
|
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
<PackageTags>utility;dotnet</PackageTags>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
|
||||||
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentResults" Version="4.0.0" />
|
||||||
|
<PackageReference Include="Mediator.Abstractions" Version="3.0.2" />
|
||||||
|
|
||||||
|
<Using Include="Mediator" />
|
||||||
|
<Using Include="FluentResults" />
|
||||||
|
<Using Include="System.Threading.Channels" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Abstractions;
|
||||||
|
|
||||||
public static class Timezones
|
public static class Timezones
|
||||||
{
|
{
|
||||||
+8
-3
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Shop.Customers.Entities;
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
||||||
{
|
{
|
||||||
@@ -7,8 +7,8 @@ public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
|||||||
builder.ToTable(nameof(Customer));
|
builder.ToTable(nameof(Customer));
|
||||||
|
|
||||||
builder.HasKey(f => f.Id);
|
builder.HasKey(f => f.Id);
|
||||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd();
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate();
|
||||||
builder.Property(f => f.Company);
|
builder.Property(f => f.Company);
|
||||||
builder.Property(f => f.Name).IsRequired();
|
builder.Property(f => f.Name).IsRequired();
|
||||||
builder.Property(f => f.LastName).IsRequired();
|
builder.Property(f => f.LastName).IsRequired();
|
||||||
@@ -26,5 +26,10 @@ public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
|||||||
builder.Property(f => f.Country);
|
builder.Property(f => f.Country);
|
||||||
builder.Property(f => f.PostalCode);
|
builder.Property(f => f.PostalCode);
|
||||||
builder.Property(f => f.Active).HasDefaultValue(true);
|
builder.Property(f => f.Active).HasDefaultValue(true);
|
||||||
|
|
||||||
|
builder.HasMany(f => f.Leads)
|
||||||
|
.WithOne(f => f.Customer)
|
||||||
|
.HasForeignKey(f => f.CustomerId)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Shop.Leads.Entities;
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
public class LeadConfiguration : IEntityTypeConfiguration<Lead>
|
public class LeadConfiguration : IEntityTypeConfiguration<Lead>
|
||||||
{
|
{
|
||||||
@@ -7,8 +7,8 @@ public class LeadConfiguration : IEntityTypeConfiguration<Lead>
|
|||||||
builder.ToTable(nameof(Lead));
|
builder.ToTable(nameof(Lead));
|
||||||
|
|
||||||
builder.HasKey(f => f.Id);
|
builder.HasKey(f => f.Id);
|
||||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd();
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate();
|
||||||
builder.Property(f => f.CustomerId).IsRequired(false);
|
builder.Property(f => f.CustomerId).IsRequired(false);
|
||||||
builder.Property(f => f.Source);
|
builder.Property(f => f.Source);
|
||||||
builder.Property(f => f.ClickId);
|
builder.Property(f => f.ClickId);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
public class NotificationConfiguration : IEntityTypeConfiguration<Notification>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Notification> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable(nameof(Notification));
|
||||||
|
|
||||||
|
builder.HasKey(f => f.Id);
|
||||||
|
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd();
|
||||||
|
builder.Property(f => f.Direction).IsRequired();
|
||||||
|
builder.Property(f => f.Author).IsRequired();
|
||||||
|
builder.Property(f => f.Title).IsRequired();
|
||||||
|
builder.Property(f => f.Description).IsRequired();
|
||||||
|
builder.Property(f => f.Platform).IsRequired();
|
||||||
|
builder.Property(f => f.PlatformAddress).IsRequired();
|
||||||
|
builder.Property(f => f.CorrelationId).IsRequired();
|
||||||
|
builder.Property(f => f.CorrelationIdType).IsRequired();
|
||||||
|
builder.Property(f => f.IsInternal).HasDefaultValue(true);
|
||||||
|
builder.Property(f => f.Processed).HasDefaultValue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Order> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable(nameof(Order));
|
||||||
|
|
||||||
|
builder.HasKey(f => f.Id);
|
||||||
|
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd();
|
||||||
|
builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate();
|
||||||
|
builder.Property(f => f.CustomerId).IsRequired();
|
||||||
|
builder.Property(f => f.QuoteId).IsRequired(false);
|
||||||
|
builder.Property(f => f.RefundId).IsRequired(false);
|
||||||
|
builder.Property(f => f.ShoppingCartId).IsRequired();
|
||||||
|
builder.Property(f => f.Status).HasConversion<int>().IsRequired();
|
||||||
|
builder.Property(f => f.Notes).HasColumnType("jsonb").IsRequired(false);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Quote)
|
||||||
|
.WithOne(f => f.Order)
|
||||||
|
.HasForeignKey<Order>(f => f.QuoteId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Customer)
|
||||||
|
.WithMany(f => f.Orders)
|
||||||
|
.HasForeignKey(f => f.CustomerId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-7
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
public class OrderRefundConfiguration : IEntityTypeConfiguration<OrderRefund>
|
public class OrderRefundConfiguration : IEntityTypeConfiguration<OrderRefund>
|
||||||
{
|
{
|
||||||
@@ -7,15 +7,13 @@ public class OrderRefundConfiguration : IEntityTypeConfiguration<OrderRefund>
|
|||||||
builder.ToTable(nameof(OrderRefund));
|
builder.ToTable(nameof(OrderRefund));
|
||||||
|
|
||||||
builder.HasKey(f => f.Id);
|
builder.HasKey(f => f.Id);
|
||||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd();
|
||||||
builder.Property(f => f.OrderId).IsRequired();
|
builder.Property(f => f.OrderId).IsRequired();
|
||||||
builder.Property(f => f.Reason).IsRequired();
|
builder.Property(f => f.Reason).IsRequired();
|
||||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
||||||
|
|
||||||
builder.HasOne(r => r.Order)
|
builder.HasOne(f => f.Order)
|
||||||
.WithMany(o => o.Refunds)
|
.WithOne(o => o.Refund)
|
||||||
.HasForeignKey(r => r.OrderId)
|
.HasForeignKey<OrderRefund>(o => o.OrderId);
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
public class ProductConfiguration : IEntityTypeConfiguration<Product>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Product> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable(nameof(Product));
|
||||||
|
|
||||||
|
builder.HasKey(f => f.Id);
|
||||||
|
builder.Property(f => f.Name).IsRequired();
|
||||||
|
builder.Property(f => f.Description).IsRequired();
|
||||||
|
builder.Property(f => f.Active).HasDefaultValue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-6
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
public class ProductPriceConfiguration : IEntityTypeConfiguration<ProductPrice>
|
public class ProductPriceConfiguration : IEntityTypeConfiguration<ProductPrice>
|
||||||
{
|
{
|
||||||
@@ -7,17 +7,16 @@ public class ProductPriceConfiguration : IEntityTypeConfiguration<ProductPrice>
|
|||||||
builder.ToTable(nameof(ProductPrice));
|
builder.ToTable(nameof(ProductPrice));
|
||||||
|
|
||||||
builder.HasKey(f => f.Id);
|
builder.HasKey(f => f.Id);
|
||||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd();
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate();
|
||||||
builder.Property(f => f.ProductId).IsRequired();
|
builder.Property(f => f.ProductId).IsRequired();
|
||||||
builder.Property(f => f.Price).IsRequired().HasPrecision(18, 2);
|
builder.Property(f => f.Price).IsRequired().HasPrecision(18, 2);
|
||||||
builder.Property(f => f.Discount).HasPrecision(18, 2);
|
builder.Property(f => f.Discount).HasPrecision(18, 2);
|
||||||
builder.Property(f => f.Active);
|
builder.Property(f => f.Active);
|
||||||
|
|
||||||
builder.HasOne(f => f.Product)
|
builder.HasOne(f => f.Product)
|
||||||
.WithMany(f => f.ProductPrices)
|
.WithMany(p => p.ProductPrices)
|
||||||
.HasForeignKey(pp => pp.ProductId)
|
.HasForeignKey(f => f.ProductId)
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
public class QuoteConfiguration : IEntityTypeConfiguration<Quote>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Quote> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable(nameof(Quote));
|
||||||
|
|
||||||
|
builder.HasKey(f => f.Id);
|
||||||
|
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd();
|
||||||
|
builder.Property(f => f.UpdatedAt).IsRequired().ValueGeneratedOnAddOrUpdate();
|
||||||
|
builder.Property(f => f.ExpiredAt).IsRequired(false);
|
||||||
|
builder.Property(f => f.CustomerId).IsRequired();
|
||||||
|
builder.Property(f => f.Status).IsRequired();
|
||||||
|
builder.Property(f => f.ShoppingCartId).IsRequired();
|
||||||
|
builder.Property(f => f.Reason).IsRequired(false);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Customer)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(f => f.CustomerId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
public class ShoppingCartConfiguration : IEntityTypeConfiguration<ShoppingCart>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<ShoppingCart> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable(nameof(ShoppingCart));
|
||||||
|
|
||||||
|
builder.HasKey(f => f.Id);
|
||||||
|
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd();
|
||||||
|
builder.Property(f => f.UpdatedAt).IsRequired().ValueGeneratedOnAddOrUpdate();
|
||||||
|
builder.Property(f => f.CustomerId).IsRequired(false);
|
||||||
|
builder.Property(f => f.OrderId).IsRequired(false);
|
||||||
|
builder.Property(f => f.QuoteId).IsRequired(false);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Customer)
|
||||||
|
.WithMany(c => c.ShoppingCarts)
|
||||||
|
.HasForeignKey(f => f.CustomerId)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Order)
|
||||||
|
.WithOne(o => o.ShoppingCart)
|
||||||
|
.HasForeignKey<Order>(o => o.ShoppingCartId)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Quote)
|
||||||
|
.WithOne(o => o.ShoppingCart)
|
||||||
|
.HasForeignKey<Quote>(o => o.ShoppingCartId)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
namespace LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
public class ShoppingCartItemConfiguration : IEntityTypeConfiguration<ShoppingCartItem>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<ShoppingCartItem> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable(nameof(ShoppingCartItem));
|
||||||
|
|
||||||
|
builder.HasKey(f => f.Id);
|
||||||
|
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd();
|
||||||
|
builder.Property(f => f.UpdatedAt).IsRequired().ValueGeneratedOnAddOrUpdate();
|
||||||
|
builder.Property(f => f.Quantity).IsRequired().HasDefaultValue(1);
|
||||||
|
builder.Property(f => f.ProductPriceId).IsRequired();
|
||||||
|
|
||||||
|
builder.HasOne(f => f.ProductPrice)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(f => f.ProductPriceId)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.ShoppingCart)
|
||||||
|
.WithMany(f => f.ShoppingCartItems)
|
||||||
|
.HasForeignKey(f => f.ShoppingCartId)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-5
@@ -1,9 +1,6 @@
|
|||||||
using LiteCharms.Features.Shop.Leads.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
using LiteCharms.Features.Shop.Orders.Entities;
|
|
||||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
|
||||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Shop.Customers.Entities;
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<CustomerConfiguration, Customer>]
|
[EntityTypeConfiguration<CustomerConfiguration, Customer>]
|
||||||
public class Customer : Models.Customer
|
public class Customer : Models.Customer
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Features.Shop.Customers.Models;
|
using LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Shop.Leads.Entities;
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<LeadConfiguration, Lead>]
|
[EntityTypeConfiguration<LeadConfiguration, Lead>]
|
||||||
public class Lead : Models.Lead
|
public class Lead : Models.Lead
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<SignAssembly>True</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Nuget Package Details -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<PackageId>LiteCharms.Entities</PackageId>
|
||||||
|
<Version>1.0.20</Version>
|
||||||
|
<Authors>Khwezi Mngoma</Authors>
|
||||||
|
<Company>Lite Charms (PTY) Ltd</Company>
|
||||||
|
<Description>Shared entities for Lite Charms applications.</Description>
|
||||||
|
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
<PackageTags>utility;dotnet</PackageTags>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE" Pack="true" PackagePath="\"/>
|
||||||
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Database -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.7" />
|
||||||
|
|
||||||
|
<!-- Global Usings -->
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LiteCharms.Models\LiteCharms.Models.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
namespace LiteCharms.Features.Shop.Notifications.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<NotificationConfiguration, Notification>]
|
[EntityTypeConfiguration<NotificationConfiguration, Notification>]
|
||||||
public class Notification : Models.Notification;
|
public class Notification : Models.Notification;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
|
[EntityTypeConfiguration<OrderConfiguration, Order>]
|
||||||
|
public class Order : Models.Order
|
||||||
|
{
|
||||||
|
public virtual OrderRefund? Refund { get; set; }
|
||||||
|
|
||||||
|
public virtual Customer? Customer { get; set; }
|
||||||
|
|
||||||
|
public virtual Quote? Quote { get; set; }
|
||||||
|
|
||||||
|
public virtual ShoppingCart? ShoppingCart { get; set; }
|
||||||
|
}
|
||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<OrderRefundConfiguration, OrderRefund>]
|
[EntityTypeConfiguration<OrderRefundConfiguration, OrderRefund>]
|
||||||
public class OrderRefund : Models.OrderRefund
|
public class OrderRefund : Models.OrderRefund
|
||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<ProductConfiguration, Product>]
|
[EntityTypeConfiguration<ProductConfiguration, Product>]
|
||||||
public class Product : Models.Product
|
public class Product : Models.Product
|
||||||
+3
-1
@@ -1,4 +1,6 @@
|
|||||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
|
|
||||||
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<ProductPriceConfiguration, ProductPrice>]
|
[EntityTypeConfiguration<ProductPriceConfiguration, ProductPrice>]
|
||||||
public class ProductPrice : Models.ProductPrice
|
public class ProductPrice : Models.ProductPrice
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
using LiteCharms.Features.Shop.Customers.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
using LiteCharms.Features.Shop.Orders.Entities;
|
|
||||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Shop.Quotes.Entities;
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<QuoteConfiguration, Quote>]
|
[EntityTypeConfiguration<QuoteConfiguration, Quote>]
|
||||||
public class Quote : Models.Quote
|
public class Quote : Models.Quote
|
||||||
{
|
{
|
||||||
public virtual Customer? Customer { get; set; }
|
public virtual Customer? Customer { get; set; }
|
||||||
|
|
||||||
public virtual Order? Order { get; set; }
|
|
||||||
|
|
||||||
public virtual ShoppingCart? ShoppingCart { get; set; }
|
public virtual ShoppingCart? ShoppingCart { get; set; }
|
||||||
|
|
||||||
|
public virtual Order? Order { get; set; }
|
||||||
}
|
}
|
||||||
+2
-6
@@ -1,8 +1,6 @@
|
|||||||
using LiteCharms.Features.Shop.Customers.Entities;
|
using LiteCharms.Entities.Configuration;
|
||||||
using LiteCharms.Features.Shop.Orders.Entities;
|
|
||||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
[EntityTypeConfiguration<ShoppingCartConfiguration, ShoppingCart>]
|
[EntityTypeConfiguration<ShoppingCartConfiguration, ShoppingCart>]
|
||||||
public class ShoppingCart : Models.ShoppingCart
|
public class ShoppingCart : Models.ShoppingCart
|
||||||
@@ -14,6 +12,4 @@ public class ShoppingCart : Models.ShoppingCart
|
|||||||
public virtual Quote? Quote { get; set; }
|
public virtual Quote? Quote { get; set; }
|
||||||
|
|
||||||
public virtual ICollection<ShoppingCartItem>? ShoppingCartItems { get; set; }
|
public virtual ICollection<ShoppingCartItem>? ShoppingCartItems { get; set; }
|
||||||
|
|
||||||
public virtual ICollection<ShoppingCartPackage>? ShoppingCartPackages { get; set; }
|
|
||||||
}
|
}
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Products.Entities;
|
namespace LiteCharms.Entities;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
|
||||||
|
|
||||||
public class ShoppingCartItem : Models.ShoppingCartItem
|
public class ShoppingCartItem : Models.ShoppingCartItem
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using LiteCharms.Models.Configuraton.Email;
|
||||||
|
|
||||||
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
|
public static class Email
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-74
@@ -1,49 +1,10 @@
|
|||||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
using LiteCharms.Models;
|
||||||
using LiteCharms.Features.Shop.Customers.Models;
|
|
||||||
using LiteCharms.Features.Shop.Leads.Models;
|
|
||||||
using LiteCharms.Features.Shop.Notifications.Models;
|
|
||||||
using LiteCharms.Features.Shop.Orders.Models;
|
|
||||||
using LiteCharms.Features.Shop.Products.Models;
|
|
||||||
using LiteCharms.Features.Shop.Quotes.Models;
|
|
||||||
using LiteCharms.Features.Shop.ShoppingCarts.Models;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
public static class EntityModeMappers
|
public static class EntityModeMappers
|
||||||
{
|
{
|
||||||
public static ShoppingCartPackage ToModel(this Shop.ShoppingCarts.Entities.ShoppingCartPackage entity) =>
|
public static ShoppingCartItem ToModel(this Entities.ShoppingCartItem entity) =>
|
||||||
new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
PackageId = entity.PackageId,
|
|
||||||
ShoppingCartId = entity.ShoppingCartId
|
|
||||||
};
|
|
||||||
|
|
||||||
public static PackageItem ToModel(this Shop.CartPackages.Entities.PackageItem entity) =>
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Active = entity.Active,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
PackageId = entity.PackageId,
|
|
||||||
ProductPriceId = entity.ProductPriceId
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Package ToModel(this Shop.CartPackages.Entities.Package entity) =>
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
Active = entity.Active,
|
|
||||||
Description = entity.Description,
|
|
||||||
Name = entity.Name,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
ImageUrl = entity.ImageUrl,
|
|
||||||
Summary = entity.Summary
|
|
||||||
};
|
|
||||||
|
|
||||||
public static ShoppingCartItem ToModel(this Shop.ShoppingCarts.Entities.ShoppingCartItem entity) =>
|
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -54,17 +15,18 @@ public static class EntityModeMappers
|
|||||||
ShoppingCartId = entity.ShoppingCartId
|
ShoppingCartId = entity.ShoppingCartId
|
||||||
};
|
};
|
||||||
|
|
||||||
public static ShoppingCart ToModel(this Shop.ShoppingCarts.Entities.ShoppingCart entity) =>
|
public static ShoppingCart ToModel(this Entities.ShoppingCart entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
CreatedAt = entity.CreatedAt,
|
CreatedAt = entity.CreatedAt,
|
||||||
UpdatedAt = entity.UpdatedAt,
|
UpdatedAt = entity.UpdatedAt,
|
||||||
CustomerId = entity.CustomerId,
|
CustomerId = entity.CustomerId,
|
||||||
OrderId = entity.OrderId
|
OrderId = entity.OrderId,
|
||||||
|
QuoteId = entity.QuoteId
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Quote ToModel(this Shop.Quotes.Entities.Quote entity) =>
|
public static Quote ToModel(this Entities.Quote entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -74,36 +36,27 @@ public static class EntityModeMappers
|
|||||||
ExpiredAt = entity.ExpiredAt,
|
ExpiredAt = entity.ExpiredAt,
|
||||||
Reason = entity.Reason,
|
Reason = entity.Reason,
|
||||||
ShoppingCartId = entity.ShoppingCartId,
|
ShoppingCartId = entity.ShoppingCartId,
|
||||||
Status = entity.Status,
|
Status = entity.Status
|
||||||
InvoiceUrl = entity.InvoiceUrl,
|
|
||||||
OrderId = entity.OrderId
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Notification ToModel(this Shop.Notifications.Entities.Notification entity) =>
|
public static Notification ToModel(this Entities.Notification entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
CreatedAt = entity.CreatedAt,
|
CreatedAt = entity.CreatedAt,
|
||||||
Message = entity.Message,
|
Description = entity.Description,
|
||||||
Direction = entity.Direction,
|
Direction = entity.Direction,
|
||||||
CorrelationId = entity.CorrelationId,
|
CorrelationId = entity.CorrelationId,
|
||||||
CorrelationIdType = entity.CorrelationIdType,
|
CorrelationIdType = entity.CorrelationIdType,
|
||||||
IsInternal = entity.IsInternal,
|
IsInternal = entity.IsInternal,
|
||||||
Sender = entity.Sender,
|
Author = entity.Author,
|
||||||
Platform = entity.Platform,
|
Platform = entity.Platform,
|
||||||
Recipient = entity.Recipient,
|
PlatformAddress = entity.PlatformAddress,
|
||||||
Subject = entity.Subject,
|
Title = entity.Title,
|
||||||
Processed = entity.Processed,
|
Processed = entity.Processed
|
||||||
SenderName = entity.SenderName,
|
|
||||||
RecipientAddress = entity.RecipientAddress,
|
|
||||||
Priority = entity.Priority,
|
|
||||||
UpdatedAt = entity?.UpdatedAt,
|
|
||||||
IsHtml = entity!.IsHtml,
|
|
||||||
HasError = entity.HasError,
|
|
||||||
Errors = entity.Errors
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Customer ToModel(this Shop.Customers.Entities.Customer entity) =>
|
public static Customer ToModel(this Entities.Customer entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -128,7 +81,7 @@ public static class EntityModeMappers
|
|||||||
Whatsapp = entity.Whatsapp
|
Whatsapp = entity.Whatsapp
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Lead ToModel(this Shop.Leads.Entities.Lead entity) =>
|
public static Lead ToModel(this Entities.Lead entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -149,7 +102,7 @@ public static class EntityModeMappers
|
|||||||
Status = entity.Status
|
Status = entity.Status
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Order ToModel(this Shop.Orders.Entities.Order entity) =>
|
public static Order ToModel(this Entities.Order entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -157,13 +110,13 @@ public static class EntityModeMappers
|
|||||||
UpdatedAt = entity.UpdatedAt,
|
UpdatedAt = entity.UpdatedAt,
|
||||||
CustomerId = entity.CustomerId,
|
CustomerId = entity.CustomerId,
|
||||||
Notes = entity.Notes,
|
Notes = entity.Notes,
|
||||||
|
RefundId = entity.RefundId,
|
||||||
|
QuoteId = entity.QuoteId,
|
||||||
Status = entity.Status,
|
Status = entity.Status,
|
||||||
Requirements = entity.Requirements,
|
ShoppingCartId = entity.ShoppingCartId
|
||||||
Terms = entity.Terms,
|
|
||||||
InvoiceUrl = entity.InvoiceUrl
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static OrderRefund ToModel(this Shop.Orders.Entities.OrderRefund entity) =>
|
public static OrderRefund ToModel(this Entities.OrderRefund entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -173,19 +126,16 @@ public static class EntityModeMappers
|
|||||||
Amount = entity.Amount
|
Amount = entity.Amount
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Product ToModel(this Shop.Products.Entities.Product entity) =>
|
public static Product ToModel(this Entities.Product entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
Description = entity.Description,
|
Description = entity.Description,
|
||||||
Active = entity.Active,
|
Active = entity.Active
|
||||||
Summary = entity.Summary,
|
|
||||||
ImageUrl = entity.ImageUrl,
|
|
||||||
Thumbnails = entity.Thumbnails
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static ProductPrice ToModel(this Shop.Products.Entities.ProductPrice entity) =>
|
public static ProductPrice ToModel(this Entities.ProductPrice entity) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Features.HealthChecks;
|
using LiteCharms.Infrastructure.HealthChecks;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
public static class HealthChecks
|
public static class HealthChecks
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<SignAssembly>True</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Warnings And Exclusions -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);MA0004</NoWarn>
|
||||||
|
<!-- https://github.com/dotnet/aspnetcore/issues/50836 -->
|
||||||
|
<NoWarn>$(NoWarn);AD0001</NoWarn>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<NoWarn>$(NoWarn);IL2080;IL2065;IL2075;IL2087;IL2057;IL2060;IL2070;IL2067;IL2072;IL2026;IL2104</NoWarn>
|
||||||
|
<NoWarn>$(NoWarn);IL2110;IL2111</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Nuget Package Details -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<PackageId>LiteCharms.Extensions</PackageId>
|
||||||
|
<Version>1.0.20</Version>
|
||||||
|
<Authors>Khwezi Mngoma</Authors>
|
||||||
|
<Company>Lite Charms (PTY) Ltd</Company>
|
||||||
|
<Description>Extension components for Lite Charms applications.</Description>
|
||||||
|
<PackageProjectUrl>https://gitea.khongisa.co.za/litecharms/components</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://gitea.khongisa.co.za/litecharms/components.git</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
<PackageTags>utility;dotnet</PackageTags>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
|
||||||
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Health Checks -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Core" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Data" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.7" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
|
||||||
|
<PackageReference Include="Quartz.AspNetCore" Version="3.18.1" />
|
||||||
|
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.18.1" />
|
||||||
|
|
||||||
|
<!-- Global Usings -->
|
||||||
|
<Using Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Open Telemetry -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
|
||||||
|
|
||||||
|
<!-- Global Usings -->
|
||||||
|
<Using Include="OpenTelemetry.Resources" />
|
||||||
|
<Using Include="OpenTelemetry.Exporter" />
|
||||||
|
<Using Include="OpenTelemetry.Logs" />
|
||||||
|
<Using Include="OpenTelemetry.Metrics" />
|
||||||
|
<Using Include="OpenTelemetry.Trace" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Database -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.7" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.7">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
||||||
|
|
||||||
|
<!-- Global Usings -->
|
||||||
|
<Using Include="Npgsql" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore.Design" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Shared Usings -->
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Quartz" />
|
||||||
|
<Using Include="Microsoft.AspNetCore.Builder" />
|
||||||
|
<Using Include="Microsoft.Extensions.Configuration" />
|
||||||
|
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<Using Include="Microsoft.Extensions.Logging" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Project References -->
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LiteCharms.Entities\LiteCharms.Entities.csproj" />
|
||||||
|
<ProjectReference Include="..\LiteCharms.Infrastructure\LiteCharms.Infrastructure.csproj" />
|
||||||
|
<ProjectReference Include="..\LiteCharms.Models\LiteCharms.Models.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
public static class Monitoring
|
public static class Monitoring
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
public static class Postgres
|
public static class Postgres
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using LiteCharms.Features.Quartz;
|
using LiteCharms.Abstractions;
|
||||||
using LiteCharms.Features.Quartz.Abstractions;
|
using LiteCharms.Infrastructure.Quartz;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
public static class Quartz
|
public static class Quartz
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using LiteCharms.Features.ServiceBus;
|
using LiteCharms.Abstractions;
|
||||||
using LiteCharms.Features.ServiceBus.Abstractions;
|
using LiteCharms.Infrastructure.ServiceBus;
|
||||||
using LiteCharms.Features.ServiceBus.Exchanges;
|
using LiteCharms.Infrastructure.ServiceBus.Exchanges;
|
||||||
using LiteCharms.Features.ServiceBus.Queues;
|
using LiteCharms.Infrastructure.ServiceBus.Queues;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Extensions;
|
namespace LiteCharms.Extensions;
|
||||||
|
|
||||||
public static class ServiceBus
|
public static class ServiceBus
|
||||||
{
|
{
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
using LiteCharms.Features.Extensions;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Tests;
|
|
||||||
|
|
||||||
public class CommonFixture : IDisposable
|
|
||||||
{
|
|
||||||
public IConfiguration Configuration { get; set; }
|
|
||||||
|
|
||||||
public IServiceProvider Services { get; set; }
|
|
||||||
|
|
||||||
public IMediator Mediator { get; set; }
|
|
||||||
|
|
||||||
public CommonFixture()
|
|
||||||
{
|
|
||||||
Configuration = new ConfigurationBuilder()
|
|
||||||
.SetBasePath(Directory.GetCurrentDirectory())
|
|
||||||
.AddJsonFile("appsettings.json")
|
|
||||||
.AddUserSecrets<CommonFixture>()
|
|
||||||
.AddEnvironmentVariables()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
Services = new ServiceCollection()
|
|
||||||
.AddMediator()
|
|
||||||
.AddLogging()
|
|
||||||
.AddEmailServiceBus()
|
|
||||||
.AddShopDatabase(Configuration)
|
|
||||||
.AddEmailServices(Configuration)
|
|
||||||
.BuildServiceProvider();
|
|
||||||
|
|
||||||
Mediator = Services.GetRequiredService<IMediator>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() { }
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
<UserSecretsId>62fa604a-1340-4edb-9ddd-3305fcf46fca</UserSecretsId>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="coverlet.collector" Version="10.0.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.2">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
|
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="Mediator"/>
|
|
||||||
<Using Include="Xunit.Abstractions"/>
|
|
||||||
<Using Include="Microsoft.Extensions.DependencyInjection"/>
|
|
||||||
<Using Include="Microsoft.Extensions.Configuration"/>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\LiteCharms.Features\LiteCharms.Features.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="Xunit" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="appsettings.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using LiteCharms.Features.Notifications.Commands;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Tests;
|
|
||||||
|
|
||||||
public class NotificationsFeatureTests(CommonFixture fixture) : IClassFixture<CommonFixture>
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public async Task CreateNotificationCommand_ShouldSucceed()
|
|
||||||
{
|
|
||||||
var command = CreateNotification.Create(Models.NotificationDirection.Outgoing, "UnitTest", "khwezi@mngoma.co.za",
|
|
||||||
"CreateNotificationCommand_ShouldSucceed Test", "Test Message", Models.NotificationPlatforms.Email, Models.Priorities.Medium,
|
|
||||||
"Khngisa Shop - Test", "shop@litecharms.co.za", Guid.NewGuid().ToString(), Models.CorrelationIdTypes.None,
|
|
||||||
true, false);
|
|
||||||
|
|
||||||
var result = await fixture.Mediator.Send(command);
|
|
||||||
|
|
||||||
Assert.True(result.IsSuccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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": "LiteCharms.LeadGenerator"
|
|
||||||
},
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using LiteCharms.Features.Extensions;
|
|
||||||
using static LiteCharms.Features.Extensions.Timezones;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Abstractions;
|
|
||||||
|
|
||||||
public abstract class EventBase
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; } = Guid.CreateVersion7();
|
|
||||||
|
|
||||||
public DateTimeOffset EnqueueAt { get; set; } = SouthAfricanTimeZone.UtcNow();
|
|
||||||
|
|
||||||
public string CorrelationId { get; set; } = Guid.CreateVersion7().ToString();
|
|
||||||
}
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Customers.Commands.Handlers;
|
namespace LiteCharms.Features.Customers.Commands.Handlers;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Customers.Commands.Handlers;
|
namespace LiteCharms.Features.Customers.Commands.Handlers;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Customers.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Customers.Queries;
|
namespace LiteCharms.Features.Customers.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Customers.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Customers.Queries;
|
namespace LiteCharms.Features.Customers.Queries;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Customers.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Customers.Queries.Handlers;
|
namespace LiteCharms.Features.Customers.Queries.Handlers;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Customers.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Customers.Queries.Handlers;
|
namespace LiteCharms.Features.Customers.Queries.Handlers;
|
||||||
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
using LiteCharms.Features.Email.Configuration;
|
|
||||||
using LiteCharms.Features.Email.Extensions;
|
|
||||||
using LiteCharms.Features.Email.Models;
|
|
||||||
using LiteCharms.Features.Shop;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email;
|
|
||||||
|
|
||||||
public class EmailService(IOptions<SmtpSettings> options) : IEmailService
|
|
||||||
{
|
|
||||||
private readonly SmtpSettings settings = options.Value;
|
|
||||||
private readonly SmtpClient client = new();
|
|
||||||
private readonly int sendMaxCount = 10;
|
|
||||||
private int sendCount = 0;
|
|
||||||
|
|
||||||
public EmailStatuses Status { get; private set; } = EmailStatuses.Disconnected;
|
|
||||||
|
|
||||||
public async Task<Result<Response>> SendEmailAsync(Message message, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
using var activity = EmailTelemetry.Source.StartActivity("Email Send");
|
|
||||||
activity?.SetTag("email.recipient", message.Recipient?.Address);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Status != EmailStatuses.Connected)
|
|
||||||
{
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, "Disconnected");
|
|
||||||
|
|
||||||
return Result.Fail<Response>("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();
|
|
||||||
|
|
||||||
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 response = await client.SendAsync(email, cancellationToken);
|
|
||||||
|
|
||||||
bool emailSent = response.Contains("OK", StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
|
|
||||||
message.Dispose();
|
|
||||||
|
|
||||||
Interlocked.Increment(ref sendCount);
|
|
||||||
|
|
||||||
if (sendCount % sendMaxCount == 0)
|
|
||||||
{
|
|
||||||
using var delayActivity = EmailTelemetry.Source.StartActivity("Rate Limit Pause");
|
|
||||||
|
|
||||||
sendCount = 0;
|
|
||||||
|
|
||||||
await Task.Delay(1000, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emailSent)
|
|
||||||
{
|
|
||||||
EmailTelemetry.EmailsSent.Add(1, new TagList { { "host", settings.Host } });
|
|
||||||
|
|
||||||
return Result.Ok(Response.Create(EmailStatuses.Success));
|
|
||||||
}
|
|
||||||
|
|
||||||
await DisconnectAsync(cancellationToken);
|
|
||||||
|
|
||||||
if (response.Contains("421"))
|
|
||||||
{
|
|
||||||
Status = EmailStatuses.TooManyConnections;
|
|
||||||
|
|
||||||
return Result.Fail<Response>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.Contains("451"))
|
|
||||||
{
|
|
||||||
Status = EmailStatuses.ConnectionAborted;
|
|
||||||
|
|
||||||
return Result.Fail<Response>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
EmailTelemetry.EmailsFailed.Add(1, new TagList { { "error_message", response } });
|
|
||||||
|
|
||||||
Status = EmailStatuses.Disconnected;
|
|
||||||
|
|
||||||
return Result.Fail<Response>("General error, disconnected");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } });
|
|
||||||
|
|
||||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Result<Response>> ConnectAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
using var activity = EmailTelemetry.Source.StartActivity("Email Connect");
|
|
||||||
activity?.SetTag("email.smtp.connect", settings.Host);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Status is EmailStatuses.Connected) return Result.Ok(Response.Create(Status));
|
|
||||||
|
|
||||||
await client.ConnectAsync(settings.Host!, settings.Port, settings.UseSsl, cancellationToken);
|
|
||||||
await client.AuthenticateAsync(settings.Credentials!.Username!, settings.Credentials.Password!, cancellationToken);
|
|
||||||
|
|
||||||
Status = EmailStatuses.Connected;
|
|
||||||
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Ok, "Connected");
|
|
||||||
|
|
||||||
return Result.Ok(Response.Create(Status));
|
|
||||||
}
|
|
||||||
catch (MailKit.ProtocolException ex)
|
|
||||||
{
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } });
|
|
||||||
|
|
||||||
Status = EmailStatuses.ProtocolError;
|
|
||||||
|
|
||||||
return Result.Fail<Response>(new Error(ex.Message).CausedBy(ex));
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (ex is MailKit.Security.SslHandshakeException || ex is MailKit.Security.AuthenticationException)
|
|
||||||
{
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } });
|
|
||||||
|
|
||||||
Status = EmailStatuses.AuthenticationError;
|
|
||||||
|
|
||||||
return Result.Fail<Response>(new Error(ex.Message).CausedBy(ex));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } });
|
|
||||||
|
|
||||||
Status = EmailStatuses.GeneralError;
|
|
||||||
|
|
||||||
return Result.Fail<Response>(new Error(ex.Message).CausedBy(ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Result> DisconnectAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
using var activity = EmailTelemetry.Source.StartActivity("Email Disconnect");
|
|
||||||
activity?.SetTag("email.smtp.disconnect", settings.Host);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Status is EmailStatuses.Disconnected) return Result.Ok();
|
|
||||||
|
|
||||||
await client.DisconnectAsync(true, cancellationToken);
|
|
||||||
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Ok, "Disconnected");
|
|
||||||
|
|
||||||
Status = EmailStatuses.Disconnected;
|
|
||||||
|
|
||||||
return Result.Ok();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } });
|
|
||||||
|
|
||||||
Status = EmailStatuses.GeneralError;
|
|
||||||
|
|
||||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
client.Dispose();
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using LiteCharms.Features.Notifications.Commands;
|
|
||||||
using LiteCharms.Features.Shop;
|
|
||||||
using static LiteCharms.Features.Email.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email.Events.Handlers;
|
|
||||||
|
|
||||||
// TODO: Inject the INotificationService
|
|
||||||
public class SendShopEmailEnquiryEventHandler(ISender mediator) :
|
|
||||||
INotificationHandler<SendShopEmailEnquiryEvent>
|
|
||||||
{
|
|
||||||
public async ValueTask Handle(SendShopEmailEnquiryEvent notification, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// TODO: Refactor this to use the NotificationService
|
|
||||||
var command = CreateNotification.Create(NotificationDirection.Outgoing, notification.SenderName!,
|
|
||||||
notification.SenderAddress!, notification.Subject!, notification.Message!, NotificationPlatforms.Email,
|
|
||||||
notification.Priority, ShopEmailFromName, ShopEmailFromAddress, Guid.CreateVersion7().ToString(),
|
|
||||||
CorrelationIdTypes.None, isInternal: true, isHtml: false);
|
|
||||||
|
|
||||||
// TODO: Remove, deprecated
|
|
||||||
await mediator.Send(command, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
|
||||||
using LiteCharms.Features.Shop;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email.Events;
|
|
||||||
|
|
||||||
public class SendShopEmailEnquiryEvent : EventBase, IEvent
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = nameof(SendShopEmailEnquiryEvent);
|
|
||||||
|
|
||||||
public string? SenderName { get; set; }
|
|
||||||
|
|
||||||
public string? SenderAddress { get; set; }
|
|
||||||
|
|
||||||
public string? Subject { get; set; }
|
|
||||||
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
public Priorities Priority { get; set; }
|
|
||||||
|
|
||||||
public SendShopEmailEnquiryEvent() { }
|
|
||||||
|
|
||||||
private SendShopEmailEnquiryEvent(string senderName, string senderAddress, string subject, string message, Priorities priority = Priorities.Medium)
|
|
||||||
{
|
|
||||||
SenderName = senderName;
|
|
||||||
SenderAddress = senderAddress;
|
|
||||||
Subject = subject;
|
|
||||||
Message = message;
|
|
||||||
Priority = priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SendShopEmailEnquiryEvent Create(string senderName, string senderAddress, string subject, string message, Priorities priority = Priorities.Medium)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(senderName, nameof(senderName));
|
|
||||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(senderAddress, nameof(senderAddress));
|
|
||||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(subject, nameof(subject));
|
|
||||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(message, nameof(message));
|
|
||||||
|
|
||||||
return new(senderName, senderAddress, subject, message, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Extensions;
|
|
||||||
|
|
||||||
public static class Constants
|
|
||||||
{
|
|
||||||
public const string ShopSchedulerName = "shop";
|
|
||||||
public const string ShopEmailFromName = "Khongisa Shop";
|
|
||||||
public const string ShopEmailFromAddress = "shop@litecharms.co.za";
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Extensions;
|
|
||||||
|
|
||||||
public static class EmailTelemetry
|
|
||||||
{
|
|
||||||
public static readonly ActivitySource Source = new("LiteCharms.EmailService");
|
|
||||||
public static readonly Meter Meter = new("LiteCharms.EmailService");
|
|
||||||
public static readonly Counter<long> EmailsSent = Meter.CreateCounter<long>("emails_sent_total", "count", "Total successful emails sent");
|
|
||||||
public static readonly Counter<long> EmailsFailed = Meter.CreateCounter<long>("emails_failed_total", "count", "Total failed email attempts");
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using LiteCharms.Features.Email.Configuration;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email.Extensions;
|
|
||||||
|
|
||||||
public static class Setup
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
|
||||||
|
|
||||||
services.AddSingleton<IEmailService, EmailService>();
|
|
||||||
|
|
||||||
services.AddOpenTelemetry()
|
|
||||||
.WithTracing(tracing => tracing.AddSource("LiteCharms.EmailService"))
|
|
||||||
.WithMetrics(metrics => metrics.AddMeter("LiteCharms.EmailService"));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using LiteCharms.Features.Email.Models;
|
|
||||||
using LiteCharms.Features.Shop;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email;
|
|
||||||
|
|
||||||
public interface IEmailService : IDisposable
|
|
||||||
{
|
|
||||||
EmailStatuses Status { get; }
|
|
||||||
|
|
||||||
Task<Result<Response>> SendEmailAsync(Message message, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<Result<Response>> ConnectAsync(CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<Result> DisconnectAsync(CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Models;
|
|
||||||
|
|
||||||
public class Attachment
|
|
||||||
{
|
|
||||||
public string? Name { get; set; }
|
|
||||||
|
|
||||||
public Stream? FileStream { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Models;
|
|
||||||
|
|
||||||
public class Body : IDisposable
|
|
||||||
{
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
public ReadOnlyCollection<Attachment>? Attachments { get; set; }
|
|
||||||
|
|
||||||
public BodyProperties Properties { get; set; } = new();
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (Attachments is null) return;
|
|
||||||
|
|
||||||
foreach (var attachment in Attachments!)
|
|
||||||
{
|
|
||||||
if (attachment is not null)
|
|
||||||
{
|
|
||||||
attachment.FileStream!.Close();
|
|
||||||
attachment.FileStream!.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Models;
|
|
||||||
|
|
||||||
public class BodyProperties
|
|
||||||
{
|
|
||||||
public bool IsHtml { get; set; }
|
|
||||||
|
|
||||||
public bool HasAttachments { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Models;
|
|
||||||
|
|
||||||
public class Message : IDisposable
|
|
||||||
{
|
|
||||||
public Party? Sender { get; set; }
|
|
||||||
|
|
||||||
public Party? Recipient { get; set; }
|
|
||||||
|
|
||||||
public string? Subject { get; set; }
|
|
||||||
|
|
||||||
public Body? Body { get; set; }
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Body?.Dispose();
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Email.Models;
|
|
||||||
|
|
||||||
public class Party
|
|
||||||
{
|
|
||||||
public string? Name { get; set; }
|
|
||||||
|
|
||||||
public string? Address { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using LiteCharms.Features.Shop;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Email.Models;
|
|
||||||
|
|
||||||
public class Response
|
|
||||||
{
|
|
||||||
public int Code { get; set; }
|
|
||||||
|
|
||||||
public string? Error { get; set; }
|
|
||||||
|
|
||||||
public EmailStatuses Status { get; set; }
|
|
||||||
|
|
||||||
private Response(EmailStatuses status, int code = 0, string? error = null)
|
|
||||||
{
|
|
||||||
Status = status;
|
|
||||||
Code = code;
|
|
||||||
Error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Response Create(EmailStatuses status, int code = 0, string? error = null) =>
|
|
||||||
new(status, code, error);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Extensions;
|
|
||||||
|
|
||||||
public static class Hash
|
|
||||||
{
|
|
||||||
public static Func<string?, string?> GenerateSha256HashString = (input) =>
|
|
||||||
Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input!)));
|
|
||||||
}
|
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Features.Utilities.Commands;
|
||||||
using LiteCharms.Features.Utilities.Hash.Commands;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Commands.Handlers;
|
namespace LiteCharms.Features.Leads.Commands.Handlers;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Commands.Handlers;
|
namespace LiteCharms.Features.Leads.Commands.Handlers;
|
||||||
|
|
||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop;
|
using LiteCharms.Models;
|
||||||
using LiteCharms.Models;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Commands;
|
namespace LiteCharms.Features.Leads.Commands;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Leads.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Queries;
|
namespace LiteCharms.Features.Leads.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Leads.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Queries;
|
namespace LiteCharms.Features.Leads.Queries;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Leads.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Queries.Handlers;
|
namespace LiteCharms.Features.Leads.Queries.Handlers;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Leads.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Leads.Queries.Handlers;
|
namespace LiteCharms.Features.Leads.Queries.Handlers;
|
||||||
|
|
||||||
@@ -28,86 +28,6 @@
|
|||||||
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Quartz Scheduler-->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="OpenTelemetry" Version="1.15.3" />
|
|
||||||
<PackageReference Include="Quartz" Version="3.18.1" />
|
|
||||||
<PackageReference Include="Quartz.Plugins" Version="3.18.1" />
|
|
||||||
<PackageReference Include="Quartz.Plugins.TimeZoneConverter" Version="3.18.1" />
|
|
||||||
<PackageReference Include="Quartz.Serialization.SystemTextJson" Version="3.18.1" />
|
|
||||||
<PackageReference Include="Quartz.AspNetCore" Version="3.18.1" />
|
|
||||||
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.18.1" />
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<Using Include="Quartz" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Configuration -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.8" />
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<Using Include="Microsoft.Extensions.Configuration" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Health Checks -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Core" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Data" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.8" />
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<Using Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Open Telemetry -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<Using Include="OpenTelemetry.Resources" />
|
|
||||||
<Using Include="OpenTelemetry.Exporter" />
|
|
||||||
<Using Include="OpenTelemetry.Logs" />
|
|
||||||
<Using Include="OpenTelemetry.Metrics" />
|
|
||||||
<Using Include="OpenTelemetry.Trace" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Database -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.8">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.8">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
|
||||||
|
|
||||||
<!-- Global Usings -->
|
|
||||||
<Using Include="Npgsql" />
|
|
||||||
<Using Include="Microsoft.EntityFrameworkCore" />
|
|
||||||
<Using Include="Microsoft.EntityFrameworkCore.Design" />
|
|
||||||
<Using Include="Microsoft.EntityFrameworkCore.Metadata.Builders" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Email -->
|
<!-- Email -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||||
@@ -130,18 +50,15 @@
|
|||||||
|
|
||||||
<!-- Shared Usings -->
|
<!-- Shared Usings -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Microsoft.AspNetCore.Builder" />
|
|
||||||
<Using Include="Microsoft.Extensions.Hosting" />
|
|
||||||
<Using Include="System.Text" />
|
<Using Include="System.Text" />
|
||||||
<Using Include="System.Text.Json" />
|
|
||||||
<Using Include="System.Threading.Channels" />
|
|
||||||
<Using Include="System.Collections.ObjectModel" />
|
|
||||||
<Using Include="System.Diagnostics" />
|
|
||||||
<Using Include="System.Diagnostics.Metrics" />
|
|
||||||
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
|
||||||
<Using Include="System.Security.Cryptography" />
|
<Using Include="System.Security.Cryptography" />
|
||||||
|
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||||
<Using Include="Microsoft.Extensions.Options" />
|
<Using Include="Microsoft.Extensions.Options" />
|
||||||
<Using Include="Microsoft.Extensions.Logging" />
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LiteCharms.Extensions\LiteCharms.Extensions.csproj" />
|
||||||
|
<ProjectReference Include="..\LiteCharms.Infrastructure\LiteCharms.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Mediator;
|
|
||||||
|
|
||||||
public sealed class LoggingPipelineBehavior<TRequest, TResponse>(ILogger<LoggingPipelineBehavior<TRequest, TResponse>> logger) :
|
|
||||||
IPipelineBehavior<TRequest, TResponse?>
|
|
||||||
where TRequest : IRequest<TResponse>
|
|
||||||
where TResponse : ResultBase, new()
|
|
||||||
{
|
|
||||||
public async ValueTask<TResponse?> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse?> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
TResponse? response = await next(message, cancellationToken);
|
|
||||||
|
|
||||||
if (response is null)
|
|
||||||
logger.LogCritical("{Request} {TypeName} was returned as null", typeof(TRequest).Name, typeof(TRequest).Name);
|
|
||||||
|
|
||||||
if(response?.IsFailed == true || response?.Errors?.Any() == true)
|
|
||||||
{
|
|
||||||
foreach (var error in response.Errors)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(error.Message))
|
|
||||||
logger.LogWarning("{Request} {Error}", typeof(TRequest).Name, error.Message);
|
|
||||||
|
|
||||||
if (error?.Reasons?.Count > 0)
|
|
||||||
error.Reasons.ForEach(r => logger.LogError("{Request} {Reason}", typeof(TRequest).Name, r.ToString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Mediator;
|
|
||||||
|
|
||||||
public static class MediatorTelemetry
|
|
||||||
{
|
|
||||||
public const string ServiceName = "LiteCharms.Mediator";
|
|
||||||
|
|
||||||
public static readonly ActivitySource Source = new(ServiceName);
|
|
||||||
public static readonly Meter Meter = new(ServiceName);
|
|
||||||
|
|
||||||
public static readonly Counter<long> RequestCounter = Meter.CreateCounter<long>("mediator_requests_total");
|
|
||||||
public static readonly Histogram<double> RequestDuration = Meter.CreateHistogram<double>("mediator_request_duration_ms");
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
namespace LiteCharms.Features.Mediator;
|
|
||||||
|
|
||||||
public sealed class TelemetryPipelineBehavior<TRequest, TResponse> :
|
|
||||||
IPipelineBehavior<TRequest, TResponse?>
|
|
||||||
where TRequest : IRequest<TResponse>
|
|
||||||
where TResponse : ResultBase, new()
|
|
||||||
{
|
|
||||||
public async ValueTask<TResponse?> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse?> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var requestName = typeof(TRequest).Name;
|
|
||||||
|
|
||||||
using var activity = MediatorTelemetry.Source.StartActivity(requestName);
|
|
||||||
|
|
||||||
activity?.SetTag("mediator.request_type", typeof(TRequest).FullName);
|
|
||||||
|
|
||||||
var stopWatch = Stopwatch.StartNew();
|
|
||||||
var status = "Success";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TResponse? response = await next(message, cancellationToken);
|
|
||||||
|
|
||||||
if (response is null)
|
|
||||||
{
|
|
||||||
status = "NullResponse";
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, "Response was null");
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.IsFailed)
|
|
||||||
{
|
|
||||||
status = "Failed";
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, "Request failed");
|
|
||||||
|
|
||||||
var firstError = response.Errors.FirstOrDefault()?.Message ?? "Unknown Error";
|
|
||||||
activity?.SetTag("error.message", firstError);
|
|
||||||
|
|
||||||
foreach (var error in response.Errors)
|
|
||||||
activity?.AddEvent(new ActivityEvent("Result Error", tags: new() { { "message", error.Message } }));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Ok);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
status = "Exception";
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
stopWatch.Stop();
|
|
||||||
|
|
||||||
var tags = new TagList { { "request", requestName }, { "status", status } };
|
|
||||||
|
|
||||||
MediatorTelemetry.RequestCounter.Add(1, tags);
|
|
||||||
MediatorTelemetry.RequestDuration.Record(stopWatch.Elapsed.TotalMilliseconds, tags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using LiteCharms.Models;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.Notifications.Commands;
|
||||||
|
|
||||||
|
public class CreateNotificationCommand : IRequest<Result<Guid>>
|
||||||
|
{
|
||||||
|
public NotificationDirection Direction { get; set; }
|
||||||
|
|
||||||
|
public string? Author { get; set; }
|
||||||
|
|
||||||
|
public string? Title { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
public string? Platform { get; set; }
|
||||||
|
|
||||||
|
public string? PlatformAddress { get; set; }
|
||||||
|
|
||||||
|
public string? CorrelationId { get; set; }
|
||||||
|
|
||||||
|
public string? CorrelationIdType { get; set; }
|
||||||
|
|
||||||
|
public bool IsInternal { get; set; }
|
||||||
|
|
||||||
|
private CreateNotificationCommand(NotificationDirection direction, string author, string title, string description, string platform, string platformAddress, string correlationId, string correlationIdType, bool isInternal)
|
||||||
|
{
|
||||||
|
Direction = direction;
|
||||||
|
Author = author;
|
||||||
|
Title = title;
|
||||||
|
Description = description;
|
||||||
|
Platform = platform;
|
||||||
|
PlatformAddress = platformAddress;
|
||||||
|
CorrelationId = correlationId;
|
||||||
|
CorrelationIdType = correlationIdType;
|
||||||
|
IsInternal = isInternal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreateNotificationCommand Create(NotificationDirection direction, string author, string title, string description, string platform, string platformAddress, string correlationId, string correlationIdType, bool isInternal)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(author))
|
||||||
|
throw new ArgumentException("Author cannot be null or whitespace.", nameof(author));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(title))
|
||||||
|
throw new ArgumentException("Title cannot be null or whitespace.", nameof(title));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(description))
|
||||||
|
throw new ArgumentException("Description cannot be null or whitespace.", nameof(description));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(platform))
|
||||||
|
throw new ArgumentException("Platform cannot be null or whitespace.", nameof(platform));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(platformAddress))
|
||||||
|
throw new ArgumentException("PlatformAddress cannot be null or whitespace.", nameof(platformAddress));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(correlationId))
|
||||||
|
throw new ArgumentException("CorrelationId cannot be null or whitespace.", nameof(correlationId));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(correlationIdType))
|
||||||
|
throw new ArgumentException("CorrelationIdType cannot be null or whitespace.", nameof(correlationIdType));
|
||||||
|
|
||||||
|
return new(direction, author, title, description, platform, platformAddress, correlationId, correlationIdType, isInternal);
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-16
@@ -1,39 +1,34 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Notifications.Commands.Handlers;
|
namespace LiteCharms.Features.Notifications.Commands.Handlers;
|
||||||
|
|
||||||
public class CreateNotificationCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<CreateNotification, Result<Guid>>
|
public class CreateNotificationCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<CreateNotificationCommand, Result<Guid>>
|
||||||
{
|
{
|
||||||
public async ValueTask<Result<Guid>> Handle(CreateNotification request, CancellationToken cancellationToken)
|
public async ValueTask<Result<Guid>> Handle(CreateNotificationCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
var newNotification = context.Notifications.Add(new Entities.Notification
|
var newNotification = context.Notifications.Add(new Entities.Notification
|
||||||
{
|
{
|
||||||
Direction = request.Direction,
|
Direction = request.Direction,
|
||||||
SenderName = request.Sender,
|
Author = request.Author,
|
||||||
Sender = request.SenderAddress,
|
Title = request.Title,
|
||||||
Recipient = request.Recipient,
|
Description = request.Description,
|
||||||
RecipientAddress = request.RecipientAddress,
|
|
||||||
Subject = request.Subject,
|
|
||||||
Message = request.Message,
|
|
||||||
Platform = request.Platform,
|
Platform = request.Platform,
|
||||||
Priority = request.Priority,
|
PlatformAddress = request.PlatformAddress,
|
||||||
CorrelationId = request.CorrelationId,
|
CorrelationId = request.CorrelationId,
|
||||||
CorrelationIdType = request.CorrelationIdType,
|
CorrelationIdType = request.CorrelationIdType,
|
||||||
IsInternal = request.IsInternal,
|
IsInternal = request.IsInternal,
|
||||||
IsHtml = request.IsHtml,
|
|
||||||
Processed = false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return newNotification is not null
|
return newNotification is not null
|
||||||
? Result.Ok(newNotification.Entity.Id)
|
? Result.Ok(newNotification.Entity.Id)
|
||||||
: Result.Fail(new Error("Failed to create notification"));
|
: Result.Fail(new Error("Failed to create notification"));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-7
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Notifications.Commands.Handlers;
|
namespace LiteCharms.Features.Notifications.Commands.Handlers;
|
||||||
|
|
||||||
@@ -17,12 +17,6 @@ public class UpdateNotificationCommandHandler(IDbContextFactory<ShopDbContext> c
|
|||||||
|
|
||||||
notification.Processed = request.Processed;
|
notification.Processed = request.Processed;
|
||||||
|
|
||||||
if (request.HasError)
|
|
||||||
{
|
|
||||||
notification.HasError = request.HasError;
|
|
||||||
notification.Errors = request.Errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||||
? Result.Ok()
|
? Result.Ok()
|
||||||
: Result.Fail(new Error($"Failed to update notification with id {request.NotificationId}."));
|
: Result.Fail(new Error($"Failed to update notification with id {request.NotificationId}."));
|
||||||
+2
-8
@@ -6,19 +6,13 @@ public class UpdateNotificationCommand : IRequest<Result>
|
|||||||
|
|
||||||
public bool Processed { get; set; }
|
public bool Processed { get; set; }
|
||||||
|
|
||||||
public bool HasError { get; set; }
|
private UpdateNotificationCommand(Guid notificationId, bool processed)
|
||||||
|
|
||||||
public string[]? Errors { get; set; }
|
|
||||||
|
|
||||||
private UpdateNotificationCommand(Guid notificationId, bool processed, bool hasError = false, string[]? errors = null)
|
|
||||||
{
|
{
|
||||||
NotificationId = notificationId;
|
NotificationId = notificationId;
|
||||||
Processed = processed;
|
Processed = processed;
|
||||||
HasError = hasError;
|
|
||||||
Errors = errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UpdateNotificationCommand Create(Guid notificationId, bool processed, bool hasError = false, string[]? errors = null)
|
public static UpdateNotificationCommand Create(Guid notificationId, bool processed)
|
||||||
{
|
{
|
||||||
if(notificationId == Guid.Empty)
|
if(notificationId == Guid.Empty)
|
||||||
throw new ArgumentException("Notification ID cannot be empty.", nameof(notificationId));
|
throw new ArgumentException("Notification ID cannot be empty.", nameof(notificationId));
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Notifications.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Notifications.Queries;
|
namespace LiteCharms.Features.Notifications.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Notifications.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Notifications.Queries;
|
namespace LiteCharms.Features.Notifications.Queries;
|
||||||
|
|
||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Notifications.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Notifications.Queries.Handlers;
|
namespace LiteCharms.Features.Notifications.Queries.Handlers;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ public class GetNotificationQueryHandler(IDbContextFactory<ShopDbContext> contex
|
|||||||
{
|
{
|
||||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
var notification = await context.Notifications.FirstOrDefaultAsync(n => n.Id == request.NotificationId, cancellationToken);
|
var notification = await context.Notifications.FindAsync(new object[] { request.NotificationId }, cancellationToken);
|
||||||
|
|
||||||
return notification is not null
|
return notification is not null
|
||||||
? Result.Ok(notification.ToModel())
|
? Result.Ok(notification.ToModel())
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Notifications.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Notifications.Queries.Handlers;
|
namespace LiteCharms.Features.Notifications.Queries.Handlers;
|
||||||
|
|
||||||
+3
-15
@@ -8,26 +8,14 @@ public class CreateOrderCommand : IRequest<Result<Guid>>
|
|||||||
|
|
||||||
public Guid? QuoteId { get; set; }
|
public Guid? QuoteId { get; set; }
|
||||||
|
|
||||||
public string[]? Requirements { get; set; }
|
private CreateOrderCommand(Guid customerId, Guid shoppingCartId, Guid? quoteId = null)
|
||||||
|
|
||||||
public string[]? Notes { get; set; }
|
|
||||||
|
|
||||||
public string[]? Terms { get; set; }
|
|
||||||
|
|
||||||
public bool DepositRequired { get; set; }
|
|
||||||
|
|
||||||
private CreateOrderCommand(Guid customerId, Guid shoppingCartId, bool depositRequired, Guid? quoteId = null, string[]? requirements = null, string[]? notes = null, string[]? terms = null)
|
|
||||||
{
|
{
|
||||||
CustomerId = customerId;
|
CustomerId = customerId;
|
||||||
ShoppingCartId = shoppingCartId;
|
ShoppingCartId = shoppingCartId;
|
||||||
DepositRequired = depositRequired;
|
|
||||||
QuoteId = quoteId;
|
QuoteId = quoteId;
|
||||||
Requirements = requirements;
|
|
||||||
Notes = notes;
|
|
||||||
Terms = terms;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CreateOrderCommand Create(Guid customerId, Guid shoppingCartId, bool depositRequired, Guid? quoteId = null, string[]? requirements = null, string[]? notes = null, string[]? terms = null)
|
public static CreateOrderCommand Create(Guid customerId, Guid shoppingCartId, Guid? quoteId = null)
|
||||||
{
|
{
|
||||||
if (customerId == Guid.Empty)
|
if (customerId == Guid.Empty)
|
||||||
throw new ArgumentException("CustomerId is required.", nameof(customerId));
|
throw new ArgumentException("CustomerId is required.", nameof(customerId));
|
||||||
@@ -35,6 +23,6 @@ public class CreateOrderCommand : IRequest<Result<Guid>>
|
|||||||
if (shoppingCartId == Guid.Empty)
|
if (shoppingCartId == Guid.Empty)
|
||||||
throw new ArgumentException("ShoppingCartId is required.", nameof(shoppingCartId));
|
throw new ArgumentException("ShoppingCartId is required.", nameof(shoppingCartId));
|
||||||
|
|
||||||
return new(customerId, shoppingCartId, depositRequired, quoteId, requirements, notes, terms);
|
return new(customerId, shoppingCartId, quoteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-10
@@ -1,6 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
|
||||||
using LiteCharms.Models;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Commands.Handlers;
|
namespace LiteCharms.Features.Orders.Commands.Handlers;
|
||||||
|
|
||||||
@@ -23,15 +21,10 @@ public class CreateOrderCommandHandler(IDbContextFactory<ShopDbContext> contextF
|
|||||||
|
|
||||||
var newOrder = context.Orders.Add(new Entities.Order
|
var newOrder = context.Orders.Add(new Entities.Order
|
||||||
{
|
{
|
||||||
CreatedAt = DateTime.UtcNow,
|
|
||||||
Status = OrderStatus.Pending,
|
|
||||||
CustomerId = request.CustomerId,
|
CustomerId = request.CustomerId,
|
||||||
QuoteId = request.QuoteId,
|
|
||||||
ShoppingCartId = request.ShoppingCartId,
|
ShoppingCartId = request.ShoppingCartId,
|
||||||
DepositRequired = request.DepositRequired,
|
QuoteId = request.QuoteId,
|
||||||
Requirements = request.Requirements,
|
CreatedAt = DateTime.UtcNow
|
||||||
Notes = request.Notes,
|
|
||||||
Terms = request.Terms
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Commands.Handlers;
|
namespace LiteCharms.Features.Orders.Commands.Handlers;
|
||||||
|
|
||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop;
|
using LiteCharms.Models;
|
||||||
using LiteCharms.Models;
|
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Commands;
|
namespace LiteCharms.Features.Orders.Commands;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Orders.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Queries;
|
namespace LiteCharms.Features.Orders.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Orders.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Queries;
|
namespace LiteCharms.Features.Orders.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Orders.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Queries;
|
namespace LiteCharms.Features.Orders.Queries;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Orders.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Orders.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
||||||
|
|
||||||
+2
-3
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Orders.Models;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
||||||
|
|
||||||
@@ -16,7 +16,6 @@ public class GetOrdersQueryHandler(IDbContextFactory<ShopDbContext> contextFacto
|
|||||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
var orders = await context.Orders
|
var orders = await context.Orders
|
||||||
.AsNoTracking()
|
|
||||||
.OrderByDescending(o => o.CreatedAt)
|
.OrderByDescending(o => o.CreatedAt)
|
||||||
.Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate)
|
.Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate)
|
||||||
.Take(request.MaxRecords)
|
.Take(request.MaxRecords)
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Products.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Products.Queries;
|
namespace LiteCharms.Features.Products.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Products.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Products.Queries;
|
namespace LiteCharms.Features.Products.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Products.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Products.Queries;
|
namespace LiteCharms.Features.Products.Queries;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
using LiteCharms.Features.Shop.Products.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Products.Queries;
|
namespace LiteCharms.Features.Products.Queries;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Products.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Products.Queries.Handlers;
|
namespace LiteCharms.Features.Products.Queries.Handlers;
|
||||||
|
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using LiteCharms.Extensions;
|
using LiteCharms.Extensions;
|
||||||
using LiteCharms.Features.Shop.Postgres;
|
using LiteCharms.Infrastructure.Database;
|
||||||
using LiteCharms.Features.Shop.Products.Models;
|
using LiteCharms.Models;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Products.Queries.Handlers;
|
namespace LiteCharms.Features.Products.Queries.Handlers;
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user