Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lab 3 src starting point #5

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Apis/CatalogApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using eShop.Catalog.API;
using eShop.Catalog.API.Model;
using eShop.Catalog.Data;

namespace Microsoft.AspNetCore.Builder;

public static class CatalogApi
{
private static readonly FileExtensionContentTypeProvider _fileContentTypeProvider = new();

public static IEndpointRouteBuilder MapCatalogApi(this IEndpointRouteBuilder app)
{
// Routes for querying catalog items.
app.MapGet("/items", GetAllItems);
app.MapGet("/items/by", GetItemsByIds);
app.MapGet("/items/{id:int}", GetItemById);
app.MapGet("/items/by/{name:minlength(1)}", GetItemsByName);
app.MapGet("/items/{catalogItemId:int}/pic", GetItemPictureById);

// Routes for resolving catalog items by type and brand.
app.MapGet("/items/type/{typeId}/brand/{brandId?}", GetItemsByBrandAndTypeId);
app.MapGet("/items/type/all/brand/{brandId:int?}", GetItemsByBrandId);
app.MapGet("/catalogtypes", async (CatalogDbContext context) => await context.CatalogTypes.OrderBy(x => x.Type).AsNoTracking().ToListAsync());
app.MapGet("/catalogbrands", async (CatalogDbContext context) => await context.CatalogBrands.OrderBy(x => x.Brand).AsNoTracking().ToListAsync());

return app;
}

public static async Task<Results<Ok<PaginatedItems<CatalogItem>>, BadRequest<string>>> GetAllItems(
[AsParameters] PaginationRequest paginationRequest,
[AsParameters] CatalogServices services)
{
var pageSize = paginationRequest.PageSize;
var pageIndex = paginationRequest.PageIndex;

var totalItems = await services.DbContext.CatalogItems
.LongCountAsync();

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

ChangeUriPlaceholder(services.Options.Value, itemsOnPage);

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

public static async Task<Ok<List<CatalogItem>>> GetItemsByIds(
[AsParameters] CatalogServices services,
int[] ids)
{
var items = await services.DbContext.CatalogItems
.Where(item => ids.Contains(item.Id))
.AsNoTracking()
.ToListAsync();

ChangeUriPlaceholder(services.Options.Value, items);

return TypedResults.Ok(items);
}

public static async Task<Results<Ok<CatalogItem>, NotFound, BadRequest<string>>> GetItemById(
[AsParameters] CatalogServices services,
int id)
{
if (id <= 0)
{
return TypedResults.BadRequest("Id is not valid.");
}

var item = await services.DbContext.CatalogItems
.Include(ci => ci.CatalogBrand)
.AsNoTracking()
.SingleOrDefaultAsync(ci => ci.Id == id);

if (item == null)
{
return TypedResults.NotFound();
}

item.PictureUri = services.Options.Value.GetPictureUrl(item.Id);

return TypedResults.Ok(item);
}

public static async Task<Ok<PaginatedItems<CatalogItem>>> GetItemsByName(
[AsParameters] PaginationRequest paginationRequest,
[AsParameters] CatalogServices services,
string name)
{
var pageSize = paginationRequest.PageSize;
var pageIndex = paginationRequest.PageIndex;

var totalItems = await services.DbContext.CatalogItems
.Where(c => c.Name.StartsWith(name))
.LongCountAsync();

var itemsOnPage = await services.DbContext.CatalogItems
.Where(c => c.Name.StartsWith(name))
.Skip(pageSize * pageIndex)
.Take(pageSize)
.AsNoTracking()
.ToListAsync();

ChangeUriPlaceholder(services.Options.Value, itemsOnPage);

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

public static async Task<Results<NotFound, PhysicalFileHttpResult>> GetItemPictureById(CatalogDbContext context, IWebHostEnvironment environment, int catalogItemId)
{
var item = await context.CatalogItems
.AsNoTracking()
.FirstOrDefaultAsync(i => i.Id == catalogItemId);

if (item is null)
{
return TypedResults.NotFound();
}

var path = GetFullPath(environment.ContentRootPath, item.PictureFileName);

var imageFileExtension = Path.GetExtension(item.PictureFileName);
_fileContentTypeProvider.TryGetContentType(imageFileExtension, out var contentType);
var lastModified = File.GetLastWriteTimeUtc(path);

return TypedResults.PhysicalFile(path, contentType, lastModified: lastModified);
}

public static async Task<Results<BadRequest<string>, RedirectToRouteHttpResult, Ok<PaginatedItems<CatalogItem>>>> GetItemsBySemanticRelevance(
[AsParameters] PaginationRequest paginationRequest,
[AsParameters] CatalogServices services,
string text)
{
return await GetItemsByName(paginationRequest, services, text);
}

public static async Task<Ok<PaginatedItems<CatalogItem>>> GetItemsByBrandAndTypeId(
[AsParameters] PaginationRequest paginationRequest,
[AsParameters] CatalogServices services,
int typeId,
int? brandId)
{
var pageSize = paginationRequest.PageSize;
var pageIndex = paginationRequest.PageIndex;

var query = services.DbContext.CatalogItems.AsQueryable();
query = query.Where(c => c.CatalogTypeId == typeId);

if (brandId is not null)
{
query = query.Where(c => c.CatalogBrandId == brandId);
}

var totalItems = await query
.LongCountAsync();

var itemsOnPage = await query
.Skip(pageSize * pageIndex)
.Take(pageSize)
.AsNoTracking()
.ToListAsync();

ChangeUriPlaceholder(services.Options.Value, itemsOnPage);

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

public static async Task<Ok<PaginatedItems<CatalogItem>>> GetItemsByBrandId(
[AsParameters] PaginationRequest paginationRequest,
[AsParameters] CatalogServices services,
int? brandId)
{
var pageSize = paginationRequest.PageSize;
var pageIndex = paginationRequest.PageIndex;

var query = (IQueryable<CatalogItem>)services.DbContext.CatalogItems;

if (brandId is not null)
{
query = query.Where(ci => ci.CatalogBrandId == brandId);
}

var totalItems = await query
.LongCountAsync();

var itemsOnPage = await query
.Skip(pageSize * pageIndex)
.Take(pageSize)
.AsNoTracking()
.ToListAsync();

ChangeUriPlaceholder(services.Options.Value, itemsOnPage);

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

private static void ChangeUriPlaceholder(CatalogOptions options, List<CatalogItem> items)
{
foreach (var item in items)
{
item.PictureUri = options.GetPictureUrl(item.Id);
}
}

public static string GetFullPath(string contentRootPath, string pictureFileName) =>
Path.Combine(contentRootPath, "Pics", pictureFileName);
}
15 changes: 15 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Catalog.API.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>eShop.Catalog.API</RootNamespace>
<UserSecretsId>d1b521ec-3411-4d39-98c6-8509466ed471</UserSecretsId>
<Nullable>enable</Nullable>
</PropertyGroup>

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

</Project>
10 changes: 10 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Catalog.API.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@Catalog.API_HostAddress = http://localhost:5222


GET {{Catalog.API_HostAddress}}/api/v1/catalog/items

###

GET {{Catalog.API_HostAddress}}/api/v1/catalog/items/type/1/brand/2

###
18 changes: 18 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/CatalogOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Globalization;

namespace eShop.Catalog.API;

public class CatalogOptions
{
public string ApiBasePath { get; set; } = "/api/v1/catalog/";

public string? PicBaseAddress { get; set; } // Set by hosting environment

public string PicBasePathFormat { get; set; } = "items/{0}/pic/";

public string GetPictureUrl(int catalogItemId)
{
// PERF: Not ideal
return PicBaseAddress + ApiBasePath + string.Format(CultureInfo.InvariantCulture, PicBasePathFormat, catalogItemId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using eShop.Catalog.API;
using eShop.Catalog.Data;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Hosting;

public static class HostingExtensions
{
public static void AddApplicationServices(this IHostApplicationBuilder builder)
{
builder.AddNpgsqlDbContext<CatalogDbContext>("CatalogDB");

builder.Services.Configure<CatalogOptions>(builder.Configuration.GetSection(nameof(CatalogOptions)));
}

public static TOptions GetOptions<TOptions>(this IHost host)
where TOptions : class, new()
{
return host.Services.GetRequiredService<IOptions<TOptions>>().Value;
}
}
13 changes: 13 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Model/CatalogServices.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.Extensions.Options;
using eShop.Catalog.Data;

namespace eShop.Catalog.API.Model;

public readonly struct CatalogServices(CatalogDbContext dbContext, IOptions<CatalogOptions> options, ILogger<CatalogServices> logger)
{
public CatalogDbContext DbContext { get; } = dbContext;

public IOptions<CatalogOptions> Options { get; } = options;

public ILogger<CatalogServices> Logger { get; } = logger;
};
12 changes: 12 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Model/PaginatedItems.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace eShop.Catalog.API.Model;

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

public int PageSize { get; } = pageSize;

public long Count { get; } = count;

public IEnumerable<TEntity> 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 = 0, int pageIndex = 0)
{
public readonly int PageSize { get; } = pageSize;

public readonly int PageIndex { get; } = pageIndex;
}
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/1.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/10.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/100.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/101.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/11.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/12.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/13.webp
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/15.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/16.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/17.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/18.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/19.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/2.webp
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/22.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/23.webp
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/26.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/27.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/28.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/29.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/3.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/30.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/31.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/32.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/33.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/34.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/35.webp
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/37.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/38.webp
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/4.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/40.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/41.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/42.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/43.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/44.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/45.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/46.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/47.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/48.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/49.webp
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/50.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/51.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/52.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/53.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/54.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/55.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/56.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/57.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/58.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/59.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/6.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/60.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/61.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/62.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/63.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/64.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/65.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/66.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/67.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/68.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/69.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/7.webp
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/71.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/72.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/73.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/74.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/75.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/76.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/77.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/78.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/79.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/8.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/80.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/81.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/82.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/83.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/84.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/85.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/86.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/87.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/88.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/89.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/9.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/90.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/91.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/92.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/93.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/94.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/95.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/96.webp
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/97.webp
Binary file not shown.
Binary file not shown.
Binary file added labs/3-Add-Identity/src/Catalog.API/Pics/99.webp
Binary file not shown.
6 changes: 6 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Program.Testing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Require a public Program class to implement the
// fixture for the WebApplicationFactory in the
// integration tests. Using IVT is not sufficient
// in this case, because the accessibility of the
// `Program` type is checked.
public partial class Program { }
21 changes: 21 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using eShop.Catalog.API;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddDefaultOpenApi();
builder.AddApplicationServices();

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseDefaultOpenApi();

app.MapDefaultEndpoints();

app.MapGroup(app.GetOptions<CatalogOptions>().ApiBasePath)
.WithTags("Catalog API")
.MapCatalogApi();

app.Run();
12 changes: 12 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:5222/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"ConnectionStrings": {
"CatalogDB": "Host=localhost;Database=CatalogDB;Username=postgres"
},
"CatalogOptions": {

}
}
27 changes: 27 additions & 0 deletions labs/3-Add-Identity/src/Catalog.API/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"OpenApi": {
"Endpoint": {
"Name": "Catalog.API V1"
},
"Document": {
"Description": "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample",
"Title": "eShop - Catalog HTTP API",
"Version": "v1"
}
},
"ConnectionStrings": {
"EventBus": "amqp://localhost"
},
"EventBus": {
"SubscriptionClientName": "Catalog"
},
"CatalogOptions": {
"PicBasePathFormat": "items/{0}/pic/"
}
}
Loading
Loading