From 8ffdea83c843eb6b4d6e5ee3e46f0ab2a7fd0dc9 Mon Sep 17 00:00:00 2001 From: erius Date: Wed, 30 Oct 2024 06:11:07 +0300 Subject: [PATCH] Implemented delivery model and filtering --- .../.idea/dictionaries/egor.xml | 3 + DeliveryWebApi/DeliveryWebApi.csproj | 4 +- DeliveryWebApi/Model/BaseEntity.cs | 4 +- DeliveryWebApi/Model/DeliveryContext.cs | 18 ----- DeliveryWebApi/Model/DeliveryDbContext.cs | 46 +++++++++++ DeliveryWebApi/Model/District.cs | 2 +- DeliveryWebApi/Model/Order.cs | 2 +- DeliveryWebApi/Program.cs | 76 +++++++++++++++---- DeliveryWebApi/appsettings.Development.json | 3 +- DeliveryWebApi/appsettings.json | 3 +- EffetiveTask.sln.DotSettings.user | 2 + 11 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 .idea/.idea.EffetiveTask/.idea/dictionaries/egor.xml delete mode 100644 DeliveryWebApi/Model/DeliveryContext.cs create mode 100644 DeliveryWebApi/Model/DeliveryDbContext.cs diff --git a/.idea/.idea.EffetiveTask/.idea/dictionaries/egor.xml b/.idea/.idea.EffetiveTask/.idea/dictionaries/egor.xml new file mode 100644 index 0000000..34f0fc0 --- /dev/null +++ b/.idea/.idea.EffetiveTask/.idea/dictionaries/egor.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/DeliveryWebApi/DeliveryWebApi.csproj b/DeliveryWebApi/DeliveryWebApi.csproj index 5b4161d..1eb0032 100644 --- a/DeliveryWebApi/DeliveryWebApi.csproj +++ b/DeliveryWebApi/DeliveryWebApi.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/DeliveryWebApi/Model/BaseEntity.cs b/DeliveryWebApi/Model/BaseEntity.cs index 8414305..a231851 100644 --- a/DeliveryWebApi/Model/BaseEntity.cs +++ b/DeliveryWebApi/Model/BaseEntity.cs @@ -1,6 +1,8 @@ +using Microsoft.EntityFrameworkCore; + namespace DeliveryWebApi.Model; public class BaseEntity { - public Guid Id { get; init; } = Guid.NewGuid(); + public long Id { get; init; } } \ No newline at end of file diff --git a/DeliveryWebApi/Model/DeliveryContext.cs b/DeliveryWebApi/Model/DeliveryContext.cs deleted file mode 100644 index d01c552..0000000 --- a/DeliveryWebApi/Model/DeliveryContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace DeliveryWebApi.Model; - -public class DeliveryContext(DbContextOptions options) : DbContext(options) -{ - public DbSet Districts { get; init; } - public DbSet Orders { get; init; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity() - .HasMany(e => e.Orders) - .WithOne(e => e.District) - .HasForeignKey(e => e.DistrictId) - .IsRequired(); - } -} \ No newline at end of file diff --git a/DeliveryWebApi/Model/DeliveryDbContext.cs b/DeliveryWebApi/Model/DeliveryDbContext.cs new file mode 100644 index 0000000..3400673 --- /dev/null +++ b/DeliveryWebApi/Model/DeliveryDbContext.cs @@ -0,0 +1,46 @@ +using System.Collections.Immutable; +using Microsoft.EntityFrameworkCore; + +namespace DeliveryWebApi.Model; + +public class DeliveryDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Districts { get; init; } + public DbSet Orders { get; init; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasMany(d => d.Orders) + .WithOne(o => o.District) + .HasForeignKey(o => o.DistrictId) + .IsRequired(); + + modelBuilder.Entity() + .HasIndex(d => d.Name) + .IsUnique(); + + // random districts + var districtNames = new List + { + "Viborgskiy", "Primorskiy", "Kalininskiy", "Petrogradskiy", "Center", "Admiralteyskiy", "Vasileostrovskiy", + "Kirovskiy", "Krasnoselskiy", "Moskovskiy", "Frunzenskiy", "Nevskiy", "Krasnogvardeyskiy", "Kolpinskiy", + "Pushkinskiy", "Pavlovskiy", "Petrodvortsoviy", "Lomonosovskiy", "Kurortniy", "Kronshtadskiy" + }; + modelBuilder.Entity() + .HasData(districtNames.Select((n, i) => new District { Id = i + 1, Name = n })); + + // random orders + var rnd = new Random(); + var startDate = new DateTime(2020, 1, 1); + var range = (DateTime.Today - startDate).Days; + modelBuilder.Entity() + .HasData(Enumerable.Range(1, 100).Select(i => + { + var districtId = rnd.Next(0, districtNames.Count) + 1; + var weight = rnd.NextSingle() * 10 + 0.1f; + var date = startDate.AddDays(rnd.Next(range)); + return new Order { Id = i, DistrictId = districtId, Weight = weight, DeliveryDate = date }; + })); + } +} \ No newline at end of file diff --git a/DeliveryWebApi/Model/District.cs b/DeliveryWebApi/Model/District.cs index f02f328..d2eb3fc 100644 --- a/DeliveryWebApi/Model/District.cs +++ b/DeliveryWebApi/Model/District.cs @@ -1,6 +1,6 @@ namespace DeliveryWebApi.Model; -public class District(string name) : BaseEntity +public class District : BaseEntity { public string Name { get; init; } public ICollection Orders { get; init; } = new List(); diff --git a/DeliveryWebApi/Model/Order.cs b/DeliveryWebApi/Model/Order.cs index 3baaef6..669c833 100644 --- a/DeliveryWebApi/Model/Order.cs +++ b/DeliveryWebApi/Model/Order.cs @@ -3,7 +3,7 @@ namespace DeliveryWebApi.Model; public class Order : BaseEntity { public float Weight { get; init; } - public Guid DistrictId { get; init; } + public long DistrictId { get; init; } public District District { get; init; } = null!; public DateTime DeliveryDate { get; init; } } \ No newline at end of file diff --git a/DeliveryWebApi/Program.cs b/DeliveryWebApi/Program.cs index 5edc626..94454cb 100644 --- a/DeliveryWebApi/Program.cs +++ b/DeliveryWebApi/Program.cs @@ -1,38 +1,86 @@ using DeliveryWebApi.Model; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -if (builder.Configuration.GetValue("UseInMemoryDatabase")) - builder.Services.AddDbContext(options => options.UseInMemoryDatabase("Delivery")); -else + +// storage setup +// either use in-memory storage or postgres depending on cfg +builder.Services.AddDbContext(options => { - builder.Services.AddDbContext(options => + if (builder.Configuration.GetValue("UseInMemoryDatabase")) + options.UseInMemoryDatabase("Delivery"); + else { var connectionString = builder.Configuration.GetConnectionString("DatabaseConnection"); options.UseNpgsql(connectionString); - }); -} - + } +}); var app = builder.Build(); -// swagger setup if (app.Environment.IsDevelopment()) { + // swagger setup app.UseSwagger(); app.UseSwaggerUI(); + + // migrate model to seed random test values for developers + app.Services.CreateScope().ServiceProvider.GetRequiredService().Database.EnsureCreated(); } app.UseHttpsRedirection(); -app.MapPut("/addOrder", (float weight, long districtId, DateTime deliveryTime) => - { - - }) - .WithName("GetWeatherForecast") - .WithOpenApi(); +app.MapPut("/addDistrict", (DeliveryDbContext context, string name) => +{ + if (context.Districts.Any(d => d.Name == name)) + return Results.BadRequest(new { Error = "District with the same name already exists" }); + var district = new District{ Name = name }; + context.Districts.Add(district); + var updated = context.SaveChanges(); + if (updated <= 0) + return Results.Json(new { Error = "Something went wrong while adding a new district" }, statusCode: 500); + return Results.Ok(new { district.Id }); +}); + +app.MapGet("/getDistrict/{id:long}", (DeliveryDbContext context, long id) => +{ + var district = context.Districts.FirstOrDefault(d => d.Id == id); + if (district == null) + return Results.NotFound(new { Error = "District with the given id does not exist" }); + return Results.Ok(new { district.Id, district.Name }); +}); + +app.MapPut("/addOrder", (DeliveryDbContext context, long districtId, float weight, DateTime deliveryDate) => +{ + var order = new Order{ DistrictId = districtId, Weight = weight, DeliveryDate = deliveryDate }; + context.Orders.Add(order); + var updated = context.SaveChanges(); + if (updated <= 0) + return Results.Json(new { Error = "Something went wrong while adding a new order" }, statusCode: 500); + return Results.Ok(new { order.Id }); +}); + +app.MapGet("/getOrder/{id:long}", (DeliveryDbContext context, long id) => +{ + var order = context.Orders.FirstOrDefault(o => o.Id == id); + if (order == null) + return Results.NotFound(new { Error = "Order with the given id does not exist" }); + return Results.Ok(new { order.Id, order.DistrictId, order.Weight, order.DeliveryDate }); +}); + +app.MapGet("/getOrders", (DeliveryDbContext context, string? districtName, DateTime? firstDeliveryDate) => +{ + var orders = context.Districts + .Where(d => districtName == null || d.Name == districtName) + .SelectMany(d => d.Orders) + .Where(o => firstDeliveryDate == null || o.DeliveryDate >= firstDeliveryDate) + .OrderBy(o => o.DeliveryDate) + .Select(o => new { o.Id, o.District.Name, o.DeliveryDate, o.Weight }); + return Results.Ok(orders); +}); app.Run(); diff --git a/DeliveryWebApi/appsettings.Development.json b/DeliveryWebApi/appsettings.Development.json index 0c208ae..696f07d 100644 --- a/DeliveryWebApi/appsettings.Development.json +++ b/DeliveryWebApi/appsettings.Development.json @@ -4,5 +4,6 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } - } + }, + "UseInMemoryDatabase": true } diff --git a/DeliveryWebApi/appsettings.json b/DeliveryWebApi/appsettings.json index 10f68b8..741c868 100644 --- a/DeliveryWebApi/appsettings.json +++ b/DeliveryWebApi/appsettings.json @@ -5,5 +5,6 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "UseInMemoryDatabase": true } diff --git a/EffetiveTask.sln.DotSettings.user b/EffetiveTask.sln.DotSettings.user index eae4c8e..f67b3cc 100644 --- a/EffetiveTask.sln.DotSettings.user +++ b/EffetiveTask.sln.DotSettings.user @@ -1,2 +1,4 @@  + ForceIncluded + ForceIncluded ForceIncluded \ No newline at end of file