From 2f175ba3549838379c8c73b17d49fa884b159f89 Mon Sep 17 00:00:00 2001 From: Tonnes Date: Thu, 6 Feb 2025 17:44:32 +0100 Subject: [PATCH 1/2] Core Done --- .gitignore | 1 + .../DTO/Request/CustomerPost - Copy.cs | 9 + .../DTO/Request/CustomerPost.cs | 9 + .../DTO/Request/OrderPost.cs | 11 + exercise.pizzashopapi/DTO/Request/OrderPut.cs | 11 + .../DTO/Request/PizzaPost.cs | 11 + exercise.pizzashopapi/DTO/Request/PizzaPut.cs | 11 + .../DTO/Response/CustomerDTO.cs | 11 + .../DTO/Response/OrderCustomerDTO.cs | 11 + .../DTO/Response/OrderDTO.cs | 13 + .../DTO/Response/PizzaDTO.cs | 12 + exercise.pizzashopapi/Data/Seeder.cs | 24 +- .../EndPoints/PizzaShopApi.cs | 387 +++++++++++++++++- exercise.pizzashopapi/Models/Customer.cs | 9 +- exercise.pizzashopapi/Models/Order.cs | 21 +- exercise.pizzashopapi/Models/Pizza.cs | 13 +- exercise.pizzashopapi/Program.cs | 9 +- .../Repository/IRepository.cs | 14 +- .../Repository/Repository.cs | 61 ++- .../exercise.pizzashopapi.csproj | 1 + 20 files changed, 623 insertions(+), 26 deletions(-) create mode 100644 exercise.pizzashopapi/DTO/Request/CustomerPost - Copy.cs create mode 100644 exercise.pizzashopapi/DTO/Request/CustomerPost.cs create mode 100644 exercise.pizzashopapi/DTO/Request/OrderPost.cs create mode 100644 exercise.pizzashopapi/DTO/Request/OrderPut.cs create mode 100644 exercise.pizzashopapi/DTO/Request/PizzaPost.cs create mode 100644 exercise.pizzashopapi/DTO/Request/PizzaPut.cs create mode 100644 exercise.pizzashopapi/DTO/Response/CustomerDTO.cs create mode 100644 exercise.pizzashopapi/DTO/Response/OrderCustomerDTO.cs create mode 100644 exercise.pizzashopapi/DTO/Response/OrderDTO.cs create mode 100644 exercise.pizzashopapi/DTO/Response/PizzaDTO.cs diff --git a/.gitignore b/.gitignore index a62e968..5d91997 100644 --- a/.gitignore +++ b/.gitignore @@ -482,3 +482,4 @@ $RECYCLE.BIN/ */**/obj/Release /exercise.pizzashopapi/appsettings.json /exercise.pizzashopapi/appsettings.Development.json +*/Migrations diff --git a/exercise.pizzashopapi/DTO/Request/CustomerPost - Copy.cs b/exercise.pizzashopapi/DTO/Request/CustomerPost - Copy.cs new file mode 100644 index 0000000..c09b980 --- /dev/null +++ b/exercise.pizzashopapi/DTO/Request/CustomerPost - Copy.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class CustomerPut + { + public string? Name { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Request/CustomerPost.cs b/exercise.pizzashopapi/DTO/Request/CustomerPost.cs new file mode 100644 index 0000000..1733a61 --- /dev/null +++ b/exercise.pizzashopapi/DTO/Request/CustomerPost.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class CustomerPost + { + public string Name { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Request/OrderPost.cs b/exercise.pizzashopapi/DTO/Request/OrderPost.cs new file mode 100644 index 0000000..da0e6a3 --- /dev/null +++ b/exercise.pizzashopapi/DTO/Request/OrderPost.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class OrderPost + { + public int CustomerId { get; set; } + public int PizzaId { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Request/OrderPut.cs b/exercise.pizzashopapi/DTO/Request/OrderPut.cs new file mode 100644 index 0000000..590d41f --- /dev/null +++ b/exercise.pizzashopapi/DTO/Request/OrderPut.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class OrderPut + { + public int? CustomerId { get; set; } + public int? PizzaId { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Request/PizzaPost.cs b/exercise.pizzashopapi/DTO/Request/PizzaPost.cs new file mode 100644 index 0000000..140c535 --- /dev/null +++ b/exercise.pizzashopapi/DTO/Request/PizzaPost.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class PizzaPost + { + public string Name { get; set; } + public decimal Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Request/PizzaPut.cs b/exercise.pizzashopapi/DTO/Request/PizzaPut.cs new file mode 100644 index 0000000..52f1590 --- /dev/null +++ b/exercise.pizzashopapi/DTO/Request/PizzaPut.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class PizzaPut + { + public string? Name { get; set; } + public decimal? Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Response/CustomerDTO.cs b/exercise.pizzashopapi/DTO/Response/CustomerDTO.cs new file mode 100644 index 0000000..d22599a --- /dev/null +++ b/exercise.pizzashopapi/DTO/Response/CustomerDTO.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class CustomerDTO + { + public int Id { get; set; } + public string Name { get; set; } + public List Orders { get; set;} = new List(); + } +} diff --git a/exercise.pizzashopapi/DTO/Response/OrderCustomerDTO.cs b/exercise.pizzashopapi/DTO/Response/OrderCustomerDTO.cs new file mode 100644 index 0000000..936ef7b --- /dev/null +++ b/exercise.pizzashopapi/DTO/Response/OrderCustomerDTO.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class OrderCustomerDTO + { + public string PizzaName { get; set; } + public decimal PizzaPrice { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Response/OrderDTO.cs b/exercise.pizzashopapi/DTO/Response/OrderDTO.cs new file mode 100644 index 0000000..53af07a --- /dev/null +++ b/exercise.pizzashopapi/DTO/Response/OrderDTO.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class OrderDTO + { + public int Id { get; set; } + public string CustomerName { get; set; } + public string PizzaName { get; set; } + public decimal PizzaPrice { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/Response/PizzaDTO.cs b/exercise.pizzashopapi/DTO/Response/PizzaDTO.cs new file mode 100644 index 0000000..be76bc4 --- /dev/null +++ b/exercise.pizzashopapi/DTO/Response/PizzaDTO.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + public class PizzaDTO + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/Data/Seeder.cs b/exercise.pizzashopapi/Data/Seeder.cs index f87fbef..d62f3e8 100644 --- a/exercise.pizzashopapi/Data/Seeder.cs +++ b/exercise.pizzashopapi/Data/Seeder.cs @@ -12,20 +12,36 @@ public async static void SeedPizzaShopApi(this WebApplication app) { db.Add(new Customer() { Name="Nigel" }); db.Add(new Customer() { Name = "Dave" }); + db.Add(new Customer() { Name = "Tonnes" }); await db.SaveChangesAsync(); } 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 = 140}); + db.Add(new Pizza() { Name = "Vegan Cheese Tastic", Price = 130}); + db.Add(new Pizza() { Name = "Pepperoni & Pineapple", Price = 150}); await db.SaveChangesAsync(); } //order data - if(1==1) + if(!db.Orders.Any()) { - + db.Add(new Order + { + CustomerId = 1, + PizzaId = 1, + }); + db.Add(new Order + { + CustomerId = 2, + PizzaId = 2, + }); + db.Add(new Order + { + CustomerId = 2, + PizzaId = 2, + }); await db.SaveChangesAsync(); } } diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs index f8be2b0..340f8e9 100644 --- a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs +++ b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs @@ -1,4 +1,7 @@ -using exercise.pizzashopapi.Repository; +using System.Configuration; +using AutoMapper; +using exercise.pizzashopapi.Models; +using exercise.pizzashopapi.Repository; using Microsoft.AspNetCore.Mvc; namespace exercise.pizzashopapi.EndPoints @@ -7,9 +10,387 @@ public static class PizzaShopApi { public static void ConfigurePizzaShopApi(this WebApplication app) { - + var pizzaShop = app.MapGroup("pizzashop"); + + + pizzaShop.MapGet("/pizza", GetPizzas); + pizzaShop.MapGet("/pizza/{id}", GetPizzaById); + pizzaShop.MapPost("/pizza", CreatePizza); + pizzaShop.MapPut("/pizza/{id}", UpdatePizza); + pizzaShop.MapDelete("/pizza/{id}", DeletePizza); + + pizzaShop.MapGet("/customers", GetCustomers); + pizzaShop.MapGet("/customers/{id}", GetCustomerById); + pizzaShop.MapPost("/customers", CreateCustomer); + pizzaShop.MapPut("/customers/{id}", UpdateCustomer); + pizzaShop.MapDelete("/customers/{id}", DeleteCustomer); + + pizzaShop.MapGet("/orders", GetOrders); + pizzaShop.MapGet("/orders/{id}", GetOrderById); + pizzaShop.MapGet("/ordersbycustomer/{id}", GetOrderByCustomer); + pizzaShop.MapPost("/orders", CreateOrder); + pizzaShop.MapPut("/orders/{id}", UpdateOrder); + pizzaShop.MapDelete("/orders/{id}", DeleteOrder); + } + + // Pizza + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetPizzas(IRepository pizzaRepo, IMapper mapper) + { + var pizzas = await pizzaRepo.Get(); + + var result = pizzas.Select(p => new PizzaDTO + { + Id = p.Id, + Name = p.Name, + Price = p.Price + }).ToList(); + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetPizzaById(IRepository pizzaRepo, int id, IMapper mapper) + { + var pizzas = await pizzaRepo.Get(); + var pizza = pizzas.FirstOrDefault(p => p.Id == id); + + if (pizza == null) return TypedResults.NotFound($"No pizza found for id {id}"); + + var result = new PizzaDTO + { + Id = pizza.Id, + Name = pizza.Name, + Price = pizza.Price + }; + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreatePizza(IRepository pizzaRepo, PizzaPost model, IMapper mapper) + { + if (string.IsNullOrWhiteSpace(model.Name) || + model.Price == null ) return Results.BadRequest("Pizza's input was formatted wrong."); + + var pizza = new Pizza() + { + Name = model.Name, + Price = model.Price + }; + + pizza = await pizzaRepo.Insert(pizza); + + var result = new PizzaDTO + { + Id = pizza.Id, + Name = pizza.Name, + Price = pizza.Price + }; + return Results.Created($"/pizzas/{pizza.Id}", result); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdatePizza(IRepository pizzaRepo, int id, PizzaPut model, IMapper mapper) + { + if (string.IsNullOrWhiteSpace(model.Name) || + model.Price == null) return Results.BadRequest("Pizza's input was formatted wrong."); + + var pizza = await pizzaRepo.GetById(id); + if (pizza == null) return Results.NotFound("Pizza not found"); + if (model.Name != null) pizza.Name = model.Name; + if (model.Price != null) pizza.Price = (decimal)model.Price; + + pizza = await pizzaRepo.Update(pizza); + + var result = new PizzaDTO + { + Id = pizza.Id, + Name = pizza.Name, + Price = pizza.Price + }; + + return Results.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeletePizza(IRepository pizzaRepo, int id, IMapper mapper) + { + var pizza = await pizzaRepo.GetById(id); + if (pizza == null) return Results.NotFound("Pizza not found"); + + pizza = await pizzaRepo.Delete(pizza); + + var result = new PizzaDTO + { + Id = pizza.Id, + Name = pizza.Name, + Price = pizza.Price + }; + + return Results.Ok(result); + } + + + + + + // Customer + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetCustomers(IRepository customerRepo, IRepository orderRepo, IMapper mapper) + { + var customers = await customerRepo.GetWithIncludes(c => c.Orders); + + var result = customers.Select(c => new CustomerDTO + { + Id = c.Id, + Name = c.Name, + Orders = c.Orders.Select(o => new OrderCustomerDTO + { + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList() + }).ToList(); + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetCustomerById(IRepository customerRepo, IRepository orderRepo, int id, IMapper mapper) + { + var customers = await customerRepo.GetWithIncludes(c => c.Orders); + var customer = customers.FirstOrDefault(c => c.Id == id); + + if (customer == null) return TypedResults.NotFound($"No customer found for id {id}"); + + var result = new CustomerDTO + { + Id = customer.Id, + Name = customer.Name, + Orders = customer.Orders.Select(o => new OrderCustomerDTO + { + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList() + }; + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateCustomer(IRepository customerRepo, CustomerPost model, IMapper mapper) + { + if (string.IsNullOrWhiteSpace(model.Name)) return Results.BadRequest("Customer's input was formatted wrong."); + + var customer = new Customer() + { + Name = model.Name + }; + + customer = await customerRepo.Insert(customer); + + var result = new CustomerDTO + { + Id = customer.Id, + Name = customer.Name, + Orders = customer.Orders.Select(o => new OrderCustomerDTO + { + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList() + }; + return Results.Created($"/customers/{customer.Id}", result); } - + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdateCustomer(IRepository customerRepo, int id, CustomerPut model, IMapper mapper) + { + if (string.IsNullOrWhiteSpace(model.Name)) return Results.BadRequest("Customer's input was formatted wrong."); + + var customer = await customerRepo.GetById(id); + if (customer == null) return Results.NotFound("Customer not found"); + if (model.Name != null) customer.Name = model.Name; + + customer = await customerRepo.Update(customer); + + var result = new CustomerDTO + { + Id = customer.Id, + Name = customer.Name, + Orders = customer.Orders.Select(o => new OrderCustomerDTO + { + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList() + }; + return Results.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeleteCustomer(IRepository customerRepo, int id, IMapper mapper) + { + var customer = await customerRepo.GetById(id); + if (customer == null) return Results.NotFound("Customer not found"); + + customer = await customerRepo.Delete(customer); + + var result = new CustomerDTO + { + Id = customer.Id, + Name = customer.Name, + Orders = customer.Orders.Select(o => new OrderCustomerDTO + { + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList() + }; + + return Results.Ok(result); + } + + + + + + // Customer + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetOrders(IRepository orderRepo, IMapper mapper) + { + var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + + var result = orders.Select(o => new OrderDTO + { + Id = o.Id, + CustomerName = o.Customer.Name, + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList(); + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetOrderById(IRepository orderRepo, int id, IMapper mapper) + { + var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var order = orders.FirstOrDefault(c => c.Id == id); + + if (order == null) return TypedResults.NotFound($"No order found for id {id}"); + + var result = new OrderDTO + { + Id = order.Id, + CustomerName = order.Customer.Name, + PizzaName = order.Pizza.Name, + PizzaPrice = order.Pizza.Price + }; + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetOrderByCustomer(IRepository orderRepo, int id, IMapper mapper) + { + var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + + var result = orders.Where(o => o.CustomerId == id).Select(o => new OrderDTO + { + Id = o.Id, + CustomerName = o.Customer.Name, + PizzaName = o.Pizza.Name, + PizzaPrice = o.Pizza.Price + }).ToList(); + if (!result.Any()) return TypedResults.NotFound($"No orders found for customer id {id}"); + return TypedResults.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateOrder(IRepository orderRepo, OrderPost model, IMapper mapper) + { + if (model.CustomerId == null || + model.PizzaId == null) return Results.BadRequest("Order's input was formatted wrong."); + + var order = new Order() + { + CustomerId = model.CustomerId, + PizzaId = model.PizzaId + }; + + order = await orderRepo.Insert(order); + + var result = new OrderDTO + { + Id = order.Id, + CustomerName = order.Customer.Name, + PizzaName = order.Pizza.Name, + PizzaPrice = order.Pizza.Price + }; + return Results.Created($"/orders/{order.Id}", result); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdateOrder(IRepository orderRepo, int id, OrderPut model, IMapper mapper) + { + if (model.CustomerId == null || + model.PizzaId == null) return Results.BadRequest("Order's input was formatted wrong."); + + var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var order = orders.FirstOrDefault(c => c.Id == id); + + if (order == null) return TypedResults.NotFound($"No order found for id {id}"); + + if (model.CustomerId != null) order.CustomerId = (int)model.CustomerId; + if (model.PizzaId != null) order.PizzaId = (int)model.PizzaId; + + order = await orderRepo.Update(order); + + var result = new OrderDTO + { + Id = order.Id, + CustomerName = order.Customer.Name, + PizzaName = order.Pizza.Name, + PizzaPrice = order.Pizza.Price + }; + return Results.Ok(result); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeleteOrder(IRepository orderRepo, int id, IMapper mapper) + { + var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var order = orders.FirstOrDefault(c => c.Id == id); + + if (order == null) return TypedResults.NotFound($"No order found for id {id}"); + + order = await orderRepo.Delete(order); + + var result = new OrderDTO + { + Id = order.Id, + CustomerName = order.Customer.Name, + PizzaName = order.Pizza.Name, + PizzaPrice = order.Pizza.Price + }; + + return Results.Ok(result); + } + + + + + + + + + + + } } diff --git a/exercise.pizzashopapi/Models/Customer.cs b/exercise.pizzashopapi/Models/Customer.cs index 2ca83bd..4682252 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("customers")] public class Customer { + [Key] + [Column("id")] public int Id { get; set; } + [Required] + [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..a220502 100644 --- a/exercise.pizzashopapi/Models/Order.cs +++ b/exercise.pizzashopapi/Models/Order.cs @@ -1,10 +1,25 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { + [Table("orders")] public class Order { - - + [Key] + [Column("id")] + public int Id { get; set; } + + [ForeignKey("Customer")] + public int CustomerId { get; set; } + + [ForeignKey("Pizza")] + public int PizzaId { get; set; } + + public Customer Customer { get; set; } + public Pizza Pizza { get; set; } + + + } } diff --git a/exercise.pizzashopapi/Models/Pizza.cs b/exercise.pizzashopapi/Models/Pizza.cs index 5c085ec..2a6b49f 100644 --- a/exercise.pizzashopapi/Models/Pizza.cs +++ b/exercise.pizzashopapi/Models/Pizza.cs @@ -1,12 +1,19 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { - + [Table("pizzas")] public class Pizza - { + { + [Key] + [Column("id")] public int Id { get; set; } + [Required] + [Column("name")] public string Name { get; set; } + [Required] + [Column("prize")] public decimal Price { get; set; } } } \ No newline at end of file diff --git a/exercise.pizzashopapi/Program.cs b/exercise.pizzashopapi/Program.cs index c04a440..bafa8fc 100644 --- a/exercise.pizzashopapi/Program.cs +++ b/exercise.pizzashopapi/Program.cs @@ -1,4 +1,6 @@ +using System.Numerics; using exercise.pizzashopapi.Data; +using exercise.pizzashopapi.Models; using exercise.pizzashopapi.EndPoints; using exercise.pizzashopapi.Repository; @@ -7,7 +9,9 @@ // 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.AddDbContext(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); @@ -28,8 +32,9 @@ app.MapControllers(); +app.SeedPizzaShopApi(); + app.ConfigurePizzaShopApi(); -app.SeedPizzaShopApi(); app.Run(); diff --git a/exercise.pizzashopapi/Repository/IRepository.cs b/exercise.pizzashopapi/Repository/IRepository.cs index b114ea8..215b651 100644 --- a/exercise.pizzashopapi/Repository/IRepository.cs +++ b/exercise.pizzashopapi/Repository/IRepository.cs @@ -1,11 +1,15 @@ -using exercise.pizzashopapi.Models; +using System.Linq.Expressions; namespace exercise.pizzashopapi.Repository { - public interface IRepository + public interface IRepository { - Task> GetOrdersByCustomer(int id); - - + Task> Get(); + Task GetById(int id); + Task Insert(T entity); + Task Update(T entity); + Task Delete(object id); + Task Save(); + Task> GetWithIncludes(params Expression>[] includes); } } diff --git a/exercise.pizzashopapi/Repository/Repository.cs b/exercise.pizzashopapi/Repository/Repository.cs index e109fce..05a70e5 100644 --- a/exercise.pizzashopapi/Repository/Repository.cs +++ b/exercise.pizzashopapi/Repository/Repository.cs @@ -1,14 +1,65 @@ -using exercise.pizzashopapi.Data; -using exercise.pizzashopapi.Models; +using System.Linq.Expressions; +using exercise.pizzashopapi.Data; +using Microsoft.EntityFrameworkCore; namespace exercise.pizzashopapi.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 dataContext) + { + _db = dataContext; + _table = _db.Set(); + } + + public async Task> Get() + { + return _table.ToList(); + } + + public async Task Insert(T entity) + { + _table.Add(entity); + _db.SaveChanges(); + return entity; + } + + public async Task Update(T entity) + { + _table.Attach(entity); + _db.Entry(entity).State = EntityState.Modified; + _db.SaveChanges(); + return entity; + } + + public async Task Delete(object id) + { + T entity = _table.Find(id); + _table.Remove(entity); + _db.SaveChanges(); + return entity; + } + + 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 query; + } + + public async Task Save() { - throw new NotImplementedException(); + _db.SaveChangesAsync(); } } } diff --git a/exercise.pizzashopapi/exercise.pizzashopapi.csproj b/exercise.pizzashopapi/exercise.pizzashopapi.csproj index 624203b..4a3bdd5 100644 --- a/exercise.pizzashopapi/exercise.pizzashopapi.csproj +++ b/exercise.pizzashopapi/exercise.pizzashopapi.csproj @@ -11,6 +11,7 @@ + all From 395a647ee3a01a28e2c9bab8dac5590f3795b24a Mon Sep 17 00:00:00 2001 From: Tonnes Date: Thu, 13 Feb 2025 10:30:45 +0100 Subject: [PATCH 2/2] Fixed and tested --- exercise.pizzashopapi/Data/DataContext.cs | 8 +- .../EndPoints/PizzaShopApi.cs | 83 +++++++++++-------- exercise.pizzashopapi/Program.cs | 6 -- .../Repository/IRepository.cs | 2 +- .../Repository/Repository.cs | 2 +- 5 files changed, 52 insertions(+), 49 deletions(-) diff --git a/exercise.pizzashopapi/Data/DataContext.cs b/exercise.pizzashopapi/Data/DataContext.cs index 129199e..7bb974d 100644 --- a/exercise.pizzashopapi/Data/DataContext.cs +++ b/exercise.pizzashopapi/Data/DataContext.cs @@ -9,17 +9,11 @@ public class DataContext : DbContext public DataContext() { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString"); - + connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(connectionString); - - //set primary of order? - - //seed data? - } public DbSet Pizzas { get; set; } public DbSet Customers { get; set; } diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs index 340f8e9..87ff7fb 100644 --- a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs +++ b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs @@ -1,8 +1,7 @@ -using System.Configuration; -using AutoMapper; -using exercise.pizzashopapi.Models; +using exercise.pizzashopapi.Models; using exercise.pizzashopapi.Repository; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace exercise.pizzashopapi.EndPoints { @@ -35,7 +34,7 @@ public static void ConfigurePizzaShopApi(this WebApplication app) // Pizza [ProducesResponseType(StatusCodes.Status200OK)] - public static async Task GetPizzas(IRepository pizzaRepo, IMapper mapper) + public static async Task GetPizzas(IRepository pizzaRepo) { var pizzas = await pizzaRepo.Get(); @@ -50,7 +49,7 @@ public static async Task GetPizzas(IRepository pizzaRepo, IMappe [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task GetPizzaById(IRepository pizzaRepo, int id, IMapper mapper) + public static async Task GetPizzaById(IRepository pizzaRepo, int id) { var pizzas = await pizzaRepo.Get(); var pizza = pizzas.FirstOrDefault(p => p.Id == id); @@ -68,7 +67,7 @@ public static async Task GetPizzaById(IRepository pizzaRepo, int [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public static async Task CreatePizza(IRepository pizzaRepo, PizzaPost model, IMapper mapper) + public static async Task CreatePizza(IRepository pizzaRepo, PizzaPost model) { if (string.IsNullOrWhiteSpace(model.Name) || model.Price == null ) return Results.BadRequest("Pizza's input was formatted wrong."); @@ -92,7 +91,7 @@ public static async Task CreatePizza(IRepository pizzaRepo, Pizz [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public static async Task UpdatePizza(IRepository pizzaRepo, int id, PizzaPut model, IMapper mapper) + public static async Task UpdatePizza(IRepository pizzaRepo, int id, PizzaPut model) { if (string.IsNullOrWhiteSpace(model.Name) || model.Price == null) return Results.BadRequest("Pizza's input was formatted wrong."); @@ -116,12 +115,12 @@ public static async Task UpdatePizza(IRepository pizzaRepo, int [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task DeletePizza(IRepository pizzaRepo, int id, IMapper mapper) + public static async Task DeletePizza(IRepository pizzaRepo, int id) { var pizza = await pizzaRepo.GetById(id); if (pizza == null) return Results.NotFound("Pizza not found"); - pizza = await pizzaRepo.Delete(pizza); + await pizzaRepo.Delete(id); var result = new PizzaDTO { @@ -139,9 +138,10 @@ public static async Task DeletePizza(IRepository pizzaRepo, int // Customer [ProducesResponseType(StatusCodes.Status200OK)] - public static async Task GetCustomers(IRepository customerRepo, IRepository orderRepo, IMapper mapper) + public static async Task GetCustomers(IRepository customerRepo, IRepository orderRepo) { - var customers = await customerRepo.GetWithIncludes(c => c.Orders); + var customerQuery = customerRepo.GetWithIncludes(c => c.Orders); + var customers = await customerQuery.Include(c => c.Orders).ThenInclude(o => o.Pizza).ToListAsync(); var result = customers.Select(c => new CustomerDTO { @@ -158,9 +158,10 @@ public static async Task GetCustomers(IRepository customerRep [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task GetCustomerById(IRepository customerRepo, IRepository orderRepo, int id, IMapper mapper) + public static async Task GetCustomerById(IRepository customerRepo, IRepository orderRepo, int id) { - var customers = await customerRepo.GetWithIncludes(c => c.Orders); + var customerQuery = customerRepo.GetWithIncludes(c => c.Orders); + var customers = await customerQuery.Include(c => c.Orders).ThenInclude(o => o.Pizza).ToListAsync(); var customer = customers.FirstOrDefault(c => c.Id == id); if (customer == null) return TypedResults.NotFound($"No customer found for id {id}"); @@ -180,7 +181,7 @@ public static async Task GetCustomerById(IRepository customer [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public static async Task CreateCustomer(IRepository customerRepo, CustomerPost model, IMapper mapper) + public static async Task CreateCustomer(IRepository customerRepo, CustomerPost model) { if (string.IsNullOrWhiteSpace(model.Name)) return Results.BadRequest("Customer's input was formatted wrong."); @@ -191,6 +192,10 @@ public static async Task CreateCustomer(IRepository customerR customer = await customerRepo.Insert(customer); + var customerQuery = customerRepo.GetWithIncludes(c => c.Orders); + var customers = await customerQuery.Include(c => c.Orders).ThenInclude(o => o.Pizza).ToListAsync(); + customer = customers.FirstOrDefault(c => c.Id == customer.Id); + var result = new CustomerDTO { Id = customer.Id, @@ -206,15 +211,18 @@ public static async Task CreateCustomer(IRepository customerR [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public static async Task UpdateCustomer(IRepository customerRepo, int id, CustomerPut model, IMapper mapper) + public static async Task UpdateCustomer(IRepository customerRepo, int id, CustomerPut model) { if (string.IsNullOrWhiteSpace(model.Name)) return Results.BadRequest("Customer's input was formatted wrong."); - var customer = await customerRepo.GetById(id); + var customerQuery = customerRepo.GetWithIncludes(c => c.Orders); + var customers = await customerQuery.Include(c => c.Orders).ThenInclude(o => o.Pizza).ToListAsync(); + var customer = customers.FirstOrDefault(c => c.Id == id); if (customer == null) return Results.NotFound("Customer not found"); if (model.Name != null) customer.Name = model.Name; - customer = await customerRepo.Update(customer); + await customerRepo.Update(customer); + var result = new CustomerDTO { @@ -231,12 +239,14 @@ public static async Task UpdateCustomer(IRepository customerR [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task DeleteCustomer(IRepository customerRepo, int id, IMapper mapper) + public static async Task DeleteCustomer(IRepository customerRepo, int id) { - var customer = await customerRepo.GetById(id); + var customerQuery = customerRepo.GetWithIncludes(c => c.Orders); + var customers = await customerQuery.Include(c => c.Orders).ThenInclude(o => o.Pizza).ToListAsync(); + var customer = customers.FirstOrDefault(c => c.Id == id); if (customer == null) return Results.NotFound("Customer not found"); - customer = await customerRepo.Delete(customer); + await customerRepo.Delete(id); var result = new CustomerDTO { @@ -256,11 +266,11 @@ public static async Task DeleteCustomer(IRepository customerR - // Customer + // Orders [ProducesResponseType(StatusCodes.Status200OK)] - public static async Task GetOrders(IRepository orderRepo, IMapper mapper) + public static async Task GetOrders(IRepository orderRepo) { - var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); var result = orders.Select(o => new OrderDTO { @@ -274,9 +284,9 @@ public static async Task GetOrders(IRepository orderRepo, IMappe [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task GetOrderById(IRepository orderRepo, int id, IMapper mapper) + public static async Task GetOrderById(IRepository orderRepo, int id) { - var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); var order = orders.FirstOrDefault(c => c.Id == id); if (order == null) return TypedResults.NotFound($"No order found for id {id}"); @@ -293,9 +303,9 @@ public static async Task GetOrderById(IRepository orderRepo, int [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task GetOrderByCustomer(IRepository orderRepo, int id, IMapper mapper) + public static async Task GetOrderByCustomer(IRepository orderRepo, int id) { - var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); var result = orders.Where(o => o.CustomerId == id).Select(o => new OrderDTO { @@ -310,7 +320,7 @@ public static async Task GetOrderByCustomer(IRepository orderRep [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public static async Task CreateOrder(IRepository orderRepo, OrderPost model, IMapper mapper) + public static async Task CreateOrder(IRepository orderRepo, OrderPost model) { if (model.CustomerId == null || model.PizzaId == null) return Results.BadRequest("Order's input was formatted wrong."); @@ -322,6 +332,8 @@ public static async Task CreateOrder(IRepository orderRepo, Orde }; order = await orderRepo.Insert(order); + var orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + order = orders.FirstOrDefault(c => c.Id == order.Id); var result = new OrderDTO { @@ -335,12 +347,12 @@ public static async Task CreateOrder(IRepository orderRepo, Orde [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public static async Task UpdateOrder(IRepository orderRepo, int id, OrderPut model, IMapper mapper) + public static async Task UpdateOrder(IRepository orderRepo, int id, OrderPut model) { if (model.CustomerId == null || model.PizzaId == null) return Results.BadRequest("Order's input was formatted wrong."); - var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); var order = orders.FirstOrDefault(c => c.Id == id); if (order == null) return TypedResults.NotFound($"No order found for id {id}"); @@ -348,7 +360,10 @@ public static async Task UpdateOrder(IRepository orderRepo, int if (model.CustomerId != null) order.CustomerId = (int)model.CustomerId; if (model.PizzaId != null) order.PizzaId = (int)model.PizzaId; - order = await orderRepo.Update(order); + await orderRepo.Update(order); + + orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + order = orders.FirstOrDefault(c => c.Id == id); var result = new OrderDTO { @@ -362,14 +377,14 @@ public static async Task UpdateOrder(IRepository orderRepo, int [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public static async Task DeleteOrder(IRepository orderRepo, int id, IMapper mapper) + public static async Task DeleteOrder(IRepository orderRepo, int id) { - var orders = await orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); + var orders = orderRepo.GetWithIncludes(o => o.Pizza, o => o.Customer); var order = orders.FirstOrDefault(c => c.Id == id); if (order == null) return TypedResults.NotFound($"No order found for id {id}"); - order = await orderRepo.Delete(order); + await orderRepo.Delete(id); var result = new OrderDTO { diff --git a/exercise.pizzashopapi/Program.cs b/exercise.pizzashopapi/Program.cs index bafa8fc..970a06a 100644 --- a/exercise.pizzashopapi/Program.cs +++ b/exercise.pizzashopapi/Program.cs @@ -27,14 +27,8 @@ } app.UseHttpsRedirection(); - app.UseAuthorization(); - app.MapControllers(); - app.SeedPizzaShopApi(); - app.ConfigurePizzaShopApi(); - - app.Run(); diff --git a/exercise.pizzashopapi/Repository/IRepository.cs b/exercise.pizzashopapi/Repository/IRepository.cs index 215b651..c4f9be2 100644 --- a/exercise.pizzashopapi/Repository/IRepository.cs +++ b/exercise.pizzashopapi/Repository/IRepository.cs @@ -10,6 +10,6 @@ public interface IRepository Task Update(T entity); Task Delete(object id); Task Save(); - Task> GetWithIncludes(params Expression>[] includes); + IQueryable GetWithIncludes(params Expression>[] includes); } } diff --git a/exercise.pizzashopapi/Repository/Repository.cs b/exercise.pizzashopapi/Repository/Repository.cs index 05a70e5..579e739 100644 --- a/exercise.pizzashopapi/Repository/Repository.cs +++ b/exercise.pizzashopapi/Repository/Repository.cs @@ -47,7 +47,7 @@ public async Task GetById(int id) { return _table.Find(id); } - public async Task> GetWithIncludes(params Expression>[] includes) + public IQueryable GetWithIncludes(params Expression>[] includes) { IQueryable query = _table; foreach (var include in includes)