diff --git a/exercise.pizzashopapi/Data/DataContext.cs b/exercise.pizzashopapi/Data/DatabaseContext.cs similarity index 72% rename from exercise.pizzashopapi/Data/DataContext.cs rename to exercise.pizzashopapi/Data/DatabaseContext.cs index 129199e..66163e4 100644 --- a/exercise.pizzashopapi/Data/DataContext.cs +++ b/exercise.pizzashopapi/Data/DatabaseContext.cs @@ -3,14 +3,14 @@ namespace exercise.pizzashopapi.Data { - public class DataContext : DbContext + public class DatabaseContext : DbContext { private string connectionString; - public DataContext() + public DatabaseContext() { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString"); - + connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString") ?? ""; + this.Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -24,5 +24,7 @@ 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..c336f8f 100644 --- a/exercise.pizzashopapi/Data/Seeder.cs +++ b/exercise.pizzashopapi/Data/Seeder.cs @@ -6,26 +6,35 @@ public static class Seeder { public async static void SeedPizzaShopApi(this WebApplication app) { - using(var db = new DataContext()) + using(var db = new DatabaseContext()) { if(!db.Customers.Any()) { - db.Add(new Customer() { Name="Nigel" }); + db.Add(new Customer() { Name = "Nigel" }); db.Add(new Customer() { Name = "Dave" }); + db.Add(new Customer() { Name = "Jone" }); 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 = 12.34m }); + db.Add(new Pizza() { Name = "Vegan Cheese Tastic", Price = 23.45m }); + db.Add(new Pizza() { Name = "Pepperoni", Price = 34.56m}); await db.SaveChangesAsync(); } - - //order data - if(1==1) + if (!db.Toppings.Any()) { - + db.Add(new Topping() { Name = "Extra Cheese", Price = 2.50m }); + db.Add(new Topping() { Name = "Olives", Price = 1.75m }); + db.Add(new Topping() { Name = "Bacon", Price = 3.00m }); + await db.SaveChangesAsync(); + } + if (!db.Orders.Any()) + { + db.Add(new Order() { CustomerId = 1, PizzaId = 2 }); + db.Add(new Order() { CustomerId = 2, PizzaId = 1 }); + db.Add(new Order() { CustomerId = 3, PizzaId = 3 }); await db.SaveChangesAsync(); } } diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs deleted file mode 100644 index f8be2b0..0000000 --- a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs +++ /dev/null @@ -1,15 +0,0 @@ -using exercise.pizzashopapi.Repository; -using Microsoft.AspNetCore.Mvc; - -namespace exercise.pizzashopapi.EndPoints -{ - public static class PizzaShopApi - { - public static void ConfigurePizzaShopApi(this WebApplication app) - { - - } - - - } -} diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopEndpoint.cs b/exercise.pizzashopapi/EndPoints/PizzaShopEndpoint.cs new file mode 100644 index 0000000..736292a --- /dev/null +++ b/exercise.pizzashopapi/EndPoints/PizzaShopEndpoint.cs @@ -0,0 +1,440 @@ +using exercise.pizzashopapi.Repository; +using Microsoft.AspNetCore.Mvc; +using exercise.pizzashopapi.Models; + +namespace exercise.pizzashopapi.EndPoints +{ + public static class PizzaShopEndpoint + { + public static void ConfigurePizzaShopEndpoint(this WebApplication app) + { + var customerGroup = app.MapGroup("customer"); + var orderGroup = app.MapGroup("order"); + var pizzaGroup = app.MapGroup("pizza"); + var toppingGroup = app.MapGroup("topping"); + + customerGroup.MapPost("/", CreateCustomer); + customerGroup.MapGet("/", GetCustomers); + customerGroup.MapGet("/{id}", GetCustomerById); + customerGroup.MapPut("/{id}", UpdateCustomer); + customerGroup.MapDelete("/{id}", DeleteCustomer); + + orderGroup.MapPost("/", CreateOrder); + orderGroup.MapGet("/", GetOrders); + orderGroup.MapGet("/{id}", GetOrderById); + orderGroup.MapPut("/{id}", UpdateOrder); + orderGroup.MapDelete("/{id}", DeleteOrder); + orderGroup.MapPost("/{orderId}/topping/{toppingId}", AddToppingToOrder); + orderGroup.MapPut("/{id}/delivered", SetOrderAsDelivered); + orderGroup.MapGet("/{id}/status", GetOrderStatus); + + pizzaGroup.MapPost("/", CreatePizza); + pizzaGroup.MapGet("/", GetPizzas); + pizzaGroup.MapGet("/{id}", GetPizzaById); + pizzaGroup.MapPut("/{id}", UpdatePizza); + pizzaGroup.MapDelete("/{id}", DeletePizza); + pizzaGroup.MapPost("/{pizzaId}/topping/{toppingId}", AddToppingToPizza); + + toppingGroup.MapPost("/", CreateTopping); + toppingGroup.MapGet("/", GetToppings); + toppingGroup.MapGet("/{id}", GetToppingById); + toppingGroup.MapPut("/{id}", UpdateTopping); + toppingGroup.MapDelete("/{id}", DeleteTopping); + } + + #region customer + + public static async Task CreateCustomer(IRepository customerRepository, string name) + { + var customer = new Customer { Name = name }; + var createdCustomer = await customerRepository.Insert(customer); + return TypedResults.Created($"/customer/{createdCustomer.Id}", createdCustomer); + } + + public static async Task GetCustomers(IRepository customerRepository) + { + var customers = await customerRepository.Get(); + return TypedResults.Ok(customers); + } + + public static async Task GetCustomerById(IRepository customerRepository, int id) + { + var customer = await customerRepository.GetById(id); + if (customer == null) + { + return TypedResults.NotFound(new { Message = "Customer not found" }); + } + return TypedResults.Ok(customer); + } + + public static async Task UpdateCustomer(IRepository customerRepository, int id, string? name) + { + var customer = await customerRepository.GetById(id); + if (customer == null) + { + return TypedResults.NotFound(new { Message = "Customer not found" }); + } + if (name != null && name != customer.Name) + { + customer.Name = name; + } + var updatedCustomer = await customerRepository.Update(customer); + return TypedResults.Ok(updatedCustomer); + } + + public static async Task DeleteCustomer(IRepository customerRepository, int id) + { + var customer = await customerRepository.GetById(id); + if (customer == null) + { + return TypedResults.NotFound(new { Message = "Customer not found" }); + } + var deletedCustomer = await customerRepository.Delete(id); + return TypedResults.Ok(deletedCustomer); + } + + #endregion + + #region order + + public static async Task CreateOrder( + IRepository orderRepository, + IRepository customerRepository, + IRepository pizzaRepository, + int customerId, + int pizzaId) + { + var customer = await customerRepository.GetById(customerId); + if (customer == null) + { + return TypedResults.NotFound(new { Message = "Customer not found" }); + } + + var pizza = await pizzaRepository.GetById(pizzaId); + if (pizza == null) + { + return TypedResults.NotFound(new { Message = "Pizza not found" }); + } + + var order = new Order + { + CustomerId = customerId, + PizzaId = pizzaId, + Status = "Preparing", + CreatedAt = DateTime.UtcNow + }; + + var createdOrder = await orderRepository.Insert(order); + + return TypedResults.Created($"/order/{createdOrder.Id}", createdOrder); + } + + + public static async Task GetOrders(IRepository orderRepository) + { + var orders = await orderRepository.GetWithIncludes( + o => o.customer, + o => o.pizza, + o => o.pizza.Toppings, + o => o.OrderToppings + ); + + var orderDetails = orders.Select(order => new + { + order.Id, + order.CustomerId, + Customer = order.customer, + order.PizzaId, + Pizza = order.pizza, + Toppings = order.pizza?.Toppings, + OrderToppings = order.OrderToppings, + order.Status, + order.CreatedAt, + order.CookingStartedAt, + order.DeliveredAt + }).ToList(); + + return TypedResults.Ok(orderDetails); + } + + public static async Task GetOrderById(IRepository orderRepository, int id) + { + var orders = await orderRepository.GetWithIncludes( + o => o.customer, + o => o.pizza, + o => o.pizza.Toppings, + o => o.OrderToppings + + ); + + var order = orders.FirstOrDefault(o => o.Id == id); + if (order == null) + { + return TypedResults.NotFound(new { Message = "Order not found" }); + } + return TypedResults.Ok(order); + } + + public static async Task UpdateOrder( + IRepository orderRepository, + IRepository customerRepository, + IRepository pizzaRepository, + int id, + int? customerId, + int? pizzaId) + { + var orders = await orderRepository.GetWithIncludes( + o => o.customer, + o => o.pizza, + o => o.pizza.Toppings, + o => o.OrderToppings + ); + + var order = orders.FirstOrDefault(o => o.Id == id); + + if (order == null) + { + return TypedResults.NotFound(new { Message = "Order not found" }); + } + + if (customerId != null) + { + var customer = await customerRepository.GetById((int)customerId); + if (customer == null) + { + return TypedResults.NotFound(new { Message = "Customer not found" }); + } + order.CustomerId = (int)customerId; + } + + if (pizzaId != null) + { + var pizza = await pizzaRepository.GetById((int)pizzaId); + if (pizza == null) + { + return TypedResults.NotFound(new { Message = "Pizza not found" }); + } + order.PizzaId = (int)pizzaId; + } + + var updatedOrder = await orderRepository.Update(order); + return TypedResults.Ok(updatedOrder); + } + + public static async Task DeleteOrder(IRepository orderRepository, int id) + { + var orders = await orderRepository.GetWithIncludes( + o => o.customer, + o => o.pizza, + o => o.pizza.Toppings, + o => o.OrderToppings + ); + + var order = orders.FirstOrDefault(o => o.Id == id); + + if (order == null) + { + return TypedResults.NotFound(new { Message = "Order not found" }); + } + + var deletedOrder = await orderRepository.Delete(id); + + return TypedResults.Ok(deletedOrder); + } + + public static async Task SetOrderAsDelivered( + IRepository orderRepository, + int orderId) + { + var order = await orderRepository.GetById(orderId); + if (order == null) + { + return TypedResults.NotFound(new { Message = "Order not found" }); + } + + order.Status = "Delivered"; + order.DeliveredAt = DateTime.UtcNow; + + var updatedOrder = await orderRepository.Update(order); + + return TypedResults.Ok(updatedOrder); + } + + public static async Task GetOrderStatus( + IRepository orderRepository, + int orderId) + { + var order = await orderRepository.GetById(orderId); + if (order == null) + { + return TypedResults.NotFound(new { Message = "Order not found" }); + } + + return TypedResults.Ok(new { order.Id, order.Status }); + } + + #endregion + + #region pizza + + public static async Task CreatePizza(IRepository pizzaRepository, string name, decimal price) + { + var pizza = new Pizza { Name = name, Price = price }; + var createdPizza = await pizzaRepository.Insert(pizza); + return TypedResults.Created($"/pizza/{createdPizza.Id}", createdPizza); + } + + public static async Task GetPizzas(IRepository pizzaRepository) + { + var pizzas = await pizzaRepository.Get(); + return TypedResults.Ok(pizzas); + } + + public static async Task GetPizzaById(IRepository pizzaRepository, int id) + { + var pizza = await pizzaRepository.GetById(id); + if (pizza == null) + { + return TypedResults.NotFound(new { Message = "Pizza not found" }); + } + return TypedResults.Ok(pizza); + } + + public static async Task UpdatePizza(IRepository pizzaRepository, int id, string? name, decimal? price) + { + var pizza = await pizzaRepository.GetById(id); + if (pizza == null) + { + return TypedResults.NotFound(new { Message = "Pizza not found" }); + } + + if (!string.IsNullOrWhiteSpace(name)) + { + pizza.Name = name; + } + if (price >= 0 && price != null) + { + pizza.Price = (decimal) price; + } + + var updatedPizza = await pizzaRepository.Update(pizza); + return TypedResults.Ok(updatedPizza); + } + + public static async Task DeletePizza(IRepository pizzaRepository, int id) + { + var pizza = await pizzaRepository.GetById(id); + if (pizza == null) + { + return TypedResults.NotFound(new { Message = "Pizza not found" }); + } + var deletedPizza = await pizzaRepository.Delete(id); + return TypedResults.Ok(deletedPizza); + } + + #endregion + + #region topping + + public static async Task CreateTopping(IRepository toppingRepository, string name, decimal price) + { + var topping = new Topping { Name = name, Price = price }; + var createdTopping = await toppingRepository.Insert(topping); + return TypedResults.Created($"/topping/{createdTopping.Id}", createdTopping); + } + + public static async Task GetToppings(IRepository toppingRepository) + { + var toppings = await toppingRepository.Get(); + return TypedResults.Ok(toppings); + } + + public static async Task GetToppingById(IRepository toppingRepository, int id) + { + var topping = await toppingRepository.GetById(id); + if (topping == null) + { + return TypedResults.NotFound(new { Message = "Topping not found" }); + } + return TypedResults.Ok(topping); + } + + public static async Task UpdateTopping(IRepository toppingRepository, int id, string? name, decimal? price) + { + var topping = await toppingRepository.GetById(id); + if (topping == null) + { + return TypedResults.NotFound(new { Message = "Topping not found" }); + } + + if (!string.IsNullOrWhiteSpace(name)) + { + topping.Name = name; + } + if (price >= 0 && price != null) + { + topping.Price = (decimal)price; + } + + var updatedTopping = await toppingRepository.Update(topping); + return TypedResults.Ok(updatedTopping); + } + + public static async Task DeleteTopping(IRepository toppingRepository, int id) + { + var topping = await toppingRepository.GetById(id); + if (topping == null) + { + return TypedResults.NotFound(new { Message = "Topping not found" }); + } + var deletedTopping = await toppingRepository.Delete(id); + return TypedResults.Ok(deletedTopping); + } + + public static async Task AddToppingToPizza( + IRepository pizzaRepository, + IRepository toppingRepository, + int pizzaId, + int toppingId) + { + var pizza = await pizzaRepository.GetWithIncludes(p => p.Toppings); + var selectedPizza = pizza.FirstOrDefault(p => p.Id == pizzaId); + + if (selectedPizza == null) + return TypedResults.NotFound(new { Message = "Pizza not found" }); + + var topping = await toppingRepository.GetById(toppingId); + + if (topping == null) + return TypedResults.NotFound(new { Message = "Topping not found" }); + + selectedPizza.Toppings ??= new List(); + selectedPizza.Toppings.Add(topping); + + await pizzaRepository.Update(selectedPizza); + return TypedResults.Ok(selectedPizza); + } + + public static async Task AddToppingToOrder( + IRepository orderToppingRepository, + IRepository orderRepository, + IRepository toppingRepository, + int orderId, + int toppingId) + { + var order = await orderRepository.GetById(orderId); + if (order == null) + return TypedResults.NotFound(new { Message = "Order not found" }); + + var topping = await toppingRepository.GetById(toppingId); + if (topping == null) + return TypedResults.NotFound(new { Message = "Topping not found" }); + + var orderTopping = new OrderTopping { OrderId = orderId, ToppingId = toppingId }; + var createdOrderTopping = await orderToppingRepository.Insert(orderTopping); + return TypedResults.Ok(createdOrderTopping); + } + + #endregion + } +} diff --git a/exercise.pizzashopapi/Models/Customer.cs b/exercise.pizzashopapi/Models/Customer.cs index 2ca83bd..dbb40fd 100644 --- a/exercise.pizzashopapi/Models/Customer.cs +++ b/exercise.pizzashopapi/Models/Customer.cs @@ -1,10 +1,16 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { + [Table("customer")] public class Customer { + [Key] + [Column("id")] public int Id { get; set; } + + [Column("name")] public string Name { get; set; } } } diff --git a/exercise.pizzashopapi/Models/Order.cs b/exercise.pizzashopapi/Models/Order.cs index fbe6113..76394a4 100644 --- a/exercise.pizzashopapi/Models/Order.cs +++ b/exercise.pizzashopapi/Models/Order.cs @@ -1,10 +1,42 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { + [Table("order")] public class Order { - - + [Key] + [Column("id")] + public int Id { get; set; } + + [ForeignKey("customer")] + [Column("customer_id")] + public int CustomerId { get; set; } + + public Customer customer { get; set; } + + [ForeignKey("pizza")] + [Column("pizza_id")] + public int PizzaId { get; set; } + + public Pizza pizza { get; set; } + + public List? OrderToppings { get; set; } + + [Column("status")] + public string Status { get; set; } = "Preparing"; + + [Column("created_at")] + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + //[Column("estimated_delivery_time")] + //public DateTime? EstimatedDeliveryTime { get; set; } + + [Column("cooking_started_at")] + public DateTime? CookingStartedAt { get; set; } + + [Column("delivered_at")] + public DateTime? DeliveredAt { get; set; } } } diff --git a/exercise.pizzashopapi/Models/OrderTopping.cs b/exercise.pizzashopapi/Models/OrderTopping.cs new file mode 100644 index 0000000..62bed73 --- /dev/null +++ b/exercise.pizzashopapi/Models/OrderTopping.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; + +namespace exercise.pizzashopapi.Models +{ + [Table("order_topping")] + public class OrderTopping + { + [Key] + [Column("id")] + public int Id { get; set; } + + [ForeignKey("order")] + [Column("order_id")] + public int OrderId { get; set; } + + [JsonIgnore] + public Order Order { get; set; } + + [ForeignKey("topping")] + [Column("topping_id")] + public int ToppingId { get; set; } + + // [JsonIgnore] + public Topping Topping { get; set; } + } +} diff --git a/exercise.pizzashopapi/Models/Pizza.cs b/exercise.pizzashopapi/Models/Pizza.cs index 5c085ec..8291395 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] + [Column("id")] public int Id { get; set; } + + [Column("name")] public string Name { get; set; } + + [Column("price")] public decimal Price { get; set; } + public List? Toppings { get; set; } } } \ No newline at end of file diff --git a/exercise.pizzashopapi/Models/Topping.cs b/exercise.pizzashopapi/Models/Topping.cs new file mode 100644 index 0000000..aecdbbc --- /dev/null +++ b/exercise.pizzashopapi/Models/Topping.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace exercise.pizzashopapi.Models +{ + [Table("topping")] + public class Topping + { + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("name")] + public string Name { get; set; } + + [Column("price")] + public decimal Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/Program.cs b/exercise.pizzashopapi/Program.cs index c04a440..6f77bed 100644 --- a/exercise.pizzashopapi/Program.cs +++ b/exercise.pizzashopapi/Program.cs @@ -1,21 +1,25 @@ using exercise.pizzashopapi.Data; using exercise.pizzashopapi.EndPoints; +using exercise.pizzashopapi.Models; using exercise.pizzashopapi.Repository; var builder = WebApplication.CreateBuilder(args); -// Add services to the container. - builder.Services.AddControllers(); -builder.Services.AddScoped(); -builder.Services.AddDbContext(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +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.AddHostedService(); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); -// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); @@ -28,7 +32,7 @@ app.MapControllers(); -app.ConfigurePizzaShopApi(); +app.ConfigurePizzaShopEndpoint(); app.SeedPizzaShopApi(); diff --git a/exercise.pizzashopapi/Repository/IRepository.cs b/exercise.pizzashopapi/Repository/IRepository.cs index b114ea8..76c6a86 100644 --- a/exercise.pizzashopapi/Repository/IRepository.cs +++ b/exercise.pizzashopapi/Repository/IRepository.cs @@ -1,11 +1,26 @@ -using exercise.pizzashopapi.Models; +using System.Linq.Expressions; +using exercise.pizzashopapi.Models; namespace exercise.pizzashopapi.Repository { - public interface IRepository + // CRUD = Create, Read, Update, Delete + public interface IRepository { - Task> GetOrdersByCustomer(int id); - + // Create + Task Insert(T entity); + // Read + Task> Get(); + Task GetById(int id); + Task> GetWithIncludes(params Expression>[] includes); + + // Update + Task Update(T entity); + + // Delete + Task Delete(object id); + + // Save + Task Save(); } } diff --git a/exercise.pizzashopapi/Repository/Repository.cs b/exercise.pizzashopapi/Repository/Repository.cs index e109fce..1be32b9 100644 --- a/exercise.pizzashopapi/Repository/Repository.cs +++ b/exercise.pizzashopapi/Repository/Repository.cs @@ -1,14 +1,86 @@ -using exercise.pizzashopapi.Data; +using System.Linq.Expressions; +using exercise.pizzashopapi.Data; using exercise.pizzashopapi.Models; +using Microsoft.EntityFrameworkCore; namespace exercise.pizzashopapi.Repository { - public class Repository : IRepository + // CRUD = Create, Read, Update, Delete + public class Repository : IRepository where T : class { - private DataContext _db; - public Task> GetOrdersByCustomer(int id) + private DatabaseContext _db; + private DbSet _table = null!; + + public Repository(DatabaseContext dataContext) + { + _db = dataContext; + _table = _db.Set(); + } + + // Create + public async Task Insert(T entity) + { + try + { + _table.Add(entity); + await _db.SaveChangesAsync(); + return entity; + } + catch (Exception ex) + { + throw new Exception("Error inserting entity", ex); + } + } + + // Read + public async Task> Get() + { + return await _table.ToListAsync(); + } + + + public async Task GetById(int id) + { + return await _table.FindAsync(id); + } + + public async Task> GetWithIncludes(params Expression>[] includes) + { + IQueryable query = _table; + foreach (var include in includes) + { + query = query.Include(include); + } + return await query.ToListAsync(); + } + + // Update + public async Task Update(T entity) + { + _table.Attach(entity); + _db.Entry(entity).State = EntityState.Modified; + await _db.SaveChangesAsync(); + return entity; + } + + // Delete + public async Task Delete(object id) + { + T entity = await _table.FindAsync(id); + if (entity == null) + { + throw new KeyNotFoundException("Entity not found for deletion."); + } + + _table.Remove(entity); + await _db.SaveChangesAsync(); + return entity; + } + + // Save + public async Task Save() { - throw new NotImplementedException(); + await _db.SaveChangesAsync(); } } } diff --git a/exercise.pizzashopapi/Services/OrderStatusUpdaterService.cs b/exercise.pizzashopapi/Services/OrderStatusUpdaterService.cs new file mode 100644 index 0000000..5fbb7fd --- /dev/null +++ b/exercise.pizzashopapi/Services/OrderStatusUpdaterService.cs @@ -0,0 +1,50 @@ +using exercise.pizzashopapi.Models; +using exercise.pizzashopapi.Repository; + +public class OrderStatusUpdaterService : BackgroundService +{ + private readonly IServiceScopeFactory _scopeFactory; + + public OrderStatusUpdaterService(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + using (var scope = _scopeFactory.CreateScope()) + { + var orderRepository = scope.ServiceProvider.GetRequiredService>(); + + await UpdateOrderStatuses(orderRepository); + } + + await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); + } + } + + private async Task UpdateOrderStatuses(IRepository orderRepository) + { + var orders = await orderRepository.Get(); + + foreach (var order in orders) + { + if (order.Status == "Preparing" && DateTime.UtcNow - order.CreatedAt > TimeSpan.FromMinutes(3)) + { + order.Status = "Cooking"; + order.CookingStartedAt = DateTime.UtcNow; + + await orderRepository.Update(order); + } + else if (order.Status == "Cooking" && order.CookingStartedAt != null && DateTime.UtcNow - order.CookingStartedAt.Value > TimeSpan.FromMinutes(15)) + { + order.Status = "Delivered"; + order.DeliveredAt = DateTime.UtcNow; + + await orderRepository.Update(order); + } + } + } +} diff --git a/exercise.pizzashopapi/exercise.pizzashopapi.csproj b/exercise.pizzashopapi/exercise.pizzashopapi.csproj index 624203b..e2c5efb 100644 --- a/exercise.pizzashopapi/exercise.pizzashopapi.csproj +++ b/exercise.pizzashopapi/exercise.pizzashopapi.csproj @@ -11,18 +11,18 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + +