Skip to content

Commit

Permalink
Add lab 1 end (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
DamianEdwards authored Feb 7, 2024
1 parent 8d7143c commit d6502f8
Show file tree
Hide file tree
Showing 37 changed files with 2,185 additions and 0 deletions.
21 changes: 21 additions & 0 deletions labs/1-Create-Catalog-API/end/Catalog.API/Catalog.API.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Catalog.Data\Catalog.Data.csproj" />
<ProjectReference Include="..\eShop.ServiceDefaults\eShop.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions labs/1-Create-Catalog-API/end/Catalog.API/Catalog.API.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@Catalog.API_HostAddress = http://localhost:5180

GET {{Catalog.API_HostAddress}}/items/
Accept: application/json

###
31 changes: 31 additions & 0 deletions labs/1-Create-Catalog-API/end/Catalog.API/CatalogApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using eShop.Catalog.API.Model;
using eShop.Catalog.Data;
using Microsoft.EntityFrameworkCore;

namespace Microsoft.AspNetCore.Builder;

public static class CatalogApi
{
public static IEndpointRouteBuilder MapCatalogApi(this IEndpointRouteBuilder app)
{
app.MapGet("/items", async ([AsParameters] PaginationRequest paginationRequest, CatalogDbContext db) =>
{
var pageSize = paginationRequest.PageSize;
var pageIndex = paginationRequest.PageIndex;

var totalItems = await db.CatalogItems
.LongCountAsync();

var itemsOnPage = await db.CatalogItems
.OrderBy(c => c.Name)
.Skip(pageSize * pageIndex)
.Take(pageSize)
.AsNoTracking()
.ToListAsync();

return new PaginatedItems<CatalogItem>(pageIndex, pageSize, totalItems, itemsOnPage);
});

return app;
}
}
13 changes: 13 additions & 0 deletions labs/1-Create-Catalog-API/end/Catalog.API/Model/PaginatedItems.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace eShop.Catalog.API.Model;

public class PaginatedItems<TItem>(int pageIndex, int pageSize, long count, IEnumerable<TItem> data)
where TItem : class
{
public int PageIndex { get; } = pageIndex;

public int PageSize { get; } = pageSize;

public long Count { get; } = count;

public IEnumerable<TItem> Data { get; } = data;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace eShop.Catalog.API.Model;

public readonly struct PaginationRequest(int pageSize = 5, int pageIndex = 0)
{
public readonly int PageSize { get; } = pageSize;

public readonly int PageIndex { get; } = pageIndex;
}
28 changes: 28 additions & 0 deletions labs/1-Create-Catalog-API/end/Catalog.API/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using eShop.Catalog.Data;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.AddNpgsqlDbContext<CatalogDbContext>("CatalogDB");

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.MapDefaultEndpoints();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.MapCatalogApi();

app.Run();

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:63531",
"sslPort": 0
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5180",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions labs/1-Create-Catalog-API/end/Catalog.API/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>eShop.Catalog.Data.Manager</RootNamespace>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Catalog.Data\Catalog.Data.csproj" />
<ProjectReference Include="..\eShop.ServiceDefaults\eShop.ServiceDefaults.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-preview.3.24081.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Compile Include="..\Shared\ActivityExtensions.cs" Link="Extensions\ActivityExtensions.cs" />
<Compile Include="..\Shared\MigrateDbContextExtensions.cs" Link="Extensions\MigrateDbContextExtensions.cs" />
</ItemGroup>

<ItemGroup>
<Folder Include="Migrations\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Npgsql;

namespace eShop.Catalog.Data.Manager;

public partial class CatalogContextSeed(IWebHostEnvironment env, ILogger<CatalogContextSeed> logger)
: IDbSeeder<CatalogDbContext>
{
public async Task SeedAsync(CatalogDbContext context)
{
var contentRootPath = env.ContentRootPath;
var picturePath = env.WebRootPath;

// Workaround from https://github.com/npgsql/efcore.pg/issues/292#issuecomment-388608426
context.Database.OpenConnection();
((NpgsqlConnection)context.Database.GetDbConnection()).ReloadTypes();

if (!context.CatalogItems.Any())
{
var sourcePath = Path.Combine(contentRootPath, "Setup", "catalog.json");
var sourceJson = File.ReadAllText(sourcePath);
var sourceItems = JsonSerializer.Deserialize<CatalogSourceEntry[]>(sourceJson)
?? throw new InvalidOperationException($"Seed data file was found but it contained no data: '{sourcePath}'");

context.CatalogBrands.RemoveRange(context.CatalogBrands);
await context.CatalogBrands.AddRangeAsync(sourceItems.Select(x => x.Brand).Distinct()
.Select(brandName => new CatalogBrand { Brand = brandName }));
logger.LogInformation("Seeded catalog with {NumBrands} brands", context.CatalogBrands.Count());

context.CatalogTypes.RemoveRange(context.CatalogTypes);
await context.CatalogTypes.AddRangeAsync(sourceItems.Select(x => x.Type).Distinct()
.Select(typeName => new CatalogType { Type = typeName }));
logger.LogInformation("Seeded catalog with {NumTypes} types", context.CatalogTypes.Count());

await context.SaveChangesAsync();

var brandIdsByName = await context.CatalogBrands.ToDictionaryAsync(x => x.Brand, x => x.Id);
var typeIdsByName = await context.CatalogTypes.ToDictionaryAsync(x => x.Type, x => x.Id);

await context.CatalogItems.AddRangeAsync(sourceItems.Select(source => new CatalogItem
{
Id = source.Id,
Name = source.Name,
Description = source.Description,
Price = source.Price,
CatalogBrandId = brandIdsByName[source.Brand],
CatalogTypeId = typeIdsByName[source.Type],
AvailableStock = 100,
MaxStockThreshold = 200,
RestockThreshold = 10,
PictureFileName = $"{source.Id}.webp"
}));

logger.LogInformation("Seeded catalog with {NumItems} items", context.CatalogItems.Count());

await context.SaveChangesAsync();
}
}

private class CatalogSourceEntry
{
public int Id { get; set; }
public required string Type { get; set; }
public required string Brand { get; set; }
public required string Name { get; set; }
public required string Description { get; set; }
public decimal Price { get; set; }
}
}
Loading

0 comments on commit d6502f8

Please sign in to comment.