-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e2a65d6
commit 1dc1b0f
Showing
232 changed files
with
6,940 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
214 changes: 214 additions & 0 deletions
214
labs/4-Add-Shopping-Basket/src/Catalog.API/Apis/CatalogApi.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
15
labs/4-Add-Shopping-Basket/src/Catalog.API/Catalog.API.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
10
labs/4-Add-Shopping-Basket/src/Catalog.API/Catalog.API.http
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
18
labs/4-Add-Shopping-Basket/src/Catalog.API/CatalogOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
labs/4-Add-Shopping-Basket/src/Catalog.API/Extensions/HostingExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
13
labs/4-Add-Shopping-Basket/src/Catalog.API/Model/CatalogServices.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
12
labs/4-Add-Shopping-Basket/src/Catalog.API/Model/PaginatedItems.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
8 changes: 8 additions & 0 deletions
8
labs/4-Add-Shopping-Basket/src/Catalog.API/Model/PaginationRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 6 additions & 0 deletions
6
labs/4-Add-Shopping-Basket/src/Catalog.API/Program.Testing.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { } |
Oops, something went wrong.