Refactored database registration to allow postgres to use internal representations for afster performance
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using LiteCharms.Features.Extensions;
|
||||
using LiteCharms.Features.MidrandBooks.Abstractions;
|
||||
using LiteCharms.Features.MidrandBooks.Extensions;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Tests;
|
||||
@@ -11,6 +12,10 @@ public class Fixture : IDisposable
|
||||
|
||||
public IMediator Mediator { get; set; }
|
||||
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new();
|
||||
|
||||
public CancellationToken CancellationToken => cancellationTokenSource.Token;
|
||||
|
||||
public Fixture()
|
||||
{
|
||||
Configuration = new ConfigurationBuilder()
|
||||
@@ -23,12 +28,12 @@ public class Fixture : IDisposable
|
||||
Services = new ServiceCollection()
|
||||
.AddMediator()
|
||||
.AddLogging()
|
||||
//.AddMidrandShopServices()
|
||||
.AddEmailServiceBus()
|
||||
.AddGarageS3(Configuration)
|
||||
.AddMidrandShopDatabase(Configuration)
|
||||
.AddEmailServices(Configuration)
|
||||
.AddSingleton(Configuration)
|
||||
.AddShopServices()
|
||||
.BuildServiceProvider();
|
||||
|
||||
Mediator = Services.GetRequiredService<IMediator>();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace LiteCharms.Features.MidrandBooks.Tests;
|
||||
|
||||
public class IntegrationFactAttribute : FactAttribute
|
||||
{
|
||||
public IntegrationFactAttribute()
|
||||
{
|
||||
if(!Debugger.IsAttached)
|
||||
Skip = "This test requires the debugger to be attached.";
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="System.Diagnostics" />
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using LiteCharms.Features.MidrandBooks.Products;
|
||||
using LiteCharms.Features.MidrandBooks.Products.Models;
|
||||
using LiteCharms.Features.Models;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Tests;
|
||||
|
||||
public class ProductServiceFeatureTest(Fixture fixture, ITestOutputHelper output) : IClassFixture<Fixture>
|
||||
{
|
||||
private readonly ProductService productService = fixture.Services.GetRequiredService<ProductService>();
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task GetProductsAsync_ShouldReturn_RetultProducts()
|
||||
{
|
||||
var range = new DateRange
|
||||
{
|
||||
From = DateOnly.FromDateTime(DateTime.UtcNow.AddDays(-7)),
|
||||
To = DateOnly.FromDateTime(DateTime.UtcNow),
|
||||
MaxRecords = 1000
|
||||
};
|
||||
|
||||
var result = await productService.GetProductsAsync(0, range, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.NotEmpty(result.Value);
|
||||
|
||||
output.WriteLine(JsonSerializer.Serialize(result.Value));
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task UpdateProductStatusAsync_ShouldReturn_ResultTrue()
|
||||
{
|
||||
var result = await productService.UpdateProductStatusAsync(2, true, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task UpdateProductPriceStatusAsync_ShouldReturn_ResultTrue()
|
||||
{
|
||||
var result = await productService.UpdateProductPriceStatusAsync(2, true, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task CreateProductPriceAsync_Should_Return_NewProductPriceId()
|
||||
{
|
||||
var request = new CreateProductPrice
|
||||
{
|
||||
Amount = 29.99m,
|
||||
Discount = 0.00m
|
||||
};
|
||||
|
||||
var result = await productService.CreateProductPriceAsync(2, request, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess, "Product price creation should be successful.");
|
||||
Assert.True(result.Value > 0, "New ProductPriceId should be greater than 0.");
|
||||
|
||||
output.WriteLine($"Created ProductPriceId: {result.Value}");
|
||||
}
|
||||
|
||||
[IntegrationFact]
|
||||
public async Task CreateProductAsync_Result_Returns_ProductId()
|
||||
{
|
||||
var request = new CreateProduct
|
||||
{
|
||||
Name = "Systems Rewired",
|
||||
Description = "[Design], <Code>, AND /CHAORS/ IN ***SYNC***",
|
||||
Summary = "A comprehensive guide to systems thinking and design.",
|
||||
ImageUrl = "https://bookshop.cdn.khongisa.co.za/design/2bf1f9a2-7b25-4fcf-9aa7-08941ea21e6c_1764838499686.webp",
|
||||
Type = ProductTypes.Book,
|
||||
Categories = ["Systems Thinking", "Design", "Programming"],
|
||||
Metadata = new ProductMetadata
|
||||
{
|
||||
CopyrightInfo = "© 2024 John Doe. All rights reserved.",
|
||||
ManufactureDate = "2024-06-01",
|
||||
Manufacturer = "TechWave Publishing",
|
||||
SerialNumber = "SR-2024-0001"
|
||||
}
|
||||
};
|
||||
|
||||
var result = await productService.CreateProductAsync(request, fixture.CancellationToken);
|
||||
|
||||
Assert.True(result.IsSuccess, "Product creation should be successful.");
|
||||
Assert.True(result.Value > 0, "ProductId should be greater than 0.");
|
||||
|
||||
output.WriteLine($"Created ProductId: {result.Value}");
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,6 @@
|
||||
"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"
|
||||
|
||||
@@ -18,7 +18,8 @@ public sealed class AuthorConfiguration : IEntityTypeConfiguration<Author>
|
||||
builder.Property(f => f.Website).IsRequired(false).HasMaxLength(1024);
|
||||
builder.Property(f => f.ImageUrl).IsRequired().HasMaxLength(2048);
|
||||
builder.Property(f => f.ThumbnailImageUrl).IsRequired(false).HasMaxLength(2048);
|
||||
builder.Property(f => f.SocialMedia).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(f => f.Enabled).HasDefaultValue(true);
|
||||
|
||||
builder.OwnsMany(f => f.SocialMedia, b => { b.ToJson(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ public sealed class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
||||
builder.Property(c => c.Email).IsRequired();
|
||||
builder.Property(c => c.Phone).IsRequired();
|
||||
builder.Property(c => c.Website).IsRequired();
|
||||
builder.Property(c => c.SocialMedia).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(c => c.Enabled).HasDefaultValue(true);
|
||||
|
||||
builder.OwnsMany(f => f.SocialMedia, b => { b.ToJson(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,18 @@ public static class Postgres
|
||||
|
||||
public static IServiceCollection AddMidrandShopDatabase(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var connectionString = configuration.GetConnectionString(MidrandBooksDbConfigName);
|
||||
|
||||
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
|
||||
|
||||
dataSourceBuilder.ConfigureTypeLoading(options => { options.EnableTypeLoading(false); });
|
||||
|
||||
var dataSource = dataSourceBuilder.Build();
|
||||
|
||||
services.AddSingleton(dataSource);
|
||||
|
||||
services.AddPooledDbContextFactory<MidrandBooksDbContext>(options =>
|
||||
options.UseNpgsql(configuration.GetConnectionString(MidrandBooksDbConfigName)));
|
||||
options.UseNpgsql(dataSource));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -4,19 +4,15 @@ namespace LiteCharms.Features.MidrandBooks.Extensions;
|
||||
|
||||
public static class Shop
|
||||
{
|
||||
public static IServiceCollection AddShopServices(this IServiceCollection services, Assembly assembly, ServiceLifetime serviceLifetime)
|
||||
public static IServiceCollection AddShopServices(this IServiceCollection services)
|
||||
{
|
||||
var serviceType = typeof(IService);
|
||||
|
||||
var implementations = assembly.GetTypes()
|
||||
var implementations = Assembly.GetExecutingAssembly().GetTypes()
|
||||
.Where(t => serviceType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract);
|
||||
|
||||
foreach (var implementation in implementations)
|
||||
{
|
||||
var descriptor = new ServiceDescriptor(serviceType, implementation, serviceLifetime);
|
||||
|
||||
services.Add(descriptor);
|
||||
}
|
||||
foreach (var implementation in implementations)
|
||||
services.AddScoped(implementation);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -162,5 +162,8 @@
|
||||
<Using Include="Microsoft.Extensions.Options" />
|
||||
<Using Include="Microsoft.Extensions.Logging" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Postgres\Migrations\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -11,7 +11,7 @@ public sealed class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||
builder.Property(o => o.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(o => o.UpdatedAt).HasDefaultValueSql("now()");
|
||||
builder.Property(o => o.Status).IsRequired();
|
||||
builder.Property(o => o.Total).IsRequired().HasColumnType("decimal(18,2)");
|
||||
builder.Property(o => o.Total).IsRequired().HasPrecision(18, 2);
|
||||
builder.Property(o => o.Notes).HasMaxLength(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ public sealed class BookPageConfiguration : IEntityTypeConfiguration<BookPage>
|
||||
builder.Property(bp => bp.AuthorBookId).IsRequired();
|
||||
builder.Property(bp => bp.Content).IsRequired();
|
||||
builder.Property(bp => bp.Type).IsRequired();
|
||||
builder.Property(bp => bp.ContentType).IsRequired();
|
||||
builder.Property(bp => bp.Notes).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(bp => bp.References).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(bp => bp.ContentType).IsRequired();
|
||||
builder.Property(bp => bp.Enabled).HasDefaultValue(true);
|
||||
builder.Property(bp => bp.Notes).IsRequired(false);
|
||||
|
||||
builder.OwnsMany(f => f.References, b => { b.ToJson(); });
|
||||
|
||||
builder.HasOne(f =>f.Book)
|
||||
.WithMany(b => b.Pages)
|
||||
|
||||
+129
-21
@@ -1,7 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.MidrandBooks.Postgres;
|
||||
using LiteCharms.Features.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
@@ -13,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(MidrandBooksDbContext))]
|
||||
[Migration("20260527070840_Init")]
|
||||
[Migration("20260528052014_Init")]
|
||||
partial class Init
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -112,9 +111,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Property<int>("PublisherType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<SocialMedia[]>("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("ThumbnailImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
@@ -291,9 +287,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<SocialMedia[]>("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
@@ -338,7 +331,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<decimal>("Total")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -494,17 +488,14 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.PrimitiveCollection<string>("Notes")
|
||||
.HasColumnType("jsonb");
|
||||
b.PrimitiveCollection<string[]>("Notes")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<PageReference[]>("References")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@@ -570,8 +561,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.PrimitiveCollection<string>("Categories")
|
||||
.HasColumnType("jsonb");
|
||||
b.PrimitiveCollection<string[]>("Categories")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -591,9 +582,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)");
|
||||
|
||||
b.Property<ProductMetadata>("Metadata")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
@@ -604,8 +592,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.PrimitiveCollection<string>("ThumbnailUrls")
|
||||
.HasColumnType("jsonb");
|
||||
b.PrimitiveCollection<string[]>("ThumbnailUrls")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
@@ -714,6 +702,38 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", b =>
|
||||
{
|
||||
b.OwnsMany("LiteCharms.Features.Models.SocialMedia", "SocialMedia", b1 =>
|
||||
{
|
||||
b1.Property<long>("AuthorId");
|
||||
|
||||
b1.Property<int>("__synthesizedOrdinal")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b1.Property<string>("ImageUrl");
|
||||
|
||||
b1.Property<string>("Name");
|
||||
|
||||
b1.Property<int>("Type");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("AuthorId", "__synthesizedOrdinal");
|
||||
|
||||
b1.ToTable("Authors");
|
||||
|
||||
b1
|
||||
.ToJson("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("AuthorId");
|
||||
});
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", "Customer")
|
||||
@@ -736,6 +756,38 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", b =>
|
||||
{
|
||||
b.OwnsMany("LiteCharms.Features.Models.SocialMedia", "SocialMedia", b1 =>
|
||||
{
|
||||
b1.Property<long>("CustomerId");
|
||||
|
||||
b1.Property<int>("__synthesizedOrdinal")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b1.Property<string>("ImageUrl");
|
||||
|
||||
b1.Property<string>("Name");
|
||||
|
||||
b1.Property<int>("Type");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("CustomerId", "__synthesizedOrdinal");
|
||||
|
||||
b1.ToTable("Customers");
|
||||
|
||||
b1
|
||||
.ToJson("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CustomerId");
|
||||
});
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.OrderItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", "AuthorBook")
|
||||
@@ -798,7 +850,34 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.OnDelete(DeleteBehavior.NoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsMany("LiteCharms.Features.Models.PageReference", "References", b1 =>
|
||||
{
|
||||
b1.Property<long>("BookPageId");
|
||||
|
||||
b1.Property<int>("__synthesizedOrdinal")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b1.Property<string>("Description");
|
||||
|
||||
b1.Property<string>("Tag");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("BookPageId", "__synthesizedOrdinal");
|
||||
|
||||
b1.ToTable("BookPages");
|
||||
|
||||
b1
|
||||
.ToJson("References")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("BookPageId");
|
||||
});
|
||||
|
||||
b.Navigation("Book");
|
||||
|
||||
b.Navigation("References");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Payments.Entities.Refund", b =>
|
||||
@@ -812,6 +891,35 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.Product", b =>
|
||||
{
|
||||
b.OwnsOne("LiteCharms.Features.Models.ProductMetadata", "Metadata", b1 =>
|
||||
{
|
||||
b1.Property<long>("ProductId");
|
||||
|
||||
b1.Property<string>("CopyrightInfo");
|
||||
|
||||
b1.Property<string>("ManufactureDate");
|
||||
|
||||
b1.Property<string>("Manufacturer");
|
||||
|
||||
b1.Property<string>("SerialNumber");
|
||||
|
||||
b1.HasKey("ProductId");
|
||||
|
||||
b1.ToTable("Products");
|
||||
|
||||
b1
|
||||
.ToJson("Metadata")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ProductId");
|
||||
});
|
||||
|
||||
b.Navigation("Metadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", "Product")
|
||||
+35
-36
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using LiteCharms.Features.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
@@ -31,8 +30,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
Website = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
ImageUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: false),
|
||||
ThumbnailImageUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||
SocialMedia = table.Column<SocialMedia[]>(type: "jsonb", nullable: true),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true)
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
|
||||
SocialMedia = table.Column<string>(type: "jsonb", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@@ -52,8 +51,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
Email = table.Column<string>(type: "text", nullable: false),
|
||||
Website = table.Column<string>(type: "text", nullable: false),
|
||||
Phone = table.Column<string>(type: "text", nullable: false),
|
||||
SocialMedia = table.Column<SocialMedia[]>(type: "jsonb", nullable: true),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true)
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
|
||||
SocialMedia = table.Column<string>(type: "jsonb", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@@ -70,7 +69,7 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true, defaultValueSql: "now()"),
|
||||
CustomerId = table.Column<long>(type: "bigint", nullable: false),
|
||||
Status = table.Column<int>(type: "integer", nullable: false),
|
||||
Total = table.Column<decimal>(type: "numeric(18,2)", nullable: false),
|
||||
Total = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
Notes = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
InvoiceUrl = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
@@ -92,10 +91,10 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
Summary = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
ImageUrl = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||
ThumbnailUrls = table.Column<string>(type: "jsonb", nullable: true),
|
||||
Categories = table.Column<string>(type: "jsonb", nullable: true),
|
||||
Metadata = table.Column<ProductMetadata>(type: "jsonb", nullable: true),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false)
|
||||
ThumbnailUrls = table.Column<string[]>(type: "text[]", nullable: true),
|
||||
Categories = table.Column<string[]>(type: "text[]", nullable: true),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||
Metadata = table.Column<string>(type: "jsonb", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@@ -260,29 +259,29 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProductPrice",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
ProductId = table.Column<long>(type: "bigint", nullable: false),
|
||||
Amount = table.Column<decimal>(type: "numeric", nullable: false),
|
||||
Discount = table.Column<decimal>(type: "numeric", nullable: false),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProductPrice", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProductPrice_Products_ProductId",
|
||||
column: x => x.ProductId,
|
||||
principalTable: "Products",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
//migrationBuilder.CreateTable(
|
||||
// name: "ProductPrice",
|
||||
// columns: table => new
|
||||
// {
|
||||
// Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
// .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
// CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
// UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
// ProductId = table.Column<long>(type: "bigint", nullable: false),
|
||||
// Amount = table.Column<decimal>(type: "numeric", nullable: false),
|
||||
// Discount = table.Column<decimal>(type: "numeric", nullable: false),
|
||||
// Enabled = table.Column<bool>(type: "boolean", nullable: false)
|
||||
// },
|
||||
// constraints: table =>
|
||||
// {
|
||||
// table.PrimaryKey("PK_ProductPrice", x => x.Id);
|
||||
// table.ForeignKey(
|
||||
// name: "FK_ProductPrice_Products_ProductId",
|
||||
// column: x => x.ProductId,
|
||||
// principalTable: "Products",
|
||||
// principalColumn: "Id",
|
||||
// onDelete: ReferentialAction.Cascade);
|
||||
// });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Shippings",
|
||||
@@ -334,9 +333,9 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
ContentType = table.Column<int>(type: "integer", nullable: false),
|
||||
Number = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
|
||||
Content = table.Column<byte[]>(type: "bytea", nullable: false),
|
||||
Notes = table.Column<string>(type: "jsonb", nullable: true),
|
||||
References = table.Column<PageReference[]>(type: "jsonb", nullable: true),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true)
|
||||
Notes = table.Column<string[]>(type: "text[]", nullable: true),
|
||||
Enabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
|
||||
References = table.Column<string>(type: "jsonb", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
+128
-20
@@ -1,7 +1,6 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.MidrandBooks.Postgres;
|
||||
using LiteCharms.Features.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
@@ -109,9 +108,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Property<int>("PublisherType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<SocialMedia[]>("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("ThumbnailImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
@@ -288,9 +284,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<SocialMedia[]>("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
@@ -335,7 +328,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<decimal>("Total")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -491,17 +485,14 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.PrimitiveCollection<string>("Notes")
|
||||
.HasColumnType("jsonb");
|
||||
b.PrimitiveCollection<string[]>("Notes")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<PageReference[]>("References")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@@ -567,8 +558,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.PrimitiveCollection<string>("Categories")
|
||||
.HasColumnType("jsonb");
|
||||
b.PrimitiveCollection<string[]>("Categories")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -588,9 +579,6 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)");
|
||||
|
||||
b.Property<ProductMetadata>("Metadata")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
@@ -601,8 +589,8 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.PrimitiveCollection<string>("ThumbnailUrls")
|
||||
.HasColumnType("jsonb");
|
||||
b.PrimitiveCollection<string[]>("ThumbnailUrls")
|
||||
.HasColumnType("text[]");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
@@ -711,6 +699,38 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Authors.Entities.Author", b =>
|
||||
{
|
||||
b.OwnsMany("LiteCharms.Features.Models.SocialMedia", "SocialMedia", b1 =>
|
||||
{
|
||||
b1.Property<long>("AuthorId");
|
||||
|
||||
b1.Property<int>("__synthesizedOrdinal")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b1.Property<string>("ImageUrl");
|
||||
|
||||
b1.Property<string>("Name");
|
||||
|
||||
b1.Property<int>("Type");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("AuthorId", "__synthesizedOrdinal");
|
||||
|
||||
b1.ToTable("Authors");
|
||||
|
||||
b1
|
||||
.ToJson("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("AuthorId");
|
||||
});
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Address", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", "Customer")
|
||||
@@ -733,6 +753,38 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Customers.Entities.Customer", b =>
|
||||
{
|
||||
b.OwnsMany("LiteCharms.Features.Models.SocialMedia", "SocialMedia", b1 =>
|
||||
{
|
||||
b1.Property<long>("CustomerId");
|
||||
|
||||
b1.Property<int>("__synthesizedOrdinal")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b1.Property<string>("ImageUrl");
|
||||
|
||||
b1.Property<string>("Name");
|
||||
|
||||
b1.Property<int>("Type");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("CustomerId", "__synthesizedOrdinal");
|
||||
|
||||
b1.ToTable("Customers");
|
||||
|
||||
b1
|
||||
.ToJson("SocialMedia")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CustomerId");
|
||||
});
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Orders.Entities.OrderItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.MidrandBooks.AuthorBooks.Entities.AuthorBook", "AuthorBook")
|
||||
@@ -795,7 +847,34 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
.OnDelete(DeleteBehavior.NoAction)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsMany("LiteCharms.Features.Models.PageReference", "References", b1 =>
|
||||
{
|
||||
b1.Property<long>("BookPageId");
|
||||
|
||||
b1.Property<int>("__synthesizedOrdinal")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b1.Property<string>("Description");
|
||||
|
||||
b1.Property<string>("Tag");
|
||||
|
||||
b1.Property<string>("Url");
|
||||
|
||||
b1.HasKey("BookPageId", "__synthesizedOrdinal");
|
||||
|
||||
b1.ToTable("BookPages");
|
||||
|
||||
b1
|
||||
.ToJson("References")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("BookPageId");
|
||||
});
|
||||
|
||||
b.Navigation("Book");
|
||||
|
||||
b.Navigation("References");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Payments.Entities.Refund", b =>
|
||||
@@ -809,6 +888,35 @@ namespace LiteCharms.Features.MidrandBooks.Postgres.Migrations
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.Product", b =>
|
||||
{
|
||||
b.OwnsOne("LiteCharms.Features.Models.ProductMetadata", "Metadata", b1 =>
|
||||
{
|
||||
b1.Property<long>("ProductId");
|
||||
|
||||
b1.Property<string>("CopyrightInfo");
|
||||
|
||||
b1.Property<string>("ManufactureDate");
|
||||
|
||||
b1.Property<string>("Manufacturer");
|
||||
|
||||
b1.Property<string>("SerialNumber");
|
||||
|
||||
b1.HasKey("ProductId");
|
||||
|
||||
b1.ToTable("Products");
|
||||
|
||||
b1
|
||||
.ToJson("Metadata")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ProductId");
|
||||
});
|
||||
|
||||
b.Navigation("Metadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Features.MidrandBooks.Products.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Features.MidrandBooks.Products.Entities.Product", "Product")
|
||||
|
||||
@@ -14,9 +14,10 @@ public sealed class ProductConfiguration : IEntityTypeConfiguration<Product>
|
||||
builder.Property(f => f.Summary).IsRequired().HasMaxLength(512);
|
||||
builder.Property(f => f.Description).HasMaxLength(1024);
|
||||
builder.Property(f => f.ImageUrl).HasMaxLength(1024);
|
||||
builder.Property(f => f.ThumbnailUrls).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(f => f.Metadata).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(f => f.Categories).IsRequired(false).HasColumnType("jsonb");
|
||||
builder.Property(f => f.Enabled).HasDefaultValue(false);
|
||||
builder.Property(f => f.Categories).IsRequired(false);
|
||||
builder.Property(f => f.ThumbnailUrls).IsRequired(false);
|
||||
|
||||
builder.OwnsOne(f => f.Metadata, b => { b.ToJson(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
[EntityTypeConfiguration<ProductPriceConfiguration, ProductPrice>]
|
||||
public class ProductPrice : Models.ProductPrice
|
||||
{
|
||||
public virtual Product Product { get; set; } = new();
|
||||
public virtual Product? Product { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace LiteCharms.Features.MidrandBooks.Products.Models;
|
||||
|
||||
public sealed class CreateProductPrice
|
||||
{
|
||||
public long ProductId { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
public decimal Discount { get; set; }
|
||||
}
|
||||
@@ -20,3 +20,10 @@ public sealed record CreateProduct
|
||||
|
||||
public ProductMetadata? Metadata { get; set; }
|
||||
}
|
||||
|
||||
public sealed class CreateProductPrice
|
||||
{
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
public decimal Discount { get; set; }
|
||||
}
|
||||
|
||||
@@ -228,8 +228,8 @@ public sealed class ProductService(IDbContextFactory<MidrandBooksDbContext> cont
|
||||
{
|
||||
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);
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
@@ -244,7 +244,7 @@ public sealed class ProductService(IDbContextFactory<MidrandBooksDbContext> cont
|
||||
.AsSplitQuery()
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
return products?.Length > 0
|
||||
? Result.Ok(products.Select(p => p.ToModel()).ToArray())
|
||||
: Result.Fail<Product[]>(new Error("Failed to retrieve products."));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user