Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5efdde37c | |||
| 87da491ed6 | |||
| 1592d5dc8f | |||
| 08a64d1578 | |||
| f67f5eaf53 | |||
| 50a8a59d92 | |||
| 70c6e0bfbc | |||
| b70d9559b0 | |||
| 032b9e1818 | |||
| 424c1c6f8c | |||
| 81d5e8f07c | |||
| 7d5e9a18d8 | |||
| 20a53942b5 | |||
| 3656223b5f | |||
| 9edb2aa4aa | |||
| ccf30ac36b | |||
| 6ed023f2cf | |||
| d6fdf1b9c8 | |||
| 2c9f5a846c | |||
| 89a343a85f | |||
| 41f7c05be3 | |||
| 52d204e286 | |||
| 1a03355e84 | |||
| f245bc94e1 | |||
| 7743c3178e | |||
| da141311ff | |||
| ab3d8e6e9a | |||
| 97bde73777 | |||
| db4c348288 | |||
| a65e926a53 | |||
| 6683234642 | |||
| 1471d9e597 | |||
| 6ddbb9479a | |||
| e978aa17f8 | |||
| 6c7349a0f8 | |||
| a31f75c5ef | |||
| e97fd6cd3f | |||
| 7f4246ac63 | |||
| 184c7c252a | |||
| dfc62c8fe1 | |||
| bfe8c458d6 | |||
| 0f91f102e5 |
+5
-1
@@ -19,6 +19,10 @@ steps:
|
||||
commands:
|
||||
- dotnet pack LiteCharms.Features/LiteCharms.Features.csproj -c Release -p:PackageVersion=$VERSION -o dist/
|
||||
- dotnet nuget push dist/LiteCharms.Features.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL
|
||||
- dotnet pack LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj -c Release -p:PackageVersion=$VERSION -o dist/
|
||||
- dotnet nuget push dist/LiteCharms.Features.TechShop.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL
|
||||
- dotnet pack LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj -c Release -p:PackageVersion=$VERSION -o dist/
|
||||
- dotnet nuget push dist/LiteCharms.Features.MidrandBooks.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL
|
||||
|
||||
- name: gitea-tag-release
|
||||
image: alpine/git
|
||||
@@ -41,7 +45,7 @@ steps:
|
||||
\"tag_name\": \"$VERSION\",
|
||||
\"target_commitish\": \"${DRONE_COMMIT_SHA}\",
|
||||
\"name\": \"Library Suite $VERSION\",
|
||||
\"body\": \"### Published NuGet Packages\nAll packages versioned as **$VERSION**:\n* LiteCharms.Features\n\n[View in Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\",
|
||||
\"body\": \"### Published NuGet Packages\nAll packages versioned as **$VERSION**:\n* LiteCharms.Features\n* LiteCharms.Features.TechShop\n* LiteCharms.Features.MidrandBooks\n\n[View in Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\",
|
||||
\"draft\": false,
|
||||
\"prerelease\": false
|
||||
}"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.MidrandBooks.Extensions;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Tests;
|
||||
|
||||
public class Fixture : IDisposable
|
||||
{
|
||||
public IConfiguration Configuration { get; set; }
|
||||
|
||||
public IServiceProvider Services { get; set; }
|
||||
|
||||
public IMediator Mediator { get; set; }
|
||||
|
||||
public Fixture()
|
||||
{
|
||||
Configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddUserSecrets<Fixture>()
|
||||
.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"), optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
Services = new ServiceCollection()
|
||||
.AddMediator()
|
||||
.AddLogging()
|
||||
//.AddMidrandShopServices()
|
||||
.AddEmailServiceBus()
|
||||
.AddGarageS3(Configuration)
|
||||
.AddMidrandShopDatabase(Configuration)
|
||||
.AddEmailServices(Configuration)
|
||||
.AddSingleton(Configuration)
|
||||
.BuildServiceProvider();
|
||||
|
||||
Mediator = Services.GetRequiredService<IMediator>();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<UserSecretsId>b205af96-ceef-44e1-851c-458c9fd1c437</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="10.0.1">
|
||||
<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.MidrandBooks\LiteCharms.Features.MidrandBooks.csproj" />
|
||||
<ProjectReference Include="..\LiteCharms.Features\LiteCharms.Features.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"BookshopS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
"BucketName": "bookshop",
|
||||
"CdnBaseUrl": "https://bookshop.cdn.khongisa.co.za"
|
||||
},
|
||||
"BookshopQuotesS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
"BucketName": "bookshop.quotes",
|
||||
"CdnBaseUrl": "https://bookshop.quotes.cdn.khongisa.co.za"
|
||||
},
|
||||
"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": "*"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using LiteCharms.Features.MidrandBooks.HealthChecks;
|
||||
using static LiteCharms.Features.Extensions.Postgres;
|
||||
using static LiteCharms.Features.MidrandBooks.Extensions.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Extensions;
|
||||
|
||||
public static class HealthChecks
|
||||
{
|
||||
public static IServiceCollection AddMidrandShopQuartzHealthCheck(this IServiceCollection services)
|
||||
{
|
||||
services.AddHealthChecks().AddCheck<MidrandShopQuartzHealthCheck>(SchedulerDbConfigName);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMidrandShopPostgresHealthCheck(this IServiceCollection services)
|
||||
{
|
||||
services.AddHealthChecks().AddCheck<PostgresMidrandShopHealthCheck>(MidrandShopDbConfigName);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHealthChecksSupport(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddHealthChecks()
|
||||
.AddCheck("Self", () => HealthCheckResult.Healthy());
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using LiteCharms.Features.MidrandBooks.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Extensions;
|
||||
|
||||
public static class Postgres
|
||||
{
|
||||
public const string MidrandShopDbConfigName = "PostgresMidrandBooks";
|
||||
|
||||
public static IServiceCollection AddMidrandShopDatabase(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddPooledDbContextFactory<MidrandShopDbContext>(options =>
|
||||
options.UseNpgsql(configuration.GetConnectionString(MidrandShopDbConfigName)));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using static LiteCharms.Features.Extensions.Quartz;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.HealthChecks;
|
||||
|
||||
public class MidrandShopQuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck
|
||||
{
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var scheduler = await schedulerFactory.GetScheduler(MidrandShopSchedulerName, cancellationToken);
|
||||
|
||||
if(scheduler == null)
|
||||
return HealthCheckResult.Unhealthy($"Scheduler with name '{MidrandShopSchedulerName}' not found.");
|
||||
|
||||
if (!scheduler.IsStarted)
|
||||
return HealthCheckResult.Unhealthy($"{MidrandShopSchedulerName} Quartz scheduler is not running");
|
||||
|
||||
await scheduler.CheckExists(new JobKey(Guid.NewGuid().ToString()), cancellationToken);
|
||||
|
||||
return HealthCheckResult.Healthy($"{MidrandShopSchedulerName} Quartz scheduler is ready");
|
||||
}
|
||||
catch (SchedulerException)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy($"{MidrandShopSchedulerName} Quartz scheduler cannot connect to the store");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using static LiteCharms.Features.MidrandBooks.Extensions.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.HealthChecks;
|
||||
|
||||
public class PostgresMidrandShopHealthCheck(IConfiguration configuration) : IHealthCheck
|
||||
{
|
||||
private readonly string connectionString = configuration.GetConnectionString(MidrandShopDbConfigName)!;
|
||||
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var dataSource = NpgsqlDataSource.Create(connectionString);
|
||||
await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = "SELECT 1";
|
||||
|
||||
await command.ExecuteScalarAsync(cancellationToken);
|
||||
|
||||
return HealthCheckResult.Healthy($"{MidrandShopDbConfigName} is responsive.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy($"{MidrandShopDbConfigName} is unreachable.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
|
||||
<UserSecretsId>5be62f49-3ed0-4468-884e-1b04e048b45a</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Nuget Package Details -->
|
||||
<PropertyGroup>
|
||||
<PackageId>LiteCharms.Features.MidrandBooks</PackageId>
|
||||
<Version>1.0.20</Version>
|
||||
<Authors>Khwezi Mngoma</Authors>
|
||||
<Company>Lite Charms (PTY) Ltd</Company>
|
||||
<Description>MidrandBooks feature 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>
|
||||
|
||||
<!-- 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 -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
|
||||
<!-- Global Usings-->
|
||||
<Using Include="MimeKit" />
|
||||
<Using Include="MailKit.Net.Smtp" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- CQRS -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentResults" Version="4.0.0" />
|
||||
<PackageReference Include="Mediator.Abstractions" Version="3.0.2" />
|
||||
|
||||
<!-- Global Usings -->
|
||||
<Using Include="FluentResults" />
|
||||
<Using Include="Mediator" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Amazon S3 SDK -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.Extensions.NetCore.Setup" Version="4.0.4.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.23.4" />
|
||||
<ProjectReference Include="..\LiteCharms.Features\LiteCharms.Features.csproj" />
|
||||
|
||||
<!-- global Usings -->
|
||||
<Using Include="Amazon.S3" />
|
||||
<Using Include="Amazon.S3.Model" />
|
||||
<Using Include="Amazon.Runtime" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Shared Usings -->
|
||||
<ItemGroup>
|
||||
<Using Include="Microsoft.AspNetCore.Builder" />
|
||||
<Using Include="Microsoft.Extensions.Hosting" />
|
||||
<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="Microsoft.Extensions.Options" />
|
||||
<Using Include="Microsoft.Extensions.Logging" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace LiteCharms.Features.MidrandBooks.Postgres;
|
||||
|
||||
public class MidrandShopDbContext(DbContextOptions<MidrandShopDbContext> options) : DbContext(options)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using static LiteCharms.Features.MidrandBooks.Extensions.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Postgres;
|
||||
|
||||
public class MidrandShopDbContextFactory : IDesignTimeDbContextFactory<MidrandShopDbContext>
|
||||
{
|
||||
public MidrandShopDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddUserSecrets(typeof(MidrandShopDbContext).Assembly)
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<MidrandShopDbContext>();
|
||||
optionsBuilder.UseNpgsql(configuration.GetConnectionString(MidrandShopDbConfigName));
|
||||
|
||||
return new MidrandShopDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"Email": {
|
||||
"Credentials": {
|
||||
"Username": "shop@litecharms.co.za"
|
||||
},
|
||||
"Port": 465,
|
||||
"Host": "mail.litecharms.co.za",
|
||||
"UseSsl": true
|
||||
},
|
||||
"Monitoring": {
|
||||
"ApiKey": "",
|
||||
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
|
||||
"ServiceName": "MidrandBooks"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Tests;
|
||||
|
||||
public class Fixture : IDisposable
|
||||
{
|
||||
public IConfiguration Configuration { get; set; }
|
||||
|
||||
public IServiceProvider Services { get; set; }
|
||||
|
||||
public IMediator Mediator { get; set; }
|
||||
|
||||
public Fixture()
|
||||
{
|
||||
Configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddUserSecrets<Fixture>()
|
||||
.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"), optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
Services = new ServiceCollection()
|
||||
.AddMediator()
|
||||
.AddLogging()
|
||||
.AddTechShopServices()
|
||||
.AddEmailServiceBus()
|
||||
.AddGarageS3(Configuration)
|
||||
.AddTechShopDatabase(Configuration)
|
||||
.AddEmailServices(Configuration)
|
||||
.AddSingleton(Configuration)
|
||||
.BuildServiceProvider();
|
||||
|
||||
Mediator = Services.GetRequiredService<IMediator>();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<UserSecretsId>fa06c1a6-2ba8-4c47-b19b-11e0f78e7b92</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="10.0.1">
|
||||
<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.TechShop\LiteCharms.Features.TechShop.csproj" />
|
||||
<ProjectReference Include="..\LiteCharms.Features\LiteCharms.Features.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,65 @@
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.TechShop.Notifications;
|
||||
using LiteCharms.Features.TechShop.Notifications.Events;
|
||||
using static LiteCharms.Features.Extensions.Email;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Tests;
|
||||
|
||||
public class NotificationsFeatureTests(Fixture fixture, ITestOutputHelper output) : IClassFixture<Fixture>
|
||||
{
|
||||
private readonly NotificationService notificationService = fixture.Services.GetRequiredService<NotificationService>();
|
||||
|
||||
[Fact]
|
||||
public async Task CreateNotificationCommand_ShouldSucceed()
|
||||
{
|
||||
Notifications.Models.CreateNotification request = new()
|
||||
{
|
||||
CorrelationId = Guid.CreateVersion7().ToString(),
|
||||
CorrelationIdType = CorrelationIdTypes.None,
|
||||
Direction = NotificationDirection.Outgoing,
|
||||
Platform = NotificationPlatforms.Email,
|
||||
Priority = Priorities.Medium,
|
||||
Sender = "xUnit Test",
|
||||
SenderAddress = "khwezi@mngoma.africa",
|
||||
Recipient = $"{ShopEmailFromName} [Test]",
|
||||
RecipientAddress = ShopEmailFromAddress,
|
||||
Subject = "Test Message",
|
||||
Message = "This is an automation test",
|
||||
IsHtml = false,
|
||||
IsInternal = true,
|
||||
};
|
||||
|
||||
var createResult = await notificationService.CreateNotificationAsync(request);
|
||||
|
||||
Assert.True(createResult.IsSuccess);
|
||||
|
||||
foreach (var error in createResult.Errors) output.WriteLine(error.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetNotifications_ShouldReturn_AllNotifications()
|
||||
{
|
||||
DateRange range = new()
|
||||
{
|
||||
From = DateOnly.FromDateTime(new DateTime(2026, 04, 01, 0, 0, 0, DateTimeKind.Utc)),
|
||||
To = DateOnly.FromDateTime(DateTime.UtcNow),
|
||||
MaxRecords = 10
|
||||
};
|
||||
|
||||
var getResult = await notificationService.GetNotificationsAsync(range);
|
||||
|
||||
Assert.True(getResult.IsSuccess);
|
||||
|
||||
foreach (var error in getResult.Errors) output.WriteLine(error.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessEmailNotificationsEvent_ShouldSucceed()
|
||||
{
|
||||
var notification = ProcessEmailNotificationsEvent.Create();
|
||||
|
||||
await fixture.Mediator.Publish(notification);
|
||||
|
||||
Assert.True(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using LiteCharms.Features.TechShop.Products;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Tests;
|
||||
|
||||
public class ProductsFeatureTests(Fixture fixture, ITestOutputHelper output) : IClassFixture<Fixture>
|
||||
{
|
||||
[Fact]
|
||||
public async Task GetProductsAsync_ReturnsProducts()
|
||||
{
|
||||
var productService = fixture.Services.GetRequiredService<ProductService>();
|
||||
|
||||
var result = await productService.GetProductsAsync();
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.NotNull(result.Value);
|
||||
|
||||
output.WriteLine($"Retrieved {result.Value.Length} products.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"BookshopS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
"BucketName": "bookshop",
|
||||
"CdnBaseUrl": "https://bookshop.cdn.khongisa.co.za"
|
||||
},
|
||||
"BookshopQuotesS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
"BucketName": "bookshop.quotes",
|
||||
"CdnBaseUrl": "https://bookshop.quotes.cdn.khongisa.co.za"
|
||||
},
|
||||
"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
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages.Entities;
|
||||
|
||||
[EntityTypeConfiguration<PackageConfirguration, Package>]
|
||||
public class Package : Models.Package
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages.Entities;
|
||||
|
||||
public class PackageConfirguration : IEntityTypeConfiguration<Package>
|
||||
{
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using LiteCharms.Features.Shop.Products.Entities;
|
||||
using LiteCharms.Features.TechShop.Products.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages.Entities;
|
||||
|
||||
[EntityTypeConfiguration<PackageItemConfiguration, PackageItem>]
|
||||
public class PackageItem : Models.PackageItem
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages.Entities;
|
||||
|
||||
public class PackageItemConfiguration : IEntityTypeConfiguration<PackageItem>
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Models;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages.Models;
|
||||
|
||||
public class Package
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Models;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages.Models;
|
||||
|
||||
public class PackageItem
|
||||
{
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using static LiteCharms.Features.Extensions.Timezones;
|
||||
using LiteCharms.Features.TechShop.CartPackages.Models;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.CartPackages;
|
||||
namespace LiteCharms.Features.TechShop.CartPackages;
|
||||
|
||||
public class PackageService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.Shop.Customers.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Customers.Models;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Customers;
|
||||
namespace LiteCharms.Features.TechShop.Customers;
|
||||
|
||||
public class CustomerService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
using LiteCharms.Features.Shop.Leads.Entities;
|
||||
using LiteCharms.Features.Shop.Orders.Entities;
|
||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
using LiteCharms.Features.TechShop.Leads.Entities;
|
||||
using LiteCharms.Features.TechShop.Orders.Entities;
|
||||
using LiteCharms.Features.TechShop.Quotes.Entities;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Customers.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Customers.Entities;
|
||||
|
||||
[EntityTypeConfiguration<CustomerConfiguration, Customer>]
|
||||
public class Customer : Models.Customer
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Customers.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Customers.Entities;
|
||||
|
||||
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Customers.Models;
|
||||
namespace LiteCharms.Features.TechShop.Customers.Models;
|
||||
|
||||
public class Customer
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Customers.Models;
|
||||
namespace LiteCharms.Features.TechShop.Customers.Models;
|
||||
|
||||
public record CreateCustomer
|
||||
{
|
||||
@@ -1,16 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop;
|
||||
|
||||
public enum EmailStatuses : int
|
||||
{
|
||||
GeneralError = 0,
|
||||
AuthenticationError = 1,
|
||||
ProtocolError = 2,
|
||||
Connected = 3,
|
||||
Disconnected = 4,
|
||||
TooManyConnections = 5,
|
||||
ConnectionAborted = 6,
|
||||
Success = 7
|
||||
}
|
||||
namespace LiteCharms.Features.TechShop;
|
||||
|
||||
public enum CorrelationIdTypes : int
|
||||
{
|
||||
@@ -27,13 +15,6 @@ public enum CorrelationIdTypes : int
|
||||
LinkedIn = 10
|
||||
}
|
||||
|
||||
public enum Priorities : int
|
||||
{
|
||||
Low = 0,
|
||||
Medium = 1,
|
||||
High = 2,
|
||||
}
|
||||
|
||||
public enum NotificationPlatforms : int
|
||||
{
|
||||
Email = 1,
|
||||
@@ -0,0 +1,22 @@
|
||||
using LiteCharms.Features.TechShop.HealthChecks;
|
||||
using static LiteCharms.Features.TechShop.Extensions.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Extensions;
|
||||
|
||||
public static class HealthChecks
|
||||
{
|
||||
public static IServiceCollection AddShopQuartzHealthCheck(this IServiceCollection services)
|
||||
{
|
||||
services.AddHealthChecks().AddCheck<ShopQuartzHealthCheck>("ShopQuartz");
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
public static IServiceCollection AddShopPostgresHealthCheck(this IServiceCollection services)
|
||||
{
|
||||
services.AddHealthChecks().AddCheck<PostgresShopHealthCheck>(TechShopDbConfigName);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
+28
-25
@@ -1,17 +1,18 @@
|
||||
using LiteCharms.Features.Shop.CartPackages.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;
|
||||
using LiteCharms.Features.TechShop.CartPackages.Models;
|
||||
using LiteCharms.Features.TechShop.Customers.Models;
|
||||
using LiteCharms.Features.TechShop.Leads.Models;
|
||||
using LiteCharms.Features.TechShop.Notifications.Models;
|
||||
using LiteCharms.Features.TechShop.Orders.Models;
|
||||
using LiteCharms.Features.TechShop.Products.Entities;
|
||||
using LiteCharms.Features.TechShop.Products.Models;
|
||||
using LiteCharms.Features.TechShop.Quotes.Models;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts.Models;
|
||||
|
||||
namespace LiteCharms.Features.Extensions;
|
||||
namespace LiteCharms.Features.TechShop.Extensions;
|
||||
|
||||
public static class EntityModeMappers
|
||||
public static class Mappers
|
||||
{
|
||||
public static ShoppingCartPackage ToModel(this Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage entity) =>
|
||||
public static ShoppingCartPackage ToModel(this ShoppingCarts.Entities.ShoppingCartPackage entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -20,7 +21,7 @@ public static class EntityModeMappers
|
||||
ShoppingCartId = entity.ShoppingCartId
|
||||
};
|
||||
|
||||
public static PackageItem ToModel(this Features.Shop.CartPackages.Entities.PackageItem entity) =>
|
||||
public static PackageItem ToModel(this CartPackages.Entities.PackageItem entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -30,7 +31,7 @@ public static class EntityModeMappers
|
||||
ProductPriceId = entity.ProductPriceId
|
||||
};
|
||||
|
||||
public static Package ToModel(this Features.Shop.CartPackages.Entities.Package entity) =>
|
||||
public static Package ToModel(this CartPackages.Entities.Package entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -43,7 +44,7 @@ public static class EntityModeMappers
|
||||
Summary = entity.Summary
|
||||
};
|
||||
|
||||
public static ShoppingCartItem ToModel(this Features.Shop.ShoppingCarts.Entities.ShoppingCartItem entity) =>
|
||||
public static ShoppingCartItem ToModel(this ShoppingCarts.Entities.ShoppingCartItem entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -54,7 +55,7 @@ public static class EntityModeMappers
|
||||
ShoppingCartId = entity.ShoppingCartId
|
||||
};
|
||||
|
||||
public static ShoppingCart ToModel(this Features.Shop.ShoppingCarts.Entities.ShoppingCart entity) =>
|
||||
public static ShoppingCart ToModel(this ShoppingCarts.Entities.ShoppingCart entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -64,7 +65,7 @@ public static class EntityModeMappers
|
||||
OrderId = entity.OrderId
|
||||
};
|
||||
|
||||
public static Quote ToModel(this Features.Shop.Quotes.Entities.Quote entity) =>
|
||||
public static Quote ToModel(this Quotes.Entities.Quote entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -79,7 +80,7 @@ public static class EntityModeMappers
|
||||
OrderId = entity.OrderId
|
||||
};
|
||||
|
||||
public static Notification ToModel(this Features.Shop.Notifications.Entities.Notification entity) =>
|
||||
public static Notification ToModel(this Notifications.Entities.Notification entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -103,7 +104,7 @@ public static class EntityModeMappers
|
||||
Errors = entity.Errors
|
||||
};
|
||||
|
||||
public static Customer ToModel(this Features.Shop.Customers.Entities.Customer entity) =>
|
||||
public static Customer ToModel(this Customers.Entities.Customer entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -128,7 +129,7 @@ public static class EntityModeMappers
|
||||
Whatsapp = entity.Whatsapp
|
||||
};
|
||||
|
||||
public static Lead ToModel(this Features.Shop.Leads.Entities.Lead entity) =>
|
||||
public static Lead ToModel(this Leads.Entities.Lead entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -149,7 +150,7 @@ public static class EntityModeMappers
|
||||
Status = entity.Status
|
||||
};
|
||||
|
||||
public static Order ToModel(this Features.Shop.Orders.Entities.Order entity) =>
|
||||
public static Order ToModel(this Orders.Entities.Order entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -163,7 +164,7 @@ public static class EntityModeMappers
|
||||
InvoiceUrl = entity.InvoiceUrl
|
||||
};
|
||||
|
||||
public static OrderRefund ToModel(this Features.Shop.Orders.Entities.OrderRefund entity) =>
|
||||
public static OrderRefund ToModel(this Orders.Entities.OrderRefund entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -173,19 +174,21 @@ public static class EntityModeMappers
|
||||
Amount = entity.Amount
|
||||
};
|
||||
|
||||
public static Product ToModel(this Features.Shop.Products.Entities.Product entity) =>
|
||||
public static Products.Models.Product ToModel(this Products.Entities.Product entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
Id = entity.Id,
|
||||
CreatedAt = entity.CreatedAt,
|
||||
Name = entity.Name,
|
||||
Description = entity.Description,
|
||||
Active = entity.Active,
|
||||
Summary = entity.Summary,
|
||||
ImageUrl = entity.ImageUrl,
|
||||
Thumbnails = entity.Thumbnails
|
||||
Thumbnails = entity.Thumbnails,
|
||||
Metadata = entity.Metadata,
|
||||
};
|
||||
|
||||
public static ProductPrice ToModel(this Features.Shop.Products.Entities.ProductPrice entity) =>
|
||||
public static Products.Models.ProductPrice ToModel(this Products.Entities.ProductPrice entity) =>
|
||||
new()
|
||||
{
|
||||
Id = entity.Id,
|
||||
@@ -0,0 +1,16 @@
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Extensions;
|
||||
|
||||
public static class Postgres
|
||||
{
|
||||
public const string TechShopDbConfigName = "PostgresShop";
|
||||
|
||||
public static IServiceCollection AddTechShopDatabase(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddPooledDbContextFactory<ShopDbContext>(options =>
|
||||
options.UseNpgsql(configuration.GetConnectionString(TechShopDbConfigName)));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using LiteCharms.Features.TechShop.CartPackages;
|
||||
using LiteCharms.Features.TechShop.Customers;
|
||||
using LiteCharms.Features.TechShop.Leads;
|
||||
using LiteCharms.Features.TechShop.Notifications;
|
||||
using LiteCharms.Features.TechShop.Orders;
|
||||
using LiteCharms.Features.TechShop.Products;
|
||||
using LiteCharms.Features.TechShop.Quotes;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Extensions;
|
||||
|
||||
public static class Shop
|
||||
{
|
||||
public static IServiceCollection AddTechShopServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<PackageService>()
|
||||
.AddSingleton<LeadService>()
|
||||
.AddSingleton<NotificationService>()
|
||||
.AddSingleton<OrderService>()
|
||||
.AddSingleton<ProductService>()
|
||||
.AddSingleton<QuoteService>()
|
||||
.AddSingleton<ShoppingCartService>()
|
||||
.AddSingleton<CustomerService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
+7
-5
@@ -1,8 +1,10 @@
|
||||
namespace LiteCharms.Features.HealthChecks;
|
||||
using static LiteCharms.Features.TechShop.Extensions.Postgres;
|
||||
|
||||
public class PostgresHealthCheck(IConfiguration configuration) : IHealthCheck
|
||||
namespace LiteCharms.Features.TechShop.HealthChecks;
|
||||
|
||||
public class PostgresShopHealthCheck(IConfiguration configuration) : IHealthCheck
|
||||
{
|
||||
private readonly string connectionString = configuration.GetConnectionString("PostgresShop")!;
|
||||
private readonly string connectionString = configuration.GetConnectionString(TechShopDbConfigName)!;
|
||||
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -16,11 +18,11 @@ public class PostgresHealthCheck(IConfiguration configuration) : IHealthCheck
|
||||
|
||||
await command.ExecuteScalarAsync(cancellationToken);
|
||||
|
||||
return HealthCheckResult.Healthy("PostgreSQL is responsive.");
|
||||
return HealthCheckResult.Healthy($"{TechShopDbConfigName} is responsive.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy("PostgreSQL is unreachable.", ex);
|
||||
return HealthCheckResult.Unhealthy($"{TechShopDbConfigName} is unreachable.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using static LiteCharms.Features.Extensions.Quartz;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.HealthChecks;
|
||||
|
||||
public class ShopQuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck
|
||||
{
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var scheduler = await schedulerFactory.GetScheduler(TechShopSchedulerName, cancellationToken);
|
||||
|
||||
if(scheduler == null)
|
||||
return HealthCheckResult.Unhealthy($"Scheduler with name '{TechShopSchedulerName}' not found.");
|
||||
|
||||
if (!scheduler.IsStarted)
|
||||
return HealthCheckResult.Unhealthy($"{TechShopSchedulerName} Quartz scheduler is not running");
|
||||
|
||||
await scheduler.CheckExists(new JobKey(Guid.NewGuid().ToString()), cancellationToken);
|
||||
|
||||
return HealthCheckResult.Healthy($"{TechShopSchedulerName} Quartz scheduler is ready");
|
||||
}
|
||||
catch (SchedulerException)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy($"{TechShopSchedulerName} Quartz scheduler cannot connect to the store");
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using LiteCharms.Features.Shop.Customers.Entities;
|
||||
using LiteCharms.Features.TechShop.Customers.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Leads.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Leads.Entities;
|
||||
|
||||
[EntityTypeConfiguration<LeadConfiguration, Lead>]
|
||||
public class Lead : Models.Lead
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Leads.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Leads.Entities;
|
||||
|
||||
public class LeadConfiguration : IEntityTypeConfiguration<Lead>
|
||||
{
|
||||
+5
-4
@@ -1,10 +1,11 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.Shop.Leads.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Leads.Models;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using static LiteCharms.Features.Extensions.Hash;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Leads;
|
||||
namespace LiteCharms.Features.TechShop.Leads;
|
||||
|
||||
public class LeadService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
@@ -28,7 +29,7 @@ public class LeadService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
FeedItemId = request.FeedItemId,
|
||||
Status = LeadStatus.New,
|
||||
TargetId = request.TargetId,
|
||||
AttributionHash = GenerateSha256HashString.Invoke($"{request.ClickId}{request.AppClickId}{request.WebClickId}")
|
||||
AttributionHash = StringToSha256Hash.Invoke($"{request.ClickId}{request.AppClickId}{request.WebClickId}")
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Leads.Models;
|
||||
namespace LiteCharms.Features.TechShop.Leads.Models;
|
||||
|
||||
public class Lead
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Leads.Models;
|
||||
namespace LiteCharms.Features.TechShop.Leads.Models;
|
||||
|
||||
public record CreateLead
|
||||
{
|
||||
@@ -0,0 +1,166 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\LiteCharms.snk</AssemblyOriginatorKeyFile>
|
||||
<UserSecretsId>fbd8f4a2-0420-44e2-baff-4678d9e7eee1</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Nuget Package Details -->
|
||||
<PropertyGroup>
|
||||
<PackageId>LiteCharms.Features.TechShop</PackageId>
|
||||
<Version>1.0.20</Version>
|
||||
<Authors>Khwezi Mngoma</Authors>
|
||||
<Company>Lite Charms (PTY) Ltd</Company>
|
||||
<Description>TechShop feature 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>
|
||||
|
||||
<!-- 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 -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||
|
||||
<!-- Global Usings-->
|
||||
<Using Include="MimeKit" />
|
||||
<Using Include="MailKit.Net.Smtp" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- CQRS -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentResults" Version="4.0.0" />
|
||||
<PackageReference Include="Mediator.Abstractions" Version="3.0.2" />
|
||||
|
||||
<!-- Global Usings -->
|
||||
<Using Include="FluentResults" />
|
||||
<Using Include="Mediator" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Amazon S3 SDK -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.Extensions.NetCore.Setup" Version="4.0.4.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.23.4" />
|
||||
<ProjectReference Include="..\LiteCharms.Features\LiteCharms.Features.csproj" />
|
||||
|
||||
<!-- global Usings -->
|
||||
<Using Include="Amazon.S3" />
|
||||
<Using Include="Amazon.S3.Model" />
|
||||
<Using Include="Amazon.Runtime" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Shared Usings -->
|
||||
<ItemGroup>
|
||||
<Using Include="Microsoft.AspNetCore.Builder" />
|
||||
<Using Include="Microsoft.Extensions.Hosting" />
|
||||
<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="Microsoft.Extensions.Options" />
|
||||
<Using Include="Microsoft.Extensions.Logging" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Entities;
|
||||
|
||||
[EntityTypeConfiguration<NotificationConfiguration, Notification>]
|
||||
public class Notification : Models.Notification;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Entities;
|
||||
|
||||
public class NotificationConfiguration : IEntityTypeConfiguration<Notification>
|
||||
{
|
||||
+14
-7
@@ -1,8 +1,8 @@
|
||||
using LiteCharms.Features.Email;
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Notifications.Models;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Notifications.Events.Handlers;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Events.Handlers;
|
||||
|
||||
public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbContext> contextFactory, ILogger<ProcessEmailNotificationsEvent> logger,
|
||||
EmailService emailService) : INotificationHandler<ProcessEmailNotificationsEvent>
|
||||
@@ -15,17 +15,20 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbConte
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (emailService.Status != EmailStatuses.Connected)
|
||||
await emailService.ConnectAsync(cancellationToken);
|
||||
|
||||
var notifications = await context.Notifications
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ThenBy(o => o.Priority)
|
||||
.Where(n => n.CorrelationIdType == CorrelationIdTypes.Email)
|
||||
.Where(n => n.Direction == NotificationDirection.Outgoing)
|
||||
.Where(n => n.Platform == NotificationPlatforms.Email &&
|
||||
n.Direction == NotificationDirection.Outgoing && n.Processed == false)
|
||||
.Take(message.MaxRecords)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var notification in notifications)
|
||||
{
|
||||
if (dropBatch || cancellationToken.IsCancellationRequested) break;
|
||||
if (dropBatch) break;
|
||||
|
||||
var sendResult = await SendEmailAsync(notification,emailService, cancellationToken);
|
||||
|
||||
@@ -46,12 +49,16 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbConte
|
||||
notification.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await emailService.DisconnectAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Result> SendEmailAsync(Notification notification, EmailService service, CancellationToken cancellationToken = default)
|
||||
+3
-5
@@ -1,14 +1,12 @@
|
||||
using LiteCharms.Features.Shop;
|
||||
using LiteCharms.Features.Shop.Notifications;
|
||||
using static LiteCharms.Features.Email.Extensions.Constants;
|
||||
using static LiteCharms.Features.Extensions.Email;
|
||||
|
||||
namespace LiteCharms.Features.Email.Events.Handlers;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Events.Handlers;
|
||||
|
||||
public class SendShopEmailEnquiryEventHandler(NotificationService notificationService) :
|
||||
INotificationHandler<SendShopEmailEnquiryEvent>
|
||||
{
|
||||
public async ValueTask Handle(SendShopEmailEnquiryEvent notification, CancellationToken cancellationToken) =>
|
||||
await notificationService.CreateNotificationAsync(new Shop.Notifications.Models.CreateNotification
|
||||
await notificationService.CreateNotificationAsync(new Models.CreateNotification
|
||||
{
|
||||
CorrelationId = notification.CorrelationId,
|
||||
CorrelationIdType = CorrelationIdTypes.None,
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
using LiteCharms.Features.Abstractions;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Notifications.Events;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Events;
|
||||
|
||||
public class ProcessEmailNotificationsEvent : EventBase, IEvent
|
||||
{
|
||||
@@ -8,6 +8,8 @@ public class ProcessEmailNotificationsEvent : EventBase, IEvent
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
public ProcessEmailNotificationsEvent() { MaxRecords = 1000; }
|
||||
|
||||
private ProcessEmailNotificationsEvent(int maxRecords = 1000) => MaxRecords = maxRecords;
|
||||
|
||||
public static ProcessEmailNotificationsEvent Create(int maxRecords = 1000) => new(maxRecords);
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
using LiteCharms.Features.Abstractions;
|
||||
using LiteCharms.Features.Shop;
|
||||
|
||||
namespace LiteCharms.Features.Email.Events;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Events;
|
||||
|
||||
public class SendShopEmailEnquiryEvent : EventBase, IEvent
|
||||
{
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Models;
|
||||
using LiteCharms.Features.TechShop;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Models;
|
||||
|
||||
public class Notification
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Models;
|
||||
namespace LiteCharms.Features.TechShop.Notifications.Models;
|
||||
|
||||
public record CreateNotification
|
||||
{
|
||||
+9
-11
@@ -1,9 +1,10 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Notifications.Models;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Notifications;
|
||||
namespace LiteCharms.Features.TechShop.Notifications;
|
||||
|
||||
public class NotificationService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
@@ -63,8 +64,8 @@ public class NotificationService(IDbContextFactory<ShopDbContext> contextFactory
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = range.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = range.To.ToDateTime(TimeOnly.MaxValue);
|
||||
var fromDate = range.From.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
|
||||
var toDate = range.To.ToDateTime(TimeOnly.MaxValue, DateTimeKind.Utc);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
@@ -96,12 +97,9 @@ public class NotificationService(IDbContextFactory<ShopDbContext> contextFactory
|
||||
return Result.Fail(new Error($"Notification with id {request.NotificationId} not found."));
|
||||
|
||||
notification.Processed = request.Processed;
|
||||
|
||||
if (request.HasError)
|
||||
{
|
||||
notification.HasError = request.HasError;
|
||||
notification.Errors = request.Errors;
|
||||
}
|
||||
notification.UpdatedAt = DateTime.UtcNow;
|
||||
notification.HasError = request.HasError;
|
||||
notification.Errors = request.Errors;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
using LiteCharms.Features.Shop.Customers.Entities;
|
||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
using LiteCharms.Features.TechShop.Customers.Entities;
|
||||
using LiteCharms.Features.TechShop.Quotes.Entities;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Orders.Entities;
|
||||
|
||||
[EntityTypeConfiguration<OrderConfiguration, Order>]
|
||||
public class Order : Models.Order
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Orders.Entities;
|
||||
|
||||
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Orders.Entities;
|
||||
|
||||
[EntityTypeConfiguration<OrderRefundConfiguration, OrderRefund>]
|
||||
public class OrderRefund : Models.OrderRefund
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Orders.Entities;
|
||||
|
||||
public class OrderRefundConfiguration : IEntityTypeConfiguration<OrderRefund>
|
||||
{
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.TechShop;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Orders.Models;
|
||||
|
||||
public class Order
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Models;
|
||||
namespace LiteCharms.Features.TechShop.Orders.Models;
|
||||
|
||||
public class OrderRefund
|
||||
{
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.TechShop;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Orders.Models;
|
||||
|
||||
public record CreateOrder
|
||||
{
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Orders.Models;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders;
|
||||
namespace LiteCharms.Features.TechShop.Orders;
|
||||
|
||||
public class OrderService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
@@ -9,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
[Migration("20260512065421_Init")]
|
||||
+1
-1
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Init : Migration
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
@@ -9,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
[Migration("20260514004002_UsedStringTableNames")]
|
||||
+1
-1
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UsedStringTableNames : Migration
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
@@ -9,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
[Migration("20260515055221_FixedLeadCustomerRelationship")]
|
||||
+1
-1
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixedLeadCustomerRelationship : Migration
|
||||
Generated
+788
@@ -0,0 +1,788 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Products.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
[Migration("20260520191059_AddedProductMetadata")]
|
||||
partial class AddedProductMetadata
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.8")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Package", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("PackageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ProductPriceId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PackageId");
|
||||
|
||||
b.HasIndex("ProductPriceId");
|
||||
|
||||
b.ToTable("PackageItem", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Entities.Customer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("City")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Company")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Country")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Discord")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LinkedIn")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PostalCode")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Region")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Slack")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Tax")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Website")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Whatsapp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Customers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long?>("AdGroupId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("AdName")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AppClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("AttributionHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("CampaignId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClickLocation")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long?>("FeedItemId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Source")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("TargetId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("WebClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.ToTable("Leads", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Notifications.Entities.Notification", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CorrelationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("CorrelationIdType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int>("Direction")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.PrimitiveCollection<string>("Errors")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<bool>("HasError")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsHtml")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsInternal")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("Processed")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<string>("RecipientAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RecipientName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SenderAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SenderName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Notification", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("InvoiceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.PrimitiveCollection<string>("Notes")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.PrimitiveCollection<string>("Requirements")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.PrimitiveCollection<string>("Terms")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.ToTable("Orders", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal>("Amount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("OrderRefunds", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<ProductMetadata>("Metadata")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.PrimitiveCollection<string>("Thumbnails")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<decimal>("Discount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<Guid>("ProductId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.ToTable("ProductPrices", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("ExpiredAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("InvoiceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<Guid?>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.HasIndex("OrderId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ShoppingCartId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Quotes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.HasIndex("OrderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ShoppingCarts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("ProductPriceId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<Guid>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProductPriceId");
|
||||
|
||||
b.HasIndex("ShoppingCartId");
|
||||
|
||||
b.ToTable("ShoppingCartItems", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("PackageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PackageId");
|
||||
|
||||
b.HasIndex("ShoppingCartId");
|
||||
|
||||
b.ToTable("ShoppingCartPackages", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package")
|
||||
.WithMany("PackageItems")
|
||||
.HasForeignKey("PackageId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductPriceId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Package");
|
||||
|
||||
b.Navigation("ProductPrice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
|
||||
.WithMany("Leads")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
|
||||
.WithMany("Orders")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order")
|
||||
.WithMany("Refunds")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Products.Entities.Product", "Product")
|
||||
.WithMany("ProductPrices")
|
||||
.HasForeignKey("ProductId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
|
||||
.WithMany("Quotes")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order")
|
||||
.WithOne("Quote")
|
||||
.HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "OrderId");
|
||||
|
||||
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithOne("Quote")
|
||||
.HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "ShoppingCartId");
|
||||
|
||||
b.Navigation("Customer");
|
||||
|
||||
b.Navigation("Order");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
|
||||
.WithMany("ShoppingCarts")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order")
|
||||
.WithOne("ShoppingCart")
|
||||
.HasForeignKey("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "OrderId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Customer");
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductPriceId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithMany("ShoppingCartItems")
|
||||
.HasForeignKey("ShoppingCartId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ProductPrice");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package")
|
||||
.WithMany()
|
||||
.HasForeignKey("PackageId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithMany("ShoppingCartPackages")
|
||||
.HasForeignKey("ShoppingCartId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Package");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b =>
|
||||
{
|
||||
b.Navigation("PackageItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Entities.Customer", b =>
|
||||
{
|
||||
b.Navigation("Leads");
|
||||
|
||||
b.Navigation("Orders");
|
||||
|
||||
b.Navigation("Quotes");
|
||||
|
||||
b.Navigation("ShoppingCarts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b =>
|
||||
{
|
||||
b.Navigation("Quote");
|
||||
|
||||
b.Navigation("Refunds");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b =>
|
||||
{
|
||||
b.Navigation("ProductPrices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.Navigation("Quote");
|
||||
|
||||
b.Navigation("ShoppingCartItems");
|
||||
|
||||
b.Navigation("ShoppingCartPackages");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using LiteCharms.Features.TechShop.Products.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedProductMetadata : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<bool>(
|
||||
name: "Active",
|
||||
table: "Products",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false,
|
||||
oldClrType: typeof(bool),
|
||||
oldType: "boolean",
|
||||
oldDefaultValue: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "CreatedAt",
|
||||
table: "Products",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValueSql: "now()");
|
||||
|
||||
migrationBuilder.AddColumn<ProductMetadata>(
|
||||
name: "Metadata",
|
||||
table: "Products",
|
||||
type: "jsonb",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "UpdatedAt",
|
||||
table: "Products",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CreatedAt",
|
||||
table: "Products");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Metadata",
|
||||
table: "Products");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UpdatedAt",
|
||||
table: "Products");
|
||||
|
||||
migrationBuilder.AlterColumn<bool>(
|
||||
name: "Active",
|
||||
table: "Products",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: true,
|
||||
oldClrType: typeof(bool),
|
||||
oldType: "boolean",
|
||||
oldDefaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -1,6 +1,7 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Products.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
@@ -8,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
namespace LiteCharms.Features.TechShop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
partial class ShopDbContextModelSnapshot : ModelSnapshot
|
||||
@@ -386,7 +387,12 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
@@ -397,6 +403,9 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<ProductMetadata>("Metadata")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@@ -409,6 +418,9 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
b.PrimitiveCollection<string>("Thumbnails")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Products", (string)null);
|
||||
@@ -0,0 +1,39 @@
|
||||
using LiteCharms.Features.TechShop.CartPackages.Entities;
|
||||
using LiteCharms.Features.TechShop.Customers.Entities;
|
||||
using LiteCharms.Features.TechShop.Leads.Entities;
|
||||
using LiteCharms.Features.TechShop.Notifications.Entities;
|
||||
using LiteCharms.Features.TechShop.Orders.Entities;
|
||||
using LiteCharms.Features.TechShop.Products.Entities;
|
||||
using LiteCharms.Features.TechShop.Quotes.Entities;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
public class ShopDbContext(DbContextOptions<ShopDbContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<Customer> Customers { get; set; }
|
||||
|
||||
public DbSet<Lead> Leads { get; set; }
|
||||
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
|
||||
public DbSet<OrderRefund> OrderRefunds { get; set; }
|
||||
|
||||
public DbSet<Product> Products { get; set; }
|
||||
|
||||
public DbSet<ProductPrice> ProductPrices { get; set; }
|
||||
|
||||
public DbSet<Notification> Notifications { get; set; }
|
||||
|
||||
public DbSet<Quote> Quotes { get; set; }
|
||||
|
||||
public DbSet<ShoppingCart> ShoppingCarts { get; set; }
|
||||
|
||||
public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
|
||||
|
||||
public DbSet<Package> Packages { get; set; }
|
||||
|
||||
public DbSet<PackageItem> PackageItems { get; set; }
|
||||
|
||||
public DbSet<ShoppingCartPackage> ShoppingCartPackages { get; set; }
|
||||
}
|
||||
+4
-2
@@ -1,4 +1,6 @@
|
||||
namespace LiteCharms.Features.Shop.Postgres;
|
||||
using static LiteCharms.Features.TechShop.Extensions.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Postgres;
|
||||
|
||||
public class ShopDbContextFactory : IDesignTimeDbContextFactory<ShopDbContext>
|
||||
{
|
||||
@@ -12,7 +14,7 @@ public class ShopDbContextFactory : IDesignTimeDbContextFactory<ShopDbContext>
|
||||
.Build();
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<ShopDbContext>();
|
||||
optionsBuilder.UseNpgsql(configuration.GetConnectionString("PostgresShop"));
|
||||
optionsBuilder.UseNpgsql(configuration.GetConnectionString(TechShopDbConfigName));
|
||||
|
||||
return new ShopDbContext(optionsBuilder.Options);
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Products.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ProductConfiguration, Product>]
|
||||
public class Product : Models.Product
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Products.Entities;
|
||||
|
||||
public class ProductConfiguration : IEntityTypeConfiguration<Product>
|
||||
{
|
||||
@@ -8,10 +8,13 @@ public class ProductConfiguration : IEntityTypeConfiguration<Product>
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.Name).IsRequired();
|
||||
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.Summary).IsRequired().HasMaxLength(512);
|
||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(2048);
|
||||
builder.Property(f => f.ImageUrl).IsRequired(false).HasMaxLength(2048);
|
||||
builder.Property(f => f.Thumbnails).HasColumnType("jsonb").IsRequired(false);
|
||||
builder.Property(f => f.Active).HasDefaultValue(true);
|
||||
builder.Property(f => f.Active).HasDefaultValue(false);
|
||||
builder.Property(f => f.Metadata).HasColumnType("jsonb").IsRequired(false);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Products.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ProductPriceConfiguration, ProductPrice>]
|
||||
public class ProductPrice : Models.ProductPrice
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Products.Entities;
|
||||
|
||||
public class ProductPriceConfiguration : IEntityTypeConfiguration<ProductPrice>
|
||||
{
|
||||
@@ -7,7 +7,7 @@ public class ProductPriceConfiguration : IEntityTypeConfiguration<ProductPrice>
|
||||
builder.ToTable("ProductPrices");
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.ProductId).IsRequired();
|
||||
builder.Property(f => f.Price).IsRequired().HasPrecision(18, 2);
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace LiteCharms.Features.TechShop.Products.Models;
|
||||
|
||||
public class CreateProductModel
|
||||
{
|
||||
[MaxLength(128)]
|
||||
[Required(ErrorMessage = "Product name is required.")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[MaxLength(512)]
|
||||
[Required(ErrorMessage = "Summary is required.")]
|
||||
public string? Summary { get; set; }
|
||||
|
||||
[MaxLength(2048)]
|
||||
[Required(ErrorMessage = "Description is required.")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
[Range(0.01, double.MaxValue, ErrorMessage = "Price must be greater than zero.")]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
[MaxLength(128)]
|
||||
[Required(ErrorMessage = "Author metadata is required.")]
|
||||
public string? Author { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "Publication Date is required.")]
|
||||
public DateTime PublishDate { get; set; } = DateTime.Today;
|
||||
|
||||
[MaxLength(255)]
|
||||
[Required(ErrorMessage = "Copyright Information field is required.")]
|
||||
public string? CopyrightInfo { get; set; }
|
||||
|
||||
[MaxLength(128)]
|
||||
[Required(ErrorMessage = "ISBN code is required.")]
|
||||
[RegularExpression(@"^(?=(?:\D*\d){10}(?:(?:\D*\d){3})?$)[\d-]+$", ErrorMessage = "Please enter a valid ISBN-10 or ISBN-13 string.")]
|
||||
public string? Isbn { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "Primary image is required.")]
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
public List<string> Thumbnails { get; set; } = [];
|
||||
}
|
||||
+7
-1
@@ -1,9 +1,13 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Models;
|
||||
namespace LiteCharms.Features.TechShop.Products.Models;
|
||||
|
||||
public class Product
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Summary { get; set; }
|
||||
@@ -15,4 +19,6 @@ public class Product
|
||||
public string[]? Thumbnails { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
|
||||
public ProductMetadata? Metadata { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace LiteCharms.Features.TechShop.Products.Models;
|
||||
|
||||
public class ProductMetadata
|
||||
{
|
||||
public string? Manufacturer { get; set; }
|
||||
|
||||
public string? ManufactureDate { get; set; }
|
||||
|
||||
public string? CopyrightInfo { get; set; }
|
||||
|
||||
public string? SerialNumber { get; set; }
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Models;
|
||||
namespace LiteCharms.Features.TechShop.Products.Models;
|
||||
|
||||
public class ProductPrice
|
||||
{
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Models;
|
||||
namespace LiteCharms.Features.TechShop.Products.Models;
|
||||
|
||||
public record CreateProduct
|
||||
{
|
||||
@@ -11,4 +11,6 @@ public record CreateProduct
|
||||
public required string ImageUrl { get; set; }
|
||||
|
||||
public string[]? Thumbnails { get; set; }
|
||||
|
||||
public ProductMetadata? Metadata { get; set; }
|
||||
}
|
||||
+58
-9
@@ -1,8 +1,8 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.Shop.Products.Models;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Products.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Products;
|
||||
namespace LiteCharms.Features.TechShop.Products;
|
||||
|
||||
public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
@@ -67,10 +67,11 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
var newProduct = context.Products.Add(new Entities.Product
|
||||
{
|
||||
Name = request.Name,
|
||||
Summary = request.Summary,
|
||||
Summary = request.Summary,
|
||||
Description = request.Description,
|
||||
ImageUrl = request.ImageUrl,
|
||||
Thumbnails = request.Thumbnails
|
||||
Thumbnails = request.Thumbnails,
|
||||
Metadata = request.Metadata
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
@@ -89,12 +90,12 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
|
||||
var newProductPrice = context.ProductPrices.Add(new Entities.ProductPrice
|
||||
{
|
||||
Price = price,
|
||||
Discount = discount,
|
||||
ProductId = productId
|
||||
ProductId = productId
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
@@ -206,7 +207,7 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
|
||||
var result = await CreateProductPriceAsync(existingPrice.ProductId, price, discount, cancellationToken);
|
||||
|
||||
if(result.IsFailed)
|
||||
if (result.IsFailed)
|
||||
{
|
||||
var deactivatedPrice = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken);
|
||||
|
||||
@@ -225,4 +226,52 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result> SetProductPriceStatusAsync(Guid productPriceId, bool active, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var productPrice = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken);
|
||||
|
||||
if (productPrice is null)
|
||||
return Result.Fail($"Could not find product price with ID {productPriceId}");
|
||||
|
||||
productPrice.Active = active;
|
||||
productPrice.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to change product price status by ID {productPriceId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result> UpdateProductMetadataAsync(Guid productId, ProductMetadata metadata, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var product = await context.Products.FirstOrDefaultAsync(p => p.Id == productId, cancellationToken);
|
||||
|
||||
if (product is null)
|
||||
return Result.Fail($"Could not find product with ID {productId}");
|
||||
|
||||
product.Metadata = metadata;
|
||||
product.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to update product metadata by ID {productId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
using LiteCharms.Features.Shop.Customers.Entities;
|
||||
using LiteCharms.Features.Shop.Orders.Entities;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
using LiteCharms.Features.TechShop.Customers.Entities;
|
||||
using LiteCharms.Features.TechShop.Orders.Entities;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Quotes.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Quotes.Entities;
|
||||
|
||||
[EntityTypeConfiguration<QuoteConfiguration, Quote>]
|
||||
public class Quote : Models.Quote
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Quotes.Entities;
|
||||
namespace LiteCharms.Features.TechShop.Quotes.Entities;
|
||||
|
||||
public class QuoteConfiguration : IEntityTypeConfiguration<Quote>
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Quotes.Models;
|
||||
namespace LiteCharms.Features.TechShop.Quotes.Models;
|
||||
|
||||
public class Quote
|
||||
{
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.Shop.Quotes.Models;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using LiteCharms.Features.TechShop.Quotes.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Quotes;
|
||||
namespace LiteCharms.Features.TechShop.Quotes;
|
||||
|
||||
public class QuoteService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
using LiteCharms.Features.Shop.Customers.Entities;
|
||||
using LiteCharms.Features.Shop.Orders.Entities;
|
||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
||||
using LiteCharms.Features.TechShop.Customers.Entities;
|
||||
using LiteCharms.Features.TechShop.Orders.Entities;
|
||||
using LiteCharms.Features.TechShop.Quotes.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ShoppingCartConfiguration, ShoppingCart>]
|
||||
public class ShoppingCart : Models.ShoppingCart
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
public class ShoppingCartConfiguration : IEntityTypeConfiguration<ShoppingCart>
|
||||
{
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using LiteCharms.Features.Shop.Products.Entities;
|
||||
using LiteCharms.Features.TechShop.Products.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ShoppingCartItemConfiguration, ShoppingCartItem>]
|
||||
public class ShoppingCartItem : Models.ShoppingCartItem
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
public class ShoppingCartItemConfiguration : IEntityTypeConfiguration<ShoppingCartItem>
|
||||
{
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
using LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
using LiteCharms.Features.TechShop.CartPackages.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ShoppingCartPackageConfiguration, ShoppingCartPackage>]
|
||||
public class ShoppingCartPackage : Models.ShoppingCartPackage
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Entities;
|
||||
|
||||
public class ShoppingCartPackageConfiguration : IEntityTypeConfiguration<ShoppingCartPackage>
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Models;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Models;
|
||||
|
||||
public class ShoppingCart
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Models;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Models;
|
||||
|
||||
public class ShoppingCartItem
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts.Models;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts.Models;
|
||||
|
||||
public class ShoppingCartPackage
|
||||
{
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Models;
|
||||
using LiteCharms.Features.TechShop.Extensions;
|
||||
using LiteCharms.Features.TechShop.Postgres;
|
||||
using LiteCharms.Features.TechShop.ShoppingCarts.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.ShoppingCarts;
|
||||
namespace LiteCharms.Features.TechShop.ShoppingCarts;
|
||||
|
||||
public class ShoppingCartService(IDbContextFactory<ShopDbContext> contextFactory)
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace LiteCharms.Features.Tests;
|
||||
|
||||
public class CommonFixture : IDisposable
|
||||
public class Fixture : IDisposable
|
||||
{
|
||||
public IConfiguration Configuration { get; set; }
|
||||
|
||||
@@ -10,22 +10,22 @@ public class CommonFixture : IDisposable
|
||||
|
||||
public IMediator Mediator { get; set; }
|
||||
|
||||
public CommonFixture()
|
||||
public Fixture()
|
||||
{
|
||||
Configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddUserSecrets<CommonFixture>()
|
||||
.AddUserSecrets<Fixture>()
|
||||
.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"), optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
Services = new ServiceCollection()
|
||||
Services = new ServiceCollection()
|
||||
.AddMediator()
|
||||
.AddLogging()
|
||||
.AddShopServices()
|
||||
.AddLogging()
|
||||
.AddEmailServiceBus()
|
||||
.AddShopDatabase(Configuration)
|
||||
.AddGarageS3(Configuration)
|
||||
.AddEmailServices(Configuration)
|
||||
.AddSingleton(Configuration)
|
||||
.BuildServiceProvider();
|
||||
|
||||
Mediator = Services.GetRequiredService<IMediator>();
|
||||
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="10.0.0">
|
||||
<PackageReference Include="coverlet.collector" Version="10.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -27,10 +27,10 @@
|
||||
|
||||
<!-- Global Usings -->
|
||||
<ItemGroup>
|
||||
<Using Include="Mediator"/>
|
||||
<Using Include="Xunit.Abstractions"/>
|
||||
<Using Include="Microsoft.Extensions.DependencyInjection"/>
|
||||
<Using Include="Microsoft.Extensions.Configuration"/>
|
||||
<Using Include="Mediator" />
|
||||
<Using Include="Xunit.Abstractions" />
|
||||
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<Using Include="Microsoft.Extensions.Configuration" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using LiteCharms.Features.Shop.Notifications;
|
||||
|
||||
namespace LiteCharms.Features.Tests;
|
||||
|
||||
public class NotificationsFeatureTests(CommonFixture fixture, ITestOutputHelper output) : IClassFixture<CommonFixture>
|
||||
{
|
||||
private readonly NotificationService notificationService = fixture.Services.GetRequiredService<NotificationService>();
|
||||
|
||||
[Fact]
|
||||
public async Task CreateNotificationCommand_ShouldSucceed()
|
||||
{
|
||||
Shop.Notifications.Models.CreateNotification request = new()
|
||||
{
|
||||
CorrelationId = Guid.CreateVersion7().ToString(),
|
||||
CorrelationIdType = Shop.CorrelationIdTypes.None,
|
||||
Direction = Shop.NotificationDirection.Outgoing,
|
||||
Platform = Shop.NotificationPlatforms.Email,
|
||||
Priority = Shop.Priorities.Medium,
|
||||
Sender = "xUnit Test",
|
||||
SenderAddress = "khwezi@mngoma.africa",
|
||||
Recipient = $"{Email.Extensions.Constants.ShopEmailFromName} [Test]",
|
||||
RecipientAddress = Email.Extensions.Constants.ShopEmailFromAddress,
|
||||
Subject = "Test Message",
|
||||
Message = "This is an automation test",
|
||||
IsHtml = false,
|
||||
IsInternal = true,
|
||||
};
|
||||
|
||||
var createResult = await notificationService.CreateNotificationAsync(request);
|
||||
|
||||
Assert.True(createResult.IsSuccess);
|
||||
|
||||
foreach (var error in createResult.Errors) output.WriteLine(error.Message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using LiteCharms.Features.S3.Abstractions;
|
||||
|
||||
namespace LiteCharms.Features.Tests;
|
||||
|
||||
public class S3ServiceFeatureTests(Fixture fixture, ITestOutputHelper output) : IClassFixture<Fixture>
|
||||
{
|
||||
[Fact]
|
||||
public async Task BookshopS3Service_MustReturnUrl()
|
||||
{
|
||||
var service = fixture.Services.GetKeyedService<IS3Service>(S3.Constants.BookshopBucketName);
|
||||
|
||||
var fileName = "appsettings.json";
|
||||
|
||||
string path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
|
||||
|
||||
Assert.True(File.Exists(path));
|
||||
|
||||
var stream = File.OpenRead(path);
|
||||
|
||||
var result = await service!.UploadFileAsync(fileName, stream, MimeKit.MimeTypes.GetMimeType(fileName));
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.NotNull(result.Value);
|
||||
Assert.NotEmpty(result.Value);
|
||||
|
||||
output.WriteLine(result.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BookshopS3Service_MustDeleteFile()
|
||||
{
|
||||
var service = fixture.Services.GetKeyedService<IS3Service>(S3.Constants.BookshopBucketName);
|
||||
|
||||
var fileName = "appsettings.json";
|
||||
|
||||
string path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
|
||||
|
||||
Assert.True(File.Exists(path));
|
||||
|
||||
var stream = File.OpenRead(path);
|
||||
|
||||
var uploadResult = await service!.UploadFileAsync(fileName, stream, MimeKit.MimeTypes.GetMimeType(fileName));
|
||||
|
||||
Assert.True(uploadResult.IsSuccess);
|
||||
Assert.NotNull(uploadResult.Value);
|
||||
Assert.NotEmpty(uploadResult.Value);
|
||||
|
||||
var fileKey = uploadResult.Value.Split('/').Last();
|
||||
|
||||
var deleteResult = await service!.DeleteFileAsync(fileKey);
|
||||
|
||||
Assert.True(deleteResult.IsSuccess);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
{
|
||||
"BookshopS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
"BucketName": "bookshop",
|
||||
"CdnBaseUrl": "https://bookshop.cdn.khongisa.co.za"
|
||||
},
|
||||
"BookshopQuotesS3Settings": {
|
||||
"ServiceUrl": "http://192.168.1.177:30900",
|
||||
"Region": "garage",
|
||||
"BucketName": "bookshop.quotes",
|
||||
"CdnBaseUrl": "https://bookshop.quotes.cdn.khongisa.co.za"
|
||||
},
|
||||
"Email": {
|
||||
"Credentials": {
|
||||
"Username": "shop@litecharms.co.za"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user