diff --git a/Directory.Packages.props b/Directory.Packages.props index c803dc53..5e641f50 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -54,6 +54,16 @@ + + + + + + + + + + diff --git a/Everything.sln b/Everything.sln index fb13a7c1..b228d63a 100644 --- a/Everything.sln +++ b/Everything.sln @@ -29,9 +29,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "tests\UnitTest EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7EFE7D65-5C2D-4D83-B034-D06B2D37564E}" ProjectSection(SolutionItems) = preProject + Directory.Packages.props = Directory.Packages.props README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopWeb.AppHost", "src\eShopWeb.AppHost\eShopWeb.AppHost.csproj", "{E3079820-FF6C-4CC7-9CBB-8DF68237299F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopWeb.AspireServiceDefaults", "src\eShopWeb.AspireServiceDefaults\eShopWeb.AspireServiceDefaults.csproj", "{7DB219F4-4ABC-45D6-A5F7-B938769D5319}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -78,6 +83,14 @@ Global {EAD6CF0B-2979-462C-BBB9-AF723B1EB570}.Debug|Any CPU.Build.0 = Debug|Any CPU {EAD6CF0B-2979-462C-BBB9-AF723B1EB570}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAD6CF0B-2979-462C-BBB9-AF723B1EB570}.Release|Any CPU.Build.0 = Release|Any CPU + {E3079820-FF6C-4CC7-9CBB-8DF68237299F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3079820-FF6C-4CC7-9CBB-8DF68237299F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3079820-FF6C-4CC7-9CBB-8DF68237299F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3079820-FF6C-4CC7-9CBB-8DF68237299F}.Release|Any CPU.Build.0 = Release|Any CPU + {7DB219F4-4ABC-45D6-A5F7-B938769D5319}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DB219F4-4ABC-45D6-A5F7-B938769D5319}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DB219F4-4ABC-45D6-A5F7-B938769D5319}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DB219F4-4ABC-45D6-A5F7-B938769D5319}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -93,5 +106,10 @@ Global {D6829485-DD9C-42CE-BEDE-4EB0E81021AC} = {BAA5312D-B54C-42D6-A3B9-504DD12F8250} {698594AE-78D3-429F-B5CC-3A6F6BCE397A} = {BAA5312D-B54C-42D6-A3B9-504DD12F8250} {EAD6CF0B-2979-462C-BBB9-AF723B1EB570} = {BAA5312D-B54C-42D6-A3B9-504DD12F8250} + {E3079820-FF6C-4CC7-9CBB-8DF68237299F} = {8FF16BDB-352E-42A2-A25F-0B5BC3A17FD7} + {7DB219F4-4ABC-45D6-A5F7-B938769D5319} = {8FF16BDB-352E-42A2-A25F-0B5BC3A17FD7} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CC66E299-A176-4667-95E3-CCDA89AAC6D0} EndGlobalSection EndGlobal diff --git a/src/BlazorAdmin/Program.cs b/src/BlazorAdmin/Program.cs index a6d4354d..7fb8c29d 100644 --- a/src/BlazorAdmin/Program.cs +++ b/src/BlazorAdmin/Program.cs @@ -14,13 +14,14 @@ using Microsoft.Extensions.Logging; var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("#admin"); builder.RootComponents.Add("head::after"); var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); builder.Services.Configure(configSection); -builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/src/BlazorAdmin/Properties/launchSettings.json b/src/BlazorAdmin/Properties/launchSettings.json index 6480448e..ed29accb 100644 --- a/src/BlazorAdmin/Properties/launchSettings.json +++ b/src/BlazorAdmin/Properties/launchSettings.json @@ -12,7 +12,7 @@ "commandName": "IISExpress", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:5001;http://localhost:5000", + "applicationUrl": "https://localhost:5011;http://localhost:5010", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -21,7 +21,7 @@ "commandName": "Project", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:5001;http://localhost:5000", + "applicationUrl": "https://localhost:5011;http://localhost:5010", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/BlazorAdmin/Services/CatalogLookupDataService.cs b/src/BlazorAdmin/Services/CatalogLookupDataService.cs index fa06b160..e6c0823f 100644 --- a/src/BlazorAdmin/Services/CatalogLookupDataService.cs +++ b/src/BlazorAdmin/Services/CatalogLookupDataService.cs @@ -35,7 +35,7 @@ public CatalogLookupDataService(HttpClient httpClient, public async Task> List() { var endpointName = typeof(TLookupData).GetCustomAttribute().Name; - _logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}"); + _logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API Endpoint : {endpointName}"); var response = await _httpClient.GetFromJsonAsync($"{_apiUrl}{endpointName}"); return response.List; diff --git a/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs index e6f25cea..6ac3850d 100644 --- a/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs +++ b/src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs @@ -12,18 +12,10 @@ namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints; /// /// Authenticates a user /// -public class AuthenticateEndpoint : Endpoint -{ - private readonly SignInManager _signInManager; - private readonly ITokenClaimsService _tokenClaimsService; - - public AuthenticateEndpoint(SignInManager signInManager, +public class AuthenticateEndpoint(SignInManager signInManager, ITokenClaimsService tokenClaimsService) - { - _signInManager = signInManager; - _tokenClaimsService = tokenClaimsService; - } - + : Endpoint +{ public override void Configure() { Post("api/authenticate"); @@ -43,7 +35,7 @@ public override async Task ExecuteAsync(AuthenticateReques // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true //var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); - var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, true); + var result = await signInManager.PasswordSignInAsync(request.Username, request.Password, false, true); response.Result = result.Succeeded; response.IsLockedOut = result.IsLockedOut; @@ -53,7 +45,7 @@ public override async Task ExecuteAsync(AuthenticateReques if (result.Succeeded) { - response.Token = await _tokenClaimsService.GetTokenAsync(request.Username); + response.Token = await tokenClaimsService.GetTokenAsync(request.Username); } return response; diff --git a/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs index 8444dcdc..e7fa3451 100644 --- a/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs +++ b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs @@ -11,17 +11,9 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints; /// /// List Catalog Brands /// -public class CatalogBrandListEndpoint : EndpointWithoutRequest +public class CatalogBrandListEndpoint(IRepository catalogBrandRepository, AutoMapper.IMapper mapper) + : EndpointWithoutRequest { - private readonly IRepository _catalogBrandRepository; - private readonly AutoMapper.IMapper _mapper; - - public CatalogBrandListEndpoint(IRepository catalogBrandRepository, AutoMapper.IMapper mapper) - { - _catalogBrandRepository = catalogBrandRepository; - _mapper = mapper; - } - public override void Configure() { Get("api/catalog-brands"); @@ -35,9 +27,9 @@ public override async Task ExecuteAsync(CancellationT { var response = new ListCatalogBrandsResponse(); - var items = await _catalogBrandRepository.ListAsync(ct); + var items = await catalogBrandRepository.ListAsync(ct); - response.CatalogBrands.AddRange(items.Select(_mapper.Map)); + response.CatalogBrands.AddRange(items.Select(mapper.Map)); return response; } diff --git a/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs index d2b8c303..a70eab19 100644 --- a/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs +++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs @@ -11,18 +11,9 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; /// /// Get a Catalog Item by Id /// -public class CatalogItemGetByIdEndpoint +public class CatalogItemGetByIdEndpoint(IRepository itemRepository, IUriComposer uriComposer) : Endpoint, NotFound>> { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; - - public CatalogItemGetByIdEndpoint(IRepository itemRepository, IUriComposer uriComposer) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - } - public override void Configure() { Get("api/catalog-items/{catalogItemId}"); @@ -36,7 +27,7 @@ public override async Task, NotFound>> Ex { var response = new GetByIdCatalogItemResponse(request.CorrelationId()); - var item = await _itemRepository.GetByIdAsync(request.CatalogItemId, ct); + var item = await itemRepository.GetByIdAsync(request.CatalogItemId, ct); if (item is null) return TypedResults.NotFound(); @@ -47,7 +38,7 @@ public override async Task, NotFound>> Ex CatalogTypeId = item.CatalogTypeId, Description = item.Description, Name = item.Name, - PictureUri = _uriComposer.ComposePicUri(item.PictureUri), + PictureUri = uriComposer.ComposePicUri(item.PictureUri), Price = item.Price }; return TypedResults.Ok(response); diff --git a/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs index eb894336..bd33bd86 100644 --- a/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs +++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs @@ -13,19 +13,10 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; /// /// List Catalog Items (paged) /// -public class CatalogItemListPagedEndpoint : Endpoint +public class CatalogItemListPagedEndpoint(IRepository itemRepository, IUriComposer uriComposer, + AutoMapper.IMapper mapper) + : Endpoint { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; - private readonly AutoMapper.IMapper _mapper; - - public CatalogItemListPagedEndpoint(IRepository itemRepository, IUriComposer uriComposer, AutoMapper.IMapper mapper) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - _mapper = mapper; - } - public override void Configure() { Get("api/catalog-items"); @@ -42,7 +33,7 @@ public override async Task ExecuteAsync(ListPagedC var response = new ListPagedCatalogItemResponse(request.CorrelationId()); var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId); - int totalItems = await _itemRepository.CountAsync(filterSpec, ct); + int totalItems = await itemRepository.CountAsync(filterSpec, ct); var pagedSpec = new CatalogFilterPaginatedSpecification( skip: request.PageIndex * request.PageSize, @@ -50,12 +41,12 @@ public override async Task ExecuteAsync(ListPagedC brandId: request.CatalogBrandId, typeId: request.CatalogTypeId); - var items = await _itemRepository.ListAsync(pagedSpec, ct); + var items = await itemRepository.ListAsync(pagedSpec, ct); - response.CatalogItems.AddRange(items.Select(_mapper.Map)); + response.CatalogItems.AddRange(items.Select(mapper.Map)); foreach (CatalogItemDto item in response.CatalogItems) { - item.PictureUri = _uriComposer.ComposePicUri(item.PictureUri); + item.PictureUri = uriComposer.ComposePicUri(item.PictureUri); } if (request.PageSize > 0) diff --git a/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs index 12ee6dc0..c974d761 100644 --- a/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs +++ b/src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs @@ -13,17 +13,9 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; /// /// Creates a new Catalog Item /// -public class CreateCatalogItemEndpoint : Endpoint +public class CreateCatalogItemEndpoint(IRepository itemRepository, IUriComposer uriComposer) + : Endpoint { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; - - public CreateCatalogItemEndpoint(IRepository itemRepository, IUriComposer uriComposer) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - } - public override void Configure() { Post("api/catalog-items"); @@ -39,14 +31,14 @@ public override async Task HandleAsync(CreateCatalogItemRequest request, Cancell var response = new CreateCatalogItemResponse(request.CorrelationId()); var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name); - var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification, ct); + var existingCataloogItem = await itemRepository.CountAsync(catalogItemNameSpecification, ct); if (existingCataloogItem > 0) { throw new DuplicateException($"A catalogItem with name {request.Name} already exists"); } var newItem = new CatalogItem(request.CatalogTypeId, request.CatalogBrandId, request.Description, request.Name, request.Price, request.PictureUri); - newItem = await _itemRepository.AddAsync(newItem, ct); + newItem = await itemRepository.AddAsync(newItem, ct); if (newItem.Id != 0) { @@ -55,7 +47,7 @@ public override async Task HandleAsync(CreateCatalogItemRequest request, Cancell // In production, we recommend uploading to a blob storage and deliver the image via CDN after a verification process. newItem.UpdatePictureUri("eCatalog-item-default.png"); - await _itemRepository.UpdateAsync(newItem, ct); + await itemRepository.UpdateAsync(newItem, ct); } var dto = new CatalogItemDto @@ -65,7 +57,7 @@ public override async Task HandleAsync(CreateCatalogItemRequest request, Cancell CatalogTypeId = newItem.CatalogTypeId, Description = newItem.Description, Name = newItem.Name, - PictureUri = _uriComposer.ComposePicUri(newItem.PictureUri), + PictureUri = uriComposer.ComposePicUri(newItem.PictureUri), Price = newItem.Price }; response.CatalogItem = dto; diff --git a/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs index 1dc992eb..2279ade4 100644 --- a/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs +++ b/src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs @@ -12,15 +12,8 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; /// /// Deletes a Catalog Item /// -public class DeleteCatalogItemEndpoint : Endpoint, NotFound>> +public class DeleteCatalogItemEndpoint(IRepository itemRepository) : Endpoint, NotFound>> { - private readonly IRepository _itemRepository; - - public DeleteCatalogItemEndpoint(IRepository itemRepository) - { - _itemRepository = itemRepository; - } - public override void Configure() { Delete("api/catalog-items/{catalogItemId}"); @@ -35,11 +28,11 @@ public override async Task, NotFound>> Exe { var response = new DeleteCatalogItemResponse(request.CorrelationId()); - var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId, ct); + var itemToDelete = await itemRepository.GetByIdAsync(request.CatalogItemId, ct); if (itemToDelete is null) return TypedResults.NotFound(); - await _itemRepository.DeleteAsync(itemToDelete, ct); + await itemRepository.DeleteAsync(itemToDelete, ct); return TypedResults.Ok(response); } diff --git a/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs b/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs index ff23009b..aa6fd614 100644 --- a/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs +++ b/src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs @@ -12,17 +12,9 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; /// /// Updates a Catalog Item /// -public class UpdateCatalogItemEndpoint : Endpoint, NotFound>> +public class UpdateCatalogItemEndpoint(IRepository itemRepository, IUriComposer uriComposer) + : Endpoint, NotFound>> { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; - - public UpdateCatalogItemEndpoint(IRepository itemRepository, IUriComposer uriComposer) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - } - public override void Configure() { Put("api/catalog-items"); @@ -37,7 +29,7 @@ public override async Task, NotFound>> Exe { var response = new UpdateCatalogItemResponse(request.CorrelationId()); - var existingItem = await _itemRepository.GetByIdAsync(request.Id, ct); + var existingItem = await itemRepository.GetByIdAsync(request.Id, ct); if (existingItem == null) { return TypedResults.NotFound(); @@ -48,7 +40,7 @@ public override async Task, NotFound>> Exe existingItem.UpdateBrand(request.CatalogBrandId); existingItem.UpdateType(request.CatalogTypeId); - await _itemRepository.UpdateAsync(existingItem, ct); + await itemRepository.UpdateAsync(existingItem, ct); var dto = new CatalogItemDto { @@ -57,7 +49,7 @@ public override async Task, NotFound>> Exe CatalogTypeId = existingItem.CatalogTypeId, Description = existingItem.Description, Name = existingItem.Name, - PictureUri = _uriComposer.ComposePicUri(existingItem.PictureUri), + PictureUri = uriComposer.ComposePicUri(existingItem.PictureUri), Price = existingItem.Price }; response.CatalogItem = dto; diff --git a/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs index f0e21c53..7d8ac2c7 100644 --- a/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs +++ b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs @@ -11,18 +11,9 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints; /// /// List Catalog Types /// -public class CatalogTypeListEndpoint : EndpointWithoutRequest +public class CatalogTypeListEndpoint(IRepository catalogTypeRepository, AutoMapper.IMapper mapper) + : EndpointWithoutRequest { - private readonly IRepository _catalogTypeRepository; - private readonly AutoMapper.IMapper _mapper; - - public CatalogTypeListEndpoint(IRepository catalogTypeRepository, AutoMapper.IMapper mapper) - { - _catalogTypeRepository = catalogTypeRepository; - _mapper = mapper; - - } - public override void Configure() { Get("api/catalog-types"); @@ -36,9 +27,9 @@ public override async Task ExecuteAsync(CancellationTo { var response = new ListCatalogTypesResponse(); - var items = await _catalogTypeRepository.ListAsync(ct); + var items = await catalogTypeRepository.ListAsync(ct); - response.CatalogTypes.AddRange(items.Select(_mapper.Map)); + response.CatalogTypes.AddRange(items.Select(mapper.Map)); return response; } diff --git a/src/PublicApi/Program.cs b/src/PublicApi/Program.cs index 4d15ca74..bf517fb1 100644 --- a/src/PublicApi/Program.cs +++ b/src/PublicApi/Program.cs @@ -14,6 +14,9 @@ var builder = WebApplication.CreateBuilder(args); +// Add service defaults & Aspire components. +builder.AddAspireServiceDefaults(); + builder.Services.AddFastEndpoints(); // Use to force loading of appsettings.json of test project diff --git a/src/PublicApi/Properties/launchSettings.json b/src/PublicApi/Properties/launchSettings.json index c44d516f..24081361 100644 --- a/src/PublicApi/Properties/launchSettings.json +++ b/src/PublicApi/Properties/launchSettings.json @@ -6,7 +6,8 @@ "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "https://localhost:5099;http://localhost:5098" }, "PublicApi": { "commandName": "Project", diff --git a/src/PublicApi/PublicApi.csproj b/src/PublicApi/PublicApi.csproj index c6644a33..c5224b4d 100644 --- a/src/PublicApi/PublicApi.csproj +++ b/src/PublicApi/PublicApi.csproj @@ -32,6 +32,7 @@ + diff --git a/src/Web/Configuration/BaseUrlConfiguration.cs b/src/Web/Configuration/BaseUrlConfiguration.cs index 57f54df5..fe729b66 100644 --- a/src/Web/Configuration/BaseUrlConfiguration.cs +++ b/src/Web/Configuration/BaseUrlConfiguration.cs @@ -5,5 +5,4 @@ public class BaseUrlConfiguration public const string CONFIG_NAME = "baseUrls"; public string ApiBase { get; set; } = string.Empty; - public string WebBase { get; set; } = string.Empty; } diff --git a/src/Web/Extensions/ServiceCollectionExtensions.cs b/src/Web/Extensions/ServiceCollectionExtensions.cs index 76fcba41..3a2c21f5 100644 --- a/src/Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/Web/Extensions/ServiceCollectionExtensions.cs @@ -63,12 +63,11 @@ public static void AddBlazor(this IServiceCollection services, ConfigurationMana { var configSection = configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); services.Configure(configSection); - var baseUrlConfig = configSection.Get(); // Blazor Admin Required Services for Prerendering services.AddScoped(s => new HttpClient { - BaseAddress = new Uri(baseUrlConfig!.WebBase) + BaseAddress = new Uri("https+http://blazoradmin") }); // add blazor services diff --git a/src/Web/Program.cs b/src/Web/Program.cs index 8d5cd6bd..f30f93de 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -10,6 +10,9 @@ var builder = WebApplication.CreateBuilder(args); +// Add service defaults & Aspire components. +builder.AddAspireServiceDefaults(); + builder.Services.AddDatabaseContexts(builder.Environment, builder.Configuration); builder.Services.AddCookieSettings(); diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index bf78d144..03cae8bb 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -42,6 +42,7 @@ + diff --git a/src/Web/appsettings.Development.json b/src/Web/appsettings.Development.json index bd6e47b6..f5275c28 100644 --- a/src/Web/appsettings.Development.json +++ b/src/Web/appsettings.Development.json @@ -1,7 +1,6 @@ { "baseUrls": { - "apiBase": "https://localhost:5099/api/", - "webBase": "https://localhost:44315/" + "apiBase": "https://localhost:5099/api/" }, "Logging": { "LogLevel": { diff --git a/src/Web/appsettings.Docker.json b/src/Web/appsettings.Docker.json index 07ea75ea..f4c05627 100644 --- a/src/Web/appsettings.Docker.json +++ b/src/Web/appsettings.Docker.json @@ -4,8 +4,7 @@ "IdentityConnection": "Server=sqlserver,1433;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;Trusted_Connection=false;TrustServerCertificate=true;" }, "baseUrls": { - "apiBase": "http://localhost:5200/api/", - "webBase": "http://host.docker.internal:5106/" + "apiBase": "http://localhost:5200/api/" }, "Logging": { "LogLevel": { diff --git a/src/Web/appsettings.json b/src/Web/appsettings.json index c2bc6592..9a201d43 100644 --- a/src/Web/appsettings.json +++ b/src/Web/appsettings.json @@ -1,7 +1,6 @@ { "baseUrls": { - "apiBase": "https://localhost:5099/api/", - "webBase": "https://localhost:44315/" + "apiBase": "https://localhost:5099/api/" }, "ConnectionStrings": { "CatalogConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;", diff --git a/src/eShopWeb.AppHost/Program.cs b/src/eShopWeb.AppHost/Program.cs new file mode 100644 index 00000000..b17f22f3 --- /dev/null +++ b/src/eShopWeb.AppHost/Program.cs @@ -0,0 +1,9 @@ +var builder = DistributedApplication.CreateBuilder(args); + +builder + .AddProject(nameof(Projects.PublicApi).ToLower()); + +builder + .AddProject(nameof(Projects.Web).ToLower()); + +builder.Build().Run(); diff --git a/src/eShopWeb.AppHost/Properties/launchSettings.json b/src/eShopWeb.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000..67077308 --- /dev/null +++ b/src/eShopWeb.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17177;http://localhost:15038", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21066", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22107" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15038", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19018", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20071" + } + } + } +} diff --git a/src/eShopWeb.AppHost/appsettings.Development.json b/src/eShopWeb.AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/src/eShopWeb.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/eShopWeb.AppHost/appsettings.json b/src/eShopWeb.AppHost/appsettings.json new file mode 100644 index 00000000..31c092aa --- /dev/null +++ b/src/eShopWeb.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/eShopWeb.AppHost/eShopWeb.AppHost.csproj b/src/eShopWeb.AppHost/eShopWeb.AppHost.csproj new file mode 100644 index 00000000..73768c84 --- /dev/null +++ b/src/eShopWeb.AppHost/eShopWeb.AppHost.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + enable + true + 5d5c1626-3c2a-4ec3-96b3-240241479084 + + + + + + + + + + + + + diff --git a/src/eShopWeb.AspireServiceDefaults/Extensions.cs b/src/eShopWeb.AspireServiceDefaults/Extensions.cs new file mode 100644 index 00000000..ec658674 --- /dev/null +++ b/src/eShopWeb.AspireServiceDefaults/Extensions.cs @@ -0,0 +1,126 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static IHostApplicationBuilder AddAspireServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddMeter( + "Microsoft.AspNetCore.Hosting", + "Microsoft.AspNetCore.Server.Kestrel", + "System.Net.Http" + ); + }) + .WithTracing(tracing => + { + if (builder.Environment.IsDevelopment()) + { + tracing.SetSampler(); + } + tracing.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + builder.Services.AddOpenTelemetry().WithMetrics(x => x.AddPrometheusExporter()); + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + app.MapPrometheusScrapingEndpoint(); + + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/src/eShopWeb.AspireServiceDefaults/eShopWeb.AspireServiceDefaults.csproj b/src/eShopWeb.AspireServiceDefaults/eShopWeb.AspireServiceDefaults.csproj new file mode 100644 index 00000000..4a91dfba --- /dev/null +++ b/src/eShopWeb.AspireServiceDefaults/eShopWeb.AspireServiceDefaults.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + +