Implemented delivery model and filtering
This commit is contained in:
parent
9d232598cd
commit
8ffdea83c8
11 changed files with 124 additions and 39 deletions
3
.idea/.idea.EffetiveTask/.idea/dictionaries/egor.xml
Normal file
3
.idea/.idea.EffetiveTask/.idea/dictionaries/egor.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="egor" />
|
||||||
|
</component>
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10"/>
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0-rc.2.24474.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.10" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0-rc.2" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DeliveryWebApi.Model;
|
namespace DeliveryWebApi.Model;
|
||||||
|
|
||||||
public class BaseEntity
|
public class BaseEntity
|
||||||
{
|
{
|
||||||
public Guid Id { get; init; } = Guid.NewGuid();
|
public long Id { get; init; }
|
||||||
}
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DeliveryWebApi.Model;
|
|
||||||
|
|
||||||
public class DeliveryContext(DbContextOptions<DeliveryContext> options) : DbContext(options)
|
|
||||||
{
|
|
||||||
public DbSet<District> Districts { get; init; }
|
|
||||||
public DbSet<Order> Orders { get; init; }
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
modelBuilder.Entity<District>()
|
|
||||||
.HasMany(e => e.Orders)
|
|
||||||
.WithOne(e => e.District)
|
|
||||||
.HasForeignKey(e => e.DistrictId)
|
|
||||||
.IsRequired();
|
|
||||||
}
|
|
||||||
}
|
|
46
DeliveryWebApi/Model/DeliveryDbContext.cs
Normal file
46
DeliveryWebApi/Model/DeliveryDbContext.cs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DeliveryWebApi.Model;
|
||||||
|
|
||||||
|
public class DeliveryDbContext(DbContextOptions<DeliveryDbContext> options) : DbContext(options)
|
||||||
|
{
|
||||||
|
public DbSet<District> Districts { get; init; }
|
||||||
|
public DbSet<Order> Orders { get; init; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<District>()
|
||||||
|
.HasMany(d => d.Orders)
|
||||||
|
.WithOne(o => o.District)
|
||||||
|
.HasForeignKey(o => o.DistrictId)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
modelBuilder.Entity<District>()
|
||||||
|
.HasIndex(d => d.Name)
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
// random districts
|
||||||
|
var districtNames = new List<string>
|
||||||
|
{
|
||||||
|
"Viborgskiy", "Primorskiy", "Kalininskiy", "Petrogradskiy", "Center", "Admiralteyskiy", "Vasileostrovskiy",
|
||||||
|
"Kirovskiy", "Krasnoselskiy", "Moskovskiy", "Frunzenskiy", "Nevskiy", "Krasnogvardeyskiy", "Kolpinskiy",
|
||||||
|
"Pushkinskiy", "Pavlovskiy", "Petrodvortsoviy", "Lomonosovskiy", "Kurortniy", "Kronshtadskiy"
|
||||||
|
};
|
||||||
|
modelBuilder.Entity<District>()
|
||||||
|
.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<Order>()
|
||||||
|
.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 };
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace DeliveryWebApi.Model;
|
namespace DeliveryWebApi.Model;
|
||||||
|
|
||||||
public class District(string name) : BaseEntity
|
public class District : BaseEntity
|
||||||
{
|
{
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
public ICollection<Order> Orders { get; init; } = new List<Order>();
|
public ICollection<Order> Orders { get; init; } = new List<Order>();
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace DeliveryWebApi.Model;
|
||||||
public class Order : BaseEntity
|
public class Order : BaseEntity
|
||||||
{
|
{
|
||||||
public float Weight { get; init; }
|
public float Weight { get; init; }
|
||||||
public Guid DistrictId { get; init; }
|
public long DistrictId { get; init; }
|
||||||
public District District { get; init; } = null!;
|
public District District { get; init; } = null!;
|
||||||
public DateTime DeliveryDate { get; init; }
|
public DateTime DeliveryDate { get; init; }
|
||||||
}
|
}
|
|
@ -1,38 +1,86 @@
|
||||||
using DeliveryWebApi.Model;
|
using DeliveryWebApi.Model;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Internal;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
if (builder.Configuration.GetValue<bool>("UseInMemoryDatabase"))
|
|
||||||
builder.Services.AddDbContext<DeliveryContext>(options => options.UseInMemoryDatabase("Delivery"));
|
// storage setup
|
||||||
else
|
// either use in-memory storage or postgres depending on cfg
|
||||||
|
builder.Services.AddDbContext<DeliveryDbContext>(options =>
|
||||||
{
|
{
|
||||||
builder.Services.AddDbContext<DeliveryContext>(options =>
|
if (builder.Configuration.GetValue<bool>("UseInMemoryDatabase"))
|
||||||
|
options.UseInMemoryDatabase("Delivery");
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var connectionString = builder.Configuration.GetConnectionString("DatabaseConnection");
|
var connectionString = builder.Configuration.GetConnectionString("DatabaseConnection");
|
||||||
options.UseNpgsql(connectionString);
|
options.UseNpgsql(connectionString);
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// swagger setup
|
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
// swagger setup
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
// migrate model to seed random test values for developers
|
||||||
|
app.Services.CreateScope().ServiceProvider.GetRequiredService<DeliveryDbContext>().Database.EnsureCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.MapPut("/addOrder", (float weight, long districtId, DateTime deliveryTime) =>
|
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" });
|
||||||
.WithName("GetWeatherForecast")
|
var district = new District{ Name = name };
|
||||||
.WithOpenApi();
|
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();
|
app.Run();
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"UseInMemoryDatabase": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*",
|
||||||
|
"UseInMemoryDatabase": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADateTime_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F14a2b70486934722acea07b38045413ab2d200_003F3e_003F15560e7c_003FDateTime_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AResults_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F18bfc57084254648b9740270d0ce5e20ed10_003Fea_003Fc087adef_003FResults_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWebApplication_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F847c7bc70f83436e9c281d8cea5531a17d10_003F7f_003F26f6c2e2_003FWebApplication_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWebApplication_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F847c7bc70f83436e9c281d8cea5531a17d10_003F7f_003F26f6c2e2_003FWebApplication_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
Loading…
Reference in a new issue