From 9d60441c77228f22ca7f5f6a5fd67c3f9936ae5a Mon Sep 17 00:00:00 2001 From: Ateeb Date: Sun, 2 Feb 2025 21:40:01 +0100 Subject: [PATCH 1/2] core --- .../DTO/CustomerNoListOrderDTO.cs | 12 ++ exercise.pizzashopapi/DTO/OrderDTO.cs | 26 +++ exercise.pizzashopapi/DTO/PizzaDTO.cs | 12 ++ .../DTO/PizzaNoListOrderDTO.cs | 9 + exercise.pizzashopapi/Data/DataContext.cs | 3 + exercise.pizzashopapi/Data/Seeder.cs | 47 ++++- .../EndPoints/PizzaShopApi.cs | 49 ++++- .../20250127110031_InititalCreate.Designer.cs | 194 ++++++++++++++++++ .../20250127110031_InititalCreate.cs | 146 +++++++++++++ .../Migrations/DataContextModelSnapshot.cs | 191 +++++++++++++++++ exercise.pizzashopapi/Models/Customer.cs | 11 +- exercise.pizzashopapi/Models/Order.cs | 28 ++- exercise.pizzashopapi/Models/OrderTopping.cs | 26 +++ exercise.pizzashopapi/Models/Pizza.cs | 14 +- exercise.pizzashopapi/Models/Toppings.cs | 21 ++ exercise.pizzashopapi/Program.cs | 11 +- .../Repository/IRepository.cs | 21 +- .../Repository/Repository.cs | 94 ++++++++- exercise.pizzashopapi/Tools/Mapper.cs | 34 +++ .../exercise.pizzashopapi.csproj | 15 +- 20 files changed, 926 insertions(+), 38 deletions(-) create mode 100644 exercise.pizzashopapi/DTO/CustomerNoListOrderDTO.cs create mode 100644 exercise.pizzashopapi/DTO/OrderDTO.cs create mode 100644 exercise.pizzashopapi/DTO/PizzaDTO.cs create mode 100644 exercise.pizzashopapi/DTO/PizzaNoListOrderDTO.cs create mode 100644 exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs create mode 100644 exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs create mode 100644 exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs create mode 100644 exercise.pizzashopapi/Models/OrderTopping.cs create mode 100644 exercise.pizzashopapi/Models/Toppings.cs create mode 100644 exercise.pizzashopapi/Tools/Mapper.cs diff --git a/exercise.pizzashopapi/DTO/CustomerNoListOrderDTO.cs b/exercise.pizzashopapi/DTO/CustomerNoListOrderDTO.cs new file mode 100644 index 0000000..30e15d6 --- /dev/null +++ b/exercise.pizzashopapi/DTO/CustomerNoListOrderDTO.cs @@ -0,0 +1,12 @@ +using exercise.pizzashopapi.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.DTO +{ + public class CustomerNoListOrderDTO + { + public int Id { get; set; } + public string? Name { get; set; } + + } +} diff --git a/exercise.pizzashopapi/DTO/OrderDTO.cs b/exercise.pizzashopapi/DTO/OrderDTO.cs new file mode 100644 index 0000000..1d71fac --- /dev/null +++ b/exercise.pizzashopapi/DTO/OrderDTO.cs @@ -0,0 +1,26 @@ +using exercise.pizzashopapi.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.DTO +{ + public class OrderDTO + { + public int Id { get; set; } + + public int CustomerId { get; set; } + + public int PizzaId { get; set; } + + public CustomerNoListOrderDTO Customer { get; set; } + + public PizzaNoListOrderDTO Pizza { get; set; } + + //public List OrderToppings { get; set; } = new List(); + + [NotMapped] + public List OrderStatus { get; } = new List + { + "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" + }; + } +} diff --git a/exercise.pizzashopapi/DTO/PizzaDTO.cs b/exercise.pizzashopapi/DTO/PizzaDTO.cs new file mode 100644 index 0000000..b1a75dd --- /dev/null +++ b/exercise.pizzashopapi/DTO/PizzaDTO.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.DTO +{ + public class PizzaDTO + { + public int Id { get; set; } + public string Name { get; set; } + + public decimal Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/PizzaNoListOrderDTO.cs b/exercise.pizzashopapi/DTO/PizzaNoListOrderDTO.cs new file mode 100644 index 0000000..4cf85e0 --- /dev/null +++ b/exercise.pizzashopapi/DTO/PizzaNoListOrderDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class PizzaNoListOrderDTO + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/Data/DataContext.cs b/exercise.pizzashopapi/Data/DataContext.cs index 129199e..d323e20 100644 --- a/exercise.pizzashopapi/Data/DataContext.cs +++ b/exercise.pizzashopapi/Data/DataContext.cs @@ -24,5 +24,8 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) public DbSet Pizzas { get; set; } public DbSet Customers { get; set; } public DbSet Orders { get; set; } + public DbSet Toppings { get; set; } + public DbSet OrderToppings { get; set; } + } } diff --git a/exercise.pizzashopapi/Data/Seeder.cs b/exercise.pizzashopapi/Data/Seeder.cs index f87fbef..9fd159e 100644 --- a/exercise.pizzashopapi/Data/Seeder.cs +++ b/exercise.pizzashopapi/Data/Seeder.cs @@ -6,28 +6,63 @@ public static class Seeder { public async static void SeedPizzaShopApi(this WebApplication app) { - using(var db = new DataContext()) + using (var db = new DataContext()) { + // Seed Customers if(!db.Customers.Any()) { - db.Add(new Customer() { Name="Nigel" }); + db.Add(new Customer() { Name = "Nigel" }); db.Add(new Customer() { Name = "Dave" }); await db.SaveChangesAsync(); } + + // Seed Pizzas if(!db.Pizzas.Any()) { - db.Add(new Pizza() { Name = "Cheese & Pineapple" }); - db.Add(new Pizza() { Name = "Vegan Cheese Tastic" }); + db.Add(new Pizza() { Name = "Cheese & Pineapple", Price = 150 }); + db.Add(new Pizza() { Name = "Vegan Cheese Tastic", Price = 120 }); await db.SaveChangesAsync(); + } + // Seed Toppings + if(!db.Toppings.Any()) + { + db.Add(new Toppings() { Type = "Cheese" }); + db.Add(new Toppings() { Type = "Pineapple" }); + await db.SaveChangesAsync(); } - //order data - if(1==1) + // Seed Orders + if(!db.Orders.Any()) { + db.Add(new Order() + { + Id = 1, + CustomerId = 1, + PizzaId = 1, + OrderToppings = new List { new OrderTopping { ToppingId = 2 } }, + OrderStatus = {"Baking"} + }); + + db.Add(new Order() + { + Id = 2, + CustomerId = 2, + PizzaId = 2, + OrderToppings = new List { new OrderTopping { ToppingId = 1 } }, + OrderStatus = { "Preparing" } + }); await db.SaveChangesAsync(); } + + // Seed Order Toppings (Many-to-Many Relationship) + if(!db.OrderToppings.Any()) + { + db.Add(new OrderTopping() { Id = 1, OrderId = 1, ToppingId = 1 }); + db.Add(new OrderTopping() { Id = 2, OrderId = 2, ToppingId = 2 }); + await db.SaveChangesAsync(); + } } } } diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs index f8be2b0..ba68095 100644 --- a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs +++ b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs @@ -1,5 +1,9 @@ -using exercise.pizzashopapi.Repository; +using AutoMapper; +using exercise.pizzashopapi.DTO; +using exercise.pizzashopapi.Models; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using workshop.wwwapi.Repository; namespace exercise.pizzashopapi.EndPoints { @@ -7,9 +11,48 @@ public static class PizzaShopApi { public static void ConfigurePizzaShopApi(this WebApplication app) { - + var pizza = app.MapGroup("shop"); + + pizza.MapGet("/pizza", GetPizzas); + pizza.MapGet("/order", GetOrders); + //pizza.MapGet("/orderByCustomer{id}", GetOrderByCustomerId); + + //pizza.MapPost("/orderforCustomer", AddOrderForCustomer); + //pizza.MapPost("/pizza", AddPizza); + //pizza.MapPost("/topping", AddTopping); + + + //pizza.MapPut("/updateOrder{id}", UpdateOrder); + //pizza.MapPut("/updatePizza{id}", UpdatePizza); + + //pizza.MapDelete("/deleteOrder{id}", DeleteOrder); } - + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetPizzas(IRepository repository, IMapper mapper) + { + var pizza = await repository.GetWithNestedIncludes(query => + query.Include(p => p.Orders) + .ThenInclude(a => a.Customer) + ); + + var response = mapper.Map>(pizza); + + return TypedResults.Ok(response); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetOrders(IRepository repository, IMapper mapper) + { + var orders = await repository.GetWithNestedIncludes(query => + query.Include(o => o.Customer) + .Include(o => o.Pizza) + ); + + var response = mapper.Map>(orders); + return TypedResults.Ok(response); + } + + } } diff --git a/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs b/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs new file mode 100644 index 0000000..add3432 --- /dev/null +++ b/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs @@ -0,0 +1,194 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using exercise.pizzashopapi.Data; + +#nullable disable + +namespace exercise.pizzashopapi.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250127110031_InititalCreate")] + partial class InititalCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("customer"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CustomerId") + .HasColumnType("integer") + .HasColumnName("customer_id"); + + b.Property("PizzaId") + .HasColumnType("integer") + .HasColumnName("pizza_id"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("PizzaId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.OrderTopping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("OrderId") + .HasColumnType("integer") + .HasColumnName("order_id"); + + b.Property("ToppingId") + .HasColumnType("integer") + .HasColumnName("topping_id"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ToppingId"); + + b.ToTable("toppingOrder"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Price") + .HasColumnType("numeric") + .HasColumnName("price"); + + b.HasKey("Id"); + + b.ToTable("pizza"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Toppings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Type") + .IsRequired() + .HasColumnType("text") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.ToTable("toppings"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.HasOne("exercise.pizzashopapi.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("exercise.pizzashopapi.Models.Pizza", "Pizza") + .WithMany("Orders") + .HasForeignKey("PizzaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Pizza"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.OrderTopping", b => + { + b.HasOne("exercise.pizzashopapi.Models.Order", "Order") + .WithMany("OrderToppings") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("exercise.pizzashopapi.Models.Toppings", "Topping") + .WithMany("ToppingOrders") + .HasForeignKey("ToppingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Topping"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.Navigation("OrderToppings"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Toppings", b => + { + b.Navigation("ToppingOrders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs b/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs new file mode 100644 index 0000000..4454571 --- /dev/null +++ b/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs @@ -0,0 +1,146 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace exercise.pizzashopapi.Migrations +{ + /// + public partial class InititalCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "customer", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_customer", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "pizza", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "text", nullable: false), + price = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_pizza", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "toppings", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + type = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_toppings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + customer_id = table.Column(type: "integer", nullable: false), + pizza_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + table.ForeignKey( + name: "FK_Orders_customer_customer_id", + column: x => x.customer_id, + principalTable: "customer", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Orders_pizza_pizza_id", + column: x => x.pizza_id, + principalTable: "pizza", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "toppingOrder", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + topping_id = table.Column(type: "integer", nullable: false), + order_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_toppingOrder", x => x.Id); + table.ForeignKey( + name: "FK_toppingOrder_Orders_order_id", + column: x => x.order_id, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_toppingOrder_toppings_topping_id", + column: x => x.topping_id, + principalTable: "toppings", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Orders_customer_id", + table: "Orders", + column: "customer_id"); + + migrationBuilder.CreateIndex( + name: "IX_Orders_pizza_id", + table: "Orders", + column: "pizza_id"); + + migrationBuilder.CreateIndex( + name: "IX_toppingOrder_order_id", + table: "toppingOrder", + column: "order_id"); + + migrationBuilder.CreateIndex( + name: "IX_toppingOrder_topping_id", + table: "toppingOrder", + column: "topping_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "toppingOrder"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "toppings"); + + migrationBuilder.DropTable( + name: "customer"); + + migrationBuilder.DropTable( + name: "pizza"); + } + } +} diff --git a/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs b/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs new file mode 100644 index 0000000..3242d83 --- /dev/null +++ b/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs @@ -0,0 +1,191 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using exercise.pizzashopapi.Data; + +#nullable disable + +namespace exercise.pizzashopapi.Migrations +{ + [DbContext(typeof(DataContext))] + partial class DataContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("customer"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CustomerId") + .HasColumnType("integer") + .HasColumnName("customer_id"); + + b.Property("PizzaId") + .HasColumnType("integer") + .HasColumnName("pizza_id"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("PizzaId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.OrderTopping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("OrderId") + .HasColumnType("integer") + .HasColumnName("order_id"); + + b.Property("ToppingId") + .HasColumnType("integer") + .HasColumnName("topping_id"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ToppingId"); + + b.ToTable("toppingOrder"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Price") + .HasColumnType("numeric") + .HasColumnName("price"); + + b.HasKey("Id"); + + b.ToTable("pizza"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Toppings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Type") + .IsRequired() + .HasColumnType("text") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.ToTable("toppings"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.HasOne("exercise.pizzashopapi.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("exercise.pizzashopapi.Models.Pizza", "Pizza") + .WithMany("Orders") + .HasForeignKey("PizzaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Pizza"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.OrderTopping", b => + { + b.HasOne("exercise.pizzashopapi.Models.Order", "Order") + .WithMany("OrderToppings") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("exercise.pizzashopapi.Models.Toppings", "Topping") + .WithMany("ToppingOrders") + .HasForeignKey("ToppingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Topping"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.Navigation("OrderToppings"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Toppings", b => + { + b.Navigation("ToppingOrders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/exercise.pizzashopapi/Models/Customer.cs b/exercise.pizzashopapi/Models/Customer.cs index 2ca83bd..84e806c 100644 --- a/exercise.pizzashopapi/Models/Customer.cs +++ b/exercise.pizzashopapi/Models/Customer.cs @@ -1,10 +1,17 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { + [Table("customer")] public class Customer { + [Key] public int Id { get; set; } - public string Name { get; set; } + + [Column("name")] + public string? Name { get; set; } + + public List Orders { get; set; } = new List(); } } diff --git a/exercise.pizzashopapi/Models/Order.cs b/exercise.pizzashopapi/Models/Order.cs index fbe6113..e6b107f 100644 --- a/exercise.pizzashopapi/Models/Order.cs +++ b/exercise.pizzashopapi/Models/Order.cs @@ -1,10 +1,32 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { public class Order { - - + [Key] + public int Id { get; set; } + + [Column("customer_id")] + [ForeignKey("customer")] + public int CustomerId { get; set; } + + + [Column("pizza_id")] + [ForeignKey("pizza")] + public int PizzaId { get; set; } + + public Customer Customer { get; set; } + + public Pizza Pizza { get; set; } + + public List OrderToppings { get; set; } = new List(); + + [NotMapped] + public List OrderStatus { get; } = new List + { + "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" + }; } } diff --git a/exercise.pizzashopapi/Models/OrderTopping.cs b/exercise.pizzashopapi/Models/OrderTopping.cs new file mode 100644 index 0000000..2991f6d --- /dev/null +++ b/exercise.pizzashopapi/Models/OrderTopping.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + [Table("toppingOrder")] + public class OrderTopping + { + + [Key] + public int Id { get; set; } + + [Column("topping_id")] + [ForeignKey("toppings")] + public int ToppingId { get; set; } + + + [Column("order_id")] + [ForeignKey("order")] + public int OrderId { get; set; } + + + public Toppings Topping { get; set; } + public Order Order { get; set; } + } +} diff --git a/exercise.pizzashopapi/Models/Pizza.cs b/exercise.pizzashopapi/Models/Pizza.cs index 5c085ec..0982bd9 100644 --- a/exercise.pizzashopapi/Models/Pizza.cs +++ b/exercise.pizzashopapi/Models/Pizza.cs @@ -1,12 +1,20 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { - + [Table("pizza")] public class Pizza - { + { + [Key] public int Id { get; set; } + + [Column("name")] public string Name { get; set; } + + [Column("price")] public decimal Price { get; set; } + + public List Orders { get; set; } = new List(); } } \ No newline at end of file diff --git a/exercise.pizzashopapi/Models/Toppings.cs b/exercise.pizzashopapi/Models/Toppings.cs new file mode 100644 index 0000000..5cd304b --- /dev/null +++ b/exercise.pizzashopapi/Models/Toppings.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace exercise.pizzashopapi.Models +{ + [Table("toppings")] + public class Toppings + { + [Key] + public int Id { get; set; } + + + [Column("type")] + public string Type { get; set; } + + public List ToppingOrders { get; set; } = new List(); + + + } +} diff --git a/exercise.pizzashopapi/Program.cs b/exercise.pizzashopapi/Program.cs index c04a440..cb178e9 100644 --- a/exercise.pizzashopapi/Program.cs +++ b/exercise.pizzashopapi/Program.cs @@ -1,14 +1,19 @@ using exercise.pizzashopapi.Data; using exercise.pizzashopapi.EndPoints; -using exercise.pizzashopapi.Repository; +using exercise.pizzashopapi.Models; +using workshop.wwwapi.Repository; var builder = WebApplication.CreateBuilder(args); // Add services to the container. - builder.Services.AddControllers(); -builder.Services.AddScoped(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); builder.Services.AddDbContext(); +builder.Services.AddAutoMapper(typeof(Program)); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); diff --git a/exercise.pizzashopapi/Repository/IRepository.cs b/exercise.pizzashopapi/Repository/IRepository.cs index b114ea8..d864a74 100644 --- a/exercise.pizzashopapi/Repository/IRepository.cs +++ b/exercise.pizzashopapi/Repository/IRepository.cs @@ -1,11 +1,22 @@ -using exercise.pizzashopapi.Models; +using System.Linq.Expressions; -namespace exercise.pizzashopapi.Repository +namespace workshop.wwwapi.Repository { - public interface IRepository + public interface IRepository { - Task> GetOrdersByCustomer(int id); - + Task> Get(); + Task GetById(int id); + + Task Add(T entity); + Task Update(T entity); + Task Delete(object id); + Task Save(); + Task> GetWithIncludes(params Expression>[] includes); + Task GetByIdWithIncludes(int id, params Expression>[] includes); + Task> GetWithNestedIncludes(params Func, IQueryable>[] includeActions); + + IQueryable GetQuery(); + } } diff --git a/exercise.pizzashopapi/Repository/Repository.cs b/exercise.pizzashopapi/Repository/Repository.cs index e109fce..537cf58 100644 --- a/exercise.pizzashopapi/Repository/Repository.cs +++ b/exercise.pizzashopapi/Repository/Repository.cs @@ -1,14 +1,96 @@ -using exercise.pizzashopapi.Data; -using exercise.pizzashopapi.Models; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using exercise.pizzashopapi.Data; +using Microsoft.EntityFrameworkCore; -namespace exercise.pizzashopapi.Repository + +namespace workshop.wwwapi.Repository { - public class Repository : IRepository + public class Repository : IRepository where T : class { private DataContext _db; - public Task> GetOrdersByCustomer(int id) + private DbSet _table = null!; + public Repository(DataContext db) + { + _db = db; + _table = _db.Set(); + } + + public async Task Add(T entity) + { + _table.Add(entity); + _db.SaveChangesAsync(); + return entity; + } + + public async Task Delete(object id) + { + T existing = _table.Find(id); + _table.Remove(existing); + _db.SaveChanges(); + return existing; + } + + public async Task> Get() + { + return _table.ToList(); + } + + public async Task GetById(int id) + { + return _table.Find(id); + } + + public async Task> GetWithIncludes(params Expression>[] includes) + { + IQueryable query = _table; + foreach (var include in includes) + { + query = query.Include(include); + } + return await query.ToListAsync(); + } + + public async Task> GetWithNestedIncludes(params Func, IQueryable>[] includeActions) + { + IQueryable query = _table; + + foreach (var includeAction in includeActions) + { + query = includeAction(query); + } + + return await query.ToListAsync(); + } + + public async Task GetByIdWithIncludes(int id, params Expression>[] includes) + { + IQueryable query = _table; + + foreach (var include in includes) + { + query = query.Include(include); + } + + return await query.FirstOrDefaultAsync(entity => EF.Property(entity, "Id") == id); + } + + public async Task Save() + { + _db.SaveChangesAsync(); + } + + public async Task Update(T entity) + { + _table.Attach(entity); + _db.Entry(entity).State = EntityState.Modified; + _db.SaveChanges(); + return entity; + } + + public IQueryable GetQuery() { - throw new NotImplementedException(); + return _table; } } } diff --git a/exercise.pizzashopapi/Tools/Mapper.cs b/exercise.pizzashopapi/Tools/Mapper.cs new file mode 100644 index 0000000..0732971 --- /dev/null +++ b/exercise.pizzashopapi/Tools/Mapper.cs @@ -0,0 +1,34 @@ +using AutoMapper; +using exercise.pizzashopapi.DTO; +using exercise.pizzashopapi.Models; + +namespace exercise.pizzashopapi.Tools +{ + public class Mapper : Profile + { + public Mapper() + { + //CreateMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + //CreateMap(); + CreateMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap() + .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer)) + .ForMember(dest => dest.Pizza, opt => opt.MapFrom(src => src.Pizza)) + .ForMember(dest => dest.CustomerId, opt => opt.MapFrom(src => src.CustomerId)) + .ForMember(dest => dest.PizzaId, opt => opt.MapFrom(src => src.PizzaId)) + .ForMember(dest => dest.OrderStatus, opt => opt.Ignore()); + + + //CreateMap(); + } + + } +} diff --git a/exercise.pizzashopapi/exercise.pizzashopapi.csproj b/exercise.pizzashopapi/exercise.pizzashopapi.csproj index 624203b..c7d5933 100644 --- a/exercise.pizzashopapi/exercise.pizzashopapi.csproj +++ b/exercise.pizzashopapi/exercise.pizzashopapi.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -11,18 +11,19 @@ - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + From f20aa7ef8edc3275921784af3286a9cb5e968bb0 Mon Sep 17 00:00:00 2001 From: Ateeb Date: Sun, 2 Feb 2025 23:01:34 +0100 Subject: [PATCH 2/2] core done and Extension Done --- exercise.pizzashopapi/DTO/AddOrderDTO.cs | 9 ++ exercise.pizzashopapi/DTO/AddToppingDTO.cs | 8 ++ exercise.pizzashopapi/DTO/OrderDTO.cs | 8 +- exercise.pizzashopapi/DTO/OrderToppingDTO.cs | 9 ++ exercise.pizzashopapi/DTO/PizzaDTO.cs | 6 +- exercise.pizzashopapi/DTO/TopppingDTO.cs | 9 ++ exercise.pizzashopapi/Data/Seeder.cs | 25 ++-- .../EndPoints/PizzaShopApi.cs | 128 ++++++++++++++++-- ...250202213744_InitialMigration.Designer.cs} | 8 +- ....cs => 20250202213744_InitialMigration.cs} | 5 +- .../Migrations/DataContextModelSnapshot.cs | 4 + exercise.pizzashopapi/Models/Order.cs | 7 +- .../Repository/Repository.cs | 14 +- exercise.pizzashopapi/Tools/Mapper.cs | 32 +++-- 14 files changed, 206 insertions(+), 66 deletions(-) create mode 100644 exercise.pizzashopapi/DTO/AddOrderDTO.cs create mode 100644 exercise.pizzashopapi/DTO/AddToppingDTO.cs create mode 100644 exercise.pizzashopapi/DTO/OrderToppingDTO.cs create mode 100644 exercise.pizzashopapi/DTO/TopppingDTO.cs rename exercise.pizzashopapi/Migrations/{20250127110031_InititalCreate.Designer.cs => 20250202213744_InitialMigration.Designer.cs} (96%) rename exercise.pizzashopapi/Migrations/{20250127110031_InititalCreate.cs => 20250202213744_InitialMigration.cs} (97%) diff --git a/exercise.pizzashopapi/DTO/AddOrderDTO.cs b/exercise.pizzashopapi/DTO/AddOrderDTO.cs new file mode 100644 index 0000000..6d6a031 --- /dev/null +++ b/exercise.pizzashopapi/DTO/AddOrderDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class AddOrderDTO + { + public int CustomerId { get; set; } + + public int PizzaId { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/AddToppingDTO.cs b/exercise.pizzashopapi/DTO/AddToppingDTO.cs new file mode 100644 index 0000000..b216f62 --- /dev/null +++ b/exercise.pizzashopapi/DTO/AddToppingDTO.cs @@ -0,0 +1,8 @@ + +namespace exercise.pizzashopapi.DTO +{ + public class AddToppingDTO + { + public string Type { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/OrderDTO.cs b/exercise.pizzashopapi/DTO/OrderDTO.cs index 1d71fac..5fdb334 100644 --- a/exercise.pizzashopapi/DTO/OrderDTO.cs +++ b/exercise.pizzashopapi/DTO/OrderDTO.cs @@ -15,12 +15,6 @@ public class OrderDTO public PizzaNoListOrderDTO Pizza { get; set; } - //public List OrderToppings { get; set; } = new List(); - - [NotMapped] - public List OrderStatus { get; } = new List - { - "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" - }; + public string OrderStatus { get; set; } } } diff --git a/exercise.pizzashopapi/DTO/OrderToppingDTO.cs b/exercise.pizzashopapi/DTO/OrderToppingDTO.cs new file mode 100644 index 0000000..f167151 --- /dev/null +++ b/exercise.pizzashopapi/DTO/OrderToppingDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class OrderToppingDTO + { + public int OrderId { get; set; } + public int ToppingId { get; set; } + } + +} diff --git a/exercise.pizzashopapi/DTO/PizzaDTO.cs b/exercise.pizzashopapi/DTO/PizzaDTO.cs index b1a75dd..a2f13ee 100644 --- a/exercise.pizzashopapi/DTO/PizzaDTO.cs +++ b/exercise.pizzashopapi/DTO/PizzaDTO.cs @@ -1,12 +1,8 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace exercise.pizzashopapi.DTO +namespace exercise.pizzashopapi.DTO { public class PizzaDTO { - public int Id { get; set; } public string Name { get; set; } - public decimal Price { get; set; } } } diff --git a/exercise.pizzashopapi/DTO/TopppingDTO.cs b/exercise.pizzashopapi/DTO/TopppingDTO.cs new file mode 100644 index 0000000..a0974b5 --- /dev/null +++ b/exercise.pizzashopapi/DTO/TopppingDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class ToppingDTO + { + public int Id { get; set; } + public string Type { get; set; } + } + +} diff --git a/exercise.pizzashopapi/Data/Seeder.cs b/exercise.pizzashopapi/Data/Seeder.cs index 9fd159e..5ec653d 100644 --- a/exercise.pizzashopapi/Data/Seeder.cs +++ b/exercise.pizzashopapi/Data/Seeder.cs @@ -1,15 +1,24 @@ using exercise.pizzashopapi.Models; +using System; +using System.Collections.Generic; +using System.Linq; namespace exercise.pizzashopapi.Data { public static class Seeder { + private static readonly Random Random = new Random(); + private static readonly List OrderStatuses = new List + { + "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" + }; + public async static void SeedPizzaShopApi(this WebApplication app) { using (var db = new DataContext()) { // Seed Customers - if(!db.Customers.Any()) + if (!db.Customers.Any()) { db.Add(new Customer() { Name = "Nigel" }); db.Add(new Customer() { Name = "Dave" }); @@ -17,7 +26,7 @@ public async static void SeedPizzaShopApi(this WebApplication app) } // Seed Pizzas - if(!db.Pizzas.Any()) + if (!db.Pizzas.Any()) { db.Add(new Pizza() { Name = "Cheese & Pineapple", Price = 150 }); db.Add(new Pizza() { Name = "Vegan Cheese Tastic", Price = 120 }); @@ -25,7 +34,7 @@ public async static void SeedPizzaShopApi(this WebApplication app) } // Seed Toppings - if(!db.Toppings.Any()) + if (!db.Toppings.Any()) { db.Add(new Toppings() { Type = "Cheese" }); db.Add(new Toppings() { Type = "Pineapple" }); @@ -33,7 +42,7 @@ public async static void SeedPizzaShopApi(this WebApplication app) } // Seed Orders - if(!db.Orders.Any()) + if (!db.Orders.Any()) { db.Add(new Order() { @@ -41,7 +50,7 @@ public async static void SeedPizzaShopApi(this WebApplication app) CustomerId = 1, PizzaId = 1, OrderToppings = new List { new OrderTopping { ToppingId = 2 } }, - OrderStatus = {"Baking"} + OrderStatus = OrderStatuses[Random.Next(OrderStatuses.Count)] }); db.Add(new Order() @@ -50,14 +59,14 @@ public async static void SeedPizzaShopApi(this WebApplication app) CustomerId = 2, PizzaId = 2, OrderToppings = new List { new OrderTopping { ToppingId = 1 } }, - OrderStatus = { "Preparing" } + OrderStatus = OrderStatuses[Random.Next(OrderStatuses.Count)] }); await db.SaveChangesAsync(); } - // Seed Order Toppings (Many-to-Many Relationship) - if(!db.OrderToppings.Any()) + + if (!db.OrderToppings.Any()) { db.Add(new OrderTopping() { Id = 1, OrderId = 1, ToppingId = 1 }); db.Add(new OrderTopping() { Id = 2, OrderId = 2, ToppingId = 2 }); diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs index ba68095..0662e15 100644 --- a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs +++ b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs @@ -15,29 +15,26 @@ public static void ConfigurePizzaShopApi(this WebApplication app) pizza.MapGet("/pizza", GetPizzas); pizza.MapGet("/order", GetOrders); - //pizza.MapGet("/orderByCustomer{id}", GetOrderByCustomerId); + pizza.MapGet("/orderByCustomer{id}", GetOrderByCustomerId); - //pizza.MapPost("/orderforCustomer", AddOrderForCustomer); - //pizza.MapPost("/pizza", AddPizza); - //pizza.MapPost("/topping", AddTopping); + pizza.MapPost("/orderforCustomer", AddOrderForCustomer); + pizza.MapPost("/pizza", AddPizza); + pizza.MapPost("/topping", AddTopping); + pizza.MapPut("/updateOrder{id}", UpdateOrder); + pizza.MapPut("/updatePizza{id}", UpdatePizza); - //pizza.MapPut("/updateOrder{id}", UpdateOrder); - //pizza.MapPut("/updatePizza{id}", UpdatePizza); - - //pizza.MapDelete("/deleteOrder{id}", DeleteOrder); + pizza.MapDelete("/deleteOrder{id}", DeleteOrder); } [ProducesResponseType(StatusCodes.Status200OK)] public static async Task GetPizzas(IRepository repository, IMapper mapper) { - var pizza = await repository.GetWithNestedIncludes(query => + var pizzas = await repository.GetWithNestedIncludes(query => query.Include(p => p.Orders) - .ThenInclude(a => a.Customer) - ); - - var response = mapper.Map>(pizza); + .ThenInclude(a => a.Customer)); + var response = mapper.Map>(pizzas); return TypedResults.Ok(response); } @@ -46,13 +43,114 @@ public static async Task GetOrders(IRepository repository, IMapp { var orders = await repository.GetWithNestedIncludes(query => query.Include(o => o.Customer) - .Include(o => o.Pizza) - ); + .Include(o => o.Pizza)); + + var response = mapper.Map>(orders); + return TypedResults.Ok(response); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetOrderByCustomerId(IRepository repository, IMapper mapper, int id) + { + var orders = await repository.GetWithNestedIncludes(query => + query.Where(o => o.CustomerId == id) + .Include(o => o.Pizza).Include(o => o.Customer)); var response = mapper.Map>(orders); return TypedResults.Ok(response); } + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task AddOrderForCustomer( + IRepository repository, + IRepository customerRepo, + IRepository pizzaRepo, + IMapper mapper, + int customerId, + int pizzaId) + { + var customer = await customerRepo.GetById(customerId); + var pizza = await pizzaRepo.GetById(pizzaId); + + if (customer == null || pizza == null) + { + return TypedResults.BadRequest("Invalid customer or pizza ID"); + } + + var order = new Order + { + CustomerId = customerId, + PizzaId = pizzaId, + Customer = customer, + Pizza = pizza, + OrderStatus = OrderStatuses[Random.Next(OrderStatuses.Count)] + }; + + await repository.Add(order); + var savedOrder = await repository.GetById(order.Id); + + if (savedOrder == null) + { + return TypedResults.Problem("Order was not saved correctly."); + } + + var response = mapper.Map(savedOrder); + return TypedResults.Created($"/shop/order/{savedOrder.Id}", response); + } + + + private static readonly Random Random = new Random(); + private static readonly List OrderStatuses = new List + { + "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" + }; + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task AddPizza(IRepository repository, IMapper mapper, PizzaDTO pizzaDto) + { + var pizza = mapper.Map(pizzaDto); + await repository.Add(pizza); + return TypedResults.Created($"/shop/pizza/{pizza.Id}", pizza); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task AddTopping(IRepository repository, IMapper mapper, AddToppingDTO toppingDto) + { + var topping = mapper.Map(toppingDto); + await repository.Add(topping); + return TypedResults.Created($"/shop/topping/{topping.Id}", topping); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task UpdateOrder(IRepository repository, IMapper mapper, int id, AddOrderDTO orderDto) + { + var existingOrder = await repository.GetById(id); + if (existingOrder == null) return TypedResults.NotFound(); + + mapper.Map(orderDto, existingOrder); + await repository.Update(existingOrder); + return TypedResults.Ok(existingOrder); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task UpdatePizza(IRepository repository, IMapper mapper, int id, PizzaNoListOrderDTO pizzaDto) + { + var existingPizza = await repository.GetById(id); + if (existingPizza == null) return TypedResults.NotFound(); + + mapper.Map(pizzaDto, existingPizza); + await repository.Update(existingPizza); + return TypedResults.Ok(existingPizza); + } + + [ProducesResponseType(StatusCodes.Status204NoContent)] + public static async Task DeleteOrder(IRepository repository, int id) + { + var existingOrder = await repository.GetById(id); + if (existingOrder == null) return TypedResults.NotFound(); + + await repository.Delete(existingOrder); + return TypedResults.NoContent(); + } } } diff --git a/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs b/exercise.pizzashopapi/Migrations/20250202213744_InitialMigration.Designer.cs similarity index 96% rename from exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs rename to exercise.pizzashopapi/Migrations/20250202213744_InitialMigration.Designer.cs index add3432..032bcf3 100644 --- a/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.Designer.cs +++ b/exercise.pizzashopapi/Migrations/20250202213744_InitialMigration.Designer.cs @@ -11,8 +11,8 @@ namespace exercise.pizzashopapi.Migrations { [DbContext(typeof(DataContext))] - [Migration("20250127110031_InititalCreate")] - partial class InititalCreate + [Migration("20250202213744_InitialMigration")] + partial class InitialMigration { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -53,6 +53,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("integer") .HasColumnName("customer_id"); + b.Property("OrderStatus") + .IsRequired() + .HasColumnType("text"); + b.Property("PizzaId") .HasColumnType("integer") .HasColumnName("pizza_id"); diff --git a/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs b/exercise.pizzashopapi/Migrations/20250202213744_InitialMigration.cs similarity index 97% rename from exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs rename to exercise.pizzashopapi/Migrations/20250202213744_InitialMigration.cs index 4454571..9c831e1 100644 --- a/exercise.pizzashopapi/Migrations/20250127110031_InititalCreate.cs +++ b/exercise.pizzashopapi/Migrations/20250202213744_InitialMigration.cs @@ -6,7 +6,7 @@ namespace exercise.pizzashopapi.Migrations { /// - public partial class InititalCreate : Migration + public partial class InitialMigration : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -58,7 +58,8 @@ protected override void Up(MigrationBuilder migrationBuilder) Id = table.Column(type: "integer", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), customer_id = table.Column(type: "integer", nullable: false), - pizza_id = table.Column(type: "integer", nullable: false) + pizza_id = table.Column(type: "integer", nullable: false), + OrderStatus = table.Column(type: "text", nullable: false) }, constraints: table => { diff --git a/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs b/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs index 3242d83..707ef68 100644 --- a/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs +++ b/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs @@ -50,6 +50,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer") .HasColumnName("customer_id"); + b.Property("OrderStatus") + .IsRequired() + .HasColumnType("text"); + b.Property("PizzaId") .HasColumnType("integer") .HasColumnName("pizza_id"); diff --git a/exercise.pizzashopapi/Models/Order.cs b/exercise.pizzashopapi/Models/Order.cs index e6b107f..6f76a8b 100644 --- a/exercise.pizzashopapi/Models/Order.cs +++ b/exercise.pizzashopapi/Models/Order.cs @@ -12,7 +12,6 @@ public class Order [ForeignKey("customer")] public int CustomerId { get; set; } - [Column("pizza_id")] [ForeignKey("pizza")] public int PizzaId { get; set; } @@ -23,10 +22,6 @@ public class Order public List OrderToppings { get; set; } = new List(); - [NotMapped] - public List OrderStatus { get; } = new List - { - "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" - }; + public string OrderStatus { get; set; } } } diff --git a/exercise.pizzashopapi/Repository/Repository.cs b/exercise.pizzashopapi/Repository/Repository.cs index 537cf58..dac0ea9 100644 --- a/exercise.pizzashopapi/Repository/Repository.cs +++ b/exercise.pizzashopapi/Repository/Repository.cs @@ -19,26 +19,26 @@ public Repository(DataContext db) public async Task Add(T entity) { _table.Add(entity); - _db.SaveChangesAsync(); + await _db.SaveChangesAsync(); return entity; } public async Task Delete(object id) { - T existing = _table.Find(id); + T existing = await _table.FindAsync(id); _table.Remove(existing); - _db.SaveChanges(); + await _db.SaveChangesAsync(); return existing; } public async Task> Get() { - return _table.ToList(); + return await _table.ToListAsync(); } public async Task GetById(int id) { - return _table.Find(id); + return await _table.FindAsync(id); } public async Task> GetWithIncludes(params Expression>[] includes) @@ -77,14 +77,14 @@ public async Task GetByIdWithIncludes(int id, params Expression Update(T entity) { _table.Attach(entity); _db.Entry(entity).State = EntityState.Modified; - _db.SaveChanges(); + await _db.SaveChangesAsync(); return entity; } diff --git a/exercise.pizzashopapi/Tools/Mapper.cs b/exercise.pizzashopapi/Tools/Mapper.cs index 0732971..187b945 100644 --- a/exercise.pizzashopapi/Tools/Mapper.cs +++ b/exercise.pizzashopapi/Tools/Mapper.cs @@ -1,34 +1,38 @@ using AutoMapper; using exercise.pizzashopapi.DTO; using exercise.pizzashopapi.Models; +using System; +using System.Collections.Generic; namespace exercise.pizzashopapi.Tools { public class Mapper : Profile { + private static readonly Random Random = new Random(); + private static readonly List OrderStatuses = new List + { + "Preparing", "Baking", "Quality Check", "Out for Delivery", "Delivered" + }; + public Mapper() { - //CreateMap(); - //CreateMap(); - //CreateMap(); - //CreateMap(); - //CreateMap(); - //CreateMap(); - //CreateMap(); - //CreateMap(); - CreateMap(); - CreateMap().ReverseMap(); - CreateMap().ReverseMap(); CreateMap() .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer)) .ForMember(dest => dest.Pizza, opt => opt.MapFrom(src => src.Pizza)) .ForMember(dest => dest.CustomerId, opt => opt.MapFrom(src => src.CustomerId)) .ForMember(dest => dest.PizzaId, opt => opt.MapFrom(src => src.PizzaId)) - .ForMember(dest => dest.OrderStatus, opt => opt.Ignore()); + .ForMember(dest => dest.OrderStatus, opt => opt.MapFrom(src => src.OrderStatus ?? "Pending")); + CreateMap() + .ForMember(dest => dest.OrderStatus, opt => opt.MapFrom(src => OrderStatuses[Random.Next(OrderStatuses.Count)])); - //CreateMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } - } }