diff --git a/.editorconfig b/.editorconfig
index 9261202f8..88b30b088 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -58,13 +58,23 @@ dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-# Use PascalCase for constant fields
+# Use PascalCase for constant fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
dotnet_naming_symbols.constant_fields.required_modifiers = const
+
+# Instance fields are camelCase and start with _
+dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
+dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
+
+dotnet_naming_symbols.instance_fields.applicable_kinds = field
+
+dotnet_naming_style.instance_field_style.capitalization = camel_case
+dotnet_naming_style.instance_field_style.required_prefix = _
###############################
# C# Coding Conventions #
###############################
diff --git a/eShopOnWeb.sln b/eShopOnWeb.sln
index b99fc4ec4..0394e30ba 100755
--- a/eShopOnWeb.sln
+++ b/eShopOnWeb.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29102.190
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{419A6ACE-0419-4315-A6FB-B0E63D39432E}"
EndProject
@@ -23,6 +23,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "tests\Fu
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BD72BEA-EF42-4B72-8B69-12A39EC76FBA}"
ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
docker-compose.override.yml = docker-compose.override.yml
docker-compose.yml = docker-compose.yml
.github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml
diff --git a/src/BlazorAdmin/Pages/CatalogItemPage/List.razor b/src/BlazorAdmin/Pages/CatalogItemPage/List.razor
index d8749d773..33d34c585 100644
--- a/src/BlazorAdmin/Pages/CatalogItemPage/List.razor
+++ b/src/BlazorAdmin/Pages/CatalogItemPage/List.razor
@@ -3,6 +3,8 @@
@inherits BlazorAdmin.Helpers.BlazorComponent
@namespace BlazorAdmin.Pages.CatalogItemPage
+eShopOnWeb Admin: Manage Product Catalog
+
Manage Product Catalog
@if (catalogItems == null)
diff --git a/src/BlazorAdmin/Program.cs b/src/BlazorAdmin/Program.cs
index b80094751..a6d4354d4 100644
--- a/src/BlazorAdmin/Program.cs
+++ b/src/BlazorAdmin/Program.cs
@@ -1,54 +1,49 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
+using BlazorAdmin;
using BlazorAdmin.Services;
using Blazored.LocalStorage;
using BlazorShared;
using BlazorShared.Models;
using Microsoft.AspNetCore.Components.Authorization;
+using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-namespace BlazorAdmin;
+var builder = WebAssemblyHostBuilder.CreateDefault(args);
+builder.RootComponents.Add("#admin");
+builder.RootComponents.Add("head::after");
-public class Program
-{
- public static async Task Main(string[] args)
- {
- var builder = WebAssemblyHostBuilder.CreateDefault(args);
- builder.RootComponents.Add("#admin");
-
- var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME);
- builder.Services.Configure(configSection);
+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();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
- builder.Services.AddBlazoredLocalStorage();
+builder.Services.AddBlazoredLocalStorage();
- builder.Services.AddAuthorizationCore();
- builder.Services.AddScoped();
- builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService());
+builder.Services.AddAuthorizationCore();
+builder.Services.AddScoped();
+builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService());
- builder.Services.AddBlazorServices();
+builder.Services.AddBlazorServices();
- builder.Logging.AddConfiguration(builder.Configuration.GetRequiredSection("Logging"));
+builder.Logging.AddConfiguration(builder.Configuration.GetRequiredSection("Logging"));
- await ClearLocalStorageCache(builder.Services);
+await ClearLocalStorageCache(builder.Services);
- await builder.Build().RunAsync();
- }
+await builder.Build().RunAsync();
- private static async Task ClearLocalStorageCache(IServiceCollection services)
- {
- var sp = services.BuildServiceProvider();
- var localStorageService = sp.GetRequiredService();
+static async Task ClearLocalStorageCache(IServiceCollection services)
+{
+ var sp = services.BuildServiceProvider();
+ var localStorageService = sp.GetRequiredService();
- await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
- await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
- }
+ await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
+ await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
}
diff --git a/src/Infrastructure/Data/CatalogContextSeed.cs b/src/Infrastructure/Data/CatalogContextSeed.cs
index 7fbd88193..e1364f52e 100644
--- a/src/Infrastructure/Data/CatalogContextSeed.cs
+++ b/src/Infrastructure/Data/CatalogContextSeed.cs
@@ -10,7 +10,8 @@ namespace Microsoft.eShopWeb.Infrastructure.Data;
public class CatalogContextSeed
{
public static async Task SeedAsync(CatalogContext catalogContext,
- ILoggerFactory loggerFactory, int retry = 0)
+ ILogger logger,
+ int retry = 0)
{
var retryForAvailability = retry;
try
@@ -49,9 +50,9 @@ await catalogContext.CatalogItems.AddRangeAsync(
if (retryForAvailability >= 10) throw;
retryForAvailability++;
- var log = loggerFactory.CreateLogger();
- log.LogError(ex.Message);
- await SeedAsync(catalogContext, loggerFactory, retryForAvailability);
+
+ logger.LogError(ex.Message);
+ await SeedAsync(catalogContext, logger, retryForAvailability);
throw;
}
}
diff --git a/src/PublicApi/AuthEndpoints/Authenticate.cs b/src/PublicApi/AuthEndpoints/Authenticate.cs
index 61d576dd7..eee5218d2 100644
--- a/src/PublicApi/AuthEndpoints/Authenticate.cs
+++ b/src/PublicApi/AuthEndpoints/Authenticate.cs
@@ -9,9 +9,9 @@
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
-public class Authenticate : BaseAsyncEndpoint
+public class Authenticate : EndpointBaseAsync
.WithRequest
- .WithResponse
+ .WithActionResult
{
private readonly SignInManager _signInManager;
private readonly ITokenClaimsService _tokenClaimsService;
diff --git a/src/PublicApi/CatalogBrandEndpoints/List.cs b/src/PublicApi/CatalogBrandEndpoints/List.cs
index 1524f4db2..d64b8ca13 100644
--- a/src/PublicApi/CatalogBrandEndpoints/List.cs
+++ b/src/PublicApi/CatalogBrandEndpoints/List.cs
@@ -10,9 +10,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
-public class List : BaseAsyncEndpoint
+public class List : EndpointBaseAsync
.WithoutRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _catalogBrandRepository;
private readonly IMapper _mapper;
diff --git a/src/PublicApi/CatalogItemEndpoints/Create.cs b/src/PublicApi/CatalogItemEndpoints/Create.cs
index 74ee7ed1a..da9b885ab 100644
--- a/src/PublicApi/CatalogItemEndpoints/Create.cs
+++ b/src/PublicApi/CatalogItemEndpoints/Create.cs
@@ -13,9 +13,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
-public class Create : BaseAsyncEndpoint
+public class Create : EndpointBaseAsync
.WithRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _itemRepository;
private readonly IUriComposer _uriComposer;
diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.cs b/src/PublicApi/CatalogItemEndpoints/Delete.cs
index 9b1173204..1724bc41c 100644
--- a/src/PublicApi/CatalogItemEndpoints/Delete.cs
+++ b/src/PublicApi/CatalogItemEndpoints/Delete.cs
@@ -11,9 +11,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
-public class Delete : BaseAsyncEndpoint
+public class Delete : EndpointBaseAsync
.WithRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _itemRepository;
diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.cs b/src/PublicApi/CatalogItemEndpoints/GetById.cs
index 722f221f6..62352cfef 100644
--- a/src/PublicApi/CatalogItemEndpoints/GetById.cs
+++ b/src/PublicApi/CatalogItemEndpoints/GetById.cs
@@ -8,9 +8,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-public class GetById : BaseAsyncEndpoint
+public class GetById : EndpointBaseAsync
.WithRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _itemRepository;
private readonly IUriComposer _uriComposer;
diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.cs b/src/PublicApi/CatalogItemEndpoints/ListPaged.cs
index 9feffa7d8..c7c20f761 100644
--- a/src/PublicApi/CatalogItemEndpoints/ListPaged.cs
+++ b/src/PublicApi/CatalogItemEndpoints/ListPaged.cs
@@ -12,9 +12,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
-public class ListPaged : BaseAsyncEndpoint
+public class ListPaged : EndpointBaseAsync
.WithRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _itemRepository;
private readonly IUriComposer _uriComposer;
diff --git a/src/PublicApi/CatalogItemEndpoints/Update.cs b/src/PublicApi/CatalogItemEndpoints/Update.cs
index 3d674b216..a960162ab 100644
--- a/src/PublicApi/CatalogItemEndpoints/Update.cs
+++ b/src/PublicApi/CatalogItemEndpoints/Update.cs
@@ -12,9 +12,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
-public class Update : BaseAsyncEndpoint
+public class Update : EndpointBaseAsync
.WithRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _itemRepository;
private readonly IUriComposer _uriComposer;
diff --git a/src/PublicApi/CatalogTypeEndpoints/List.cs b/src/PublicApi/CatalogTypeEndpoints/List.cs
index 7e40aac88..eb1b21231 100644
--- a/src/PublicApi/CatalogTypeEndpoints/List.cs
+++ b/src/PublicApi/CatalogTypeEndpoints/List.cs
@@ -10,9 +10,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
-public class List : BaseAsyncEndpoint
+public class List : EndpointBaseAsync
.WithoutRequest
- .WithResponse
+ .WithActionResult
{
private readonly IRepository _catalogTypeRepository;
private readonly IMapper _mapper;
diff --git a/src/PublicApi/MiddleWares/ExceptionMiddleware.cs b/src/PublicApi/Middleware/ExceptionMiddleware.cs
similarity index 95%
rename from src/PublicApi/MiddleWares/ExceptionMiddleware.cs
rename to src/PublicApi/Middleware/ExceptionMiddleware.cs
index bae4800a0..46b24f8c8 100644
--- a/src/PublicApi/MiddleWares/ExceptionMiddleware.cs
+++ b/src/PublicApi/Middleware/ExceptionMiddleware.cs
@@ -5,7 +5,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
-namespace Microsoft.eShopWeb.PublicApi.MiddleWares;
+namespace Microsoft.eShopWeb.PublicApi.Middleware;
public class ExceptionMiddleware
{
diff --git a/src/PublicApi/Program.cs b/src/PublicApi/Program.cs
index a1c2b2448..176d9becd 100644
--- a/src/PublicApi/Program.cs
+++ b/src/PublicApi/Program.cs
@@ -1,50 +1,186 @@
-using System;
+using System;
+using System.Collections.Generic;
+using System.Text;
using System.Threading.Tasks;
+using BlazorShared;
+using BlazorShared.Models;
+using MediatR;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.eShopWeb;
+using Microsoft.eShopWeb.ApplicationCore.Constants;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
+using Microsoft.eShopWeb.ApplicationCore.Services;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.Identity;
+using Microsoft.eShopWeb.Infrastructure.Logging;
+using Microsoft.eShopWeb.PublicApi;
+using Microsoft.eShopWeb.PublicApi.Middleware;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.OpenApi.Models;
-namespace Microsoft.eShopWeb.PublicApi;
+var builder = WebApplication.CreateBuilder(args);
-public class Program
+builder.Logging.AddConsole();
+
+// use real database
+// Requires LocalDB which can be installed with SQL Server Express 2016
+// https://www.microsoft.com/en-us/download/details.aspx?id=54284
+builder.Services.AddDbContext(c =>
+ c.UseSqlServer(builder.Configuration.GetConnectionString("CatalogConnection")));
+
+// Add Identity DbContext
+builder.Services.AddDbContext(options =>
+ options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection")));
+
+builder.Services.AddIdentity()
+ .AddEntityFrameworkStores()
+ .AddDefaultTokenProviders();
+
+builder.Services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
+builder.Services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
+builder.Services.Configure(builder.Configuration);
+builder.Services.AddSingleton(new UriComposer(builder.Configuration.Get()));
+builder.Services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
+builder.Services.AddScoped();
+
+var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME);
+builder.Services.Configure(configSection);
+var baseUrlConfig = configSection.Get();
+
+builder.Services.AddMemoryCache();
+
+var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY);
+builder.Services.AddAuthentication(config =>
+{
+ config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+})
+.AddJwtBearer(config =>
{
- public static async Task Main(string[] args)
+ config.RequireHttpsMetadata = false;
+ config.SaveToken = true;
+ config.TokenValidationParameters = new TokenValidationParameters
{
- var host = CreateHostBuilder(args)
- .Build();
-
- using (var scope = host.Services.CreateScope())
- {
- var services = scope.ServiceProvider;
- var loggerFactory = services.GetRequiredService();
- try
- {
- var catalogContext = services.GetRequiredService();
- await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory);
-
- var userManager = services.GetRequiredService>();
- var roleManager = services.GetRequiredService>();
- await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
- }
- catch (Exception ex)
- {
- var logger = loggerFactory.CreateLogger();
- logger.LogError(ex, "An error occurred seeding the DB.");
- }
- }
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(key),
+ ValidateIssuer = false,
+ ValidateAudience = false
+ };
+});
- host.Run();
- }
+const string CORS_POLICY = "CorsPolicy";
+builder.Services.AddCors(options =>
+{
+ options.AddPolicy(name: CORS_POLICY,
+ builder =>
+ {
+ builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/'));
+ builder.AllowAnyMethod();
+ builder.AllowAnyHeader();
+ });
+});
+
+builder.Services.AddControllers();
+
+builder.Services.AddMediatR(typeof(CatalogItem).Assembly);
+builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly);
+builder.Services.AddSwaggerGen(c =>
+{
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
+ c.EnableAnnotations();
+ c.SchemaFilter();
+ c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
+ Enter 'Bearer' [space] and then your token in the text input below.
+ \r\n\r\nExample: 'Bearer 12345abcdef'",
+ Name = "Authorization",
+ In = ParameterLocation.Header,
+ Type = SecuritySchemeType.ApiKey,
+ Scheme = "Bearer"
+ });
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
+ c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
- webBuilder.UseStartup();
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ },
+ Scheme = "oauth2",
+ Name = "Bearer",
+ In = ParameterLocation.Header,
+
+ },
+ new List()
+ }
});
+});
+
+var app = builder.Build();
+
+app.Logger.LogInformation("PublicApi App created...");
+
+if (app.Environment.IsDevelopment())
+{
+ app.UseDeveloperExceptionPage();
}
+
+app.UseMiddleware();
+
+app.UseHttpsRedirection();
+
+app.UseRouting();
+
+app.UseCors(CORS_POLICY);
+
+app.UseAuthorization();
+
+// Enable middleware to serve generated Swagger as a JSON endpoint.
+app.UseSwagger();
+
+// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
+// specifying the Swagger JSON endpoint.
+app.UseSwaggerUI(c =>
+{
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
+});
+
+app.UseEndpoints(endpoints =>
+{
+ endpoints.MapControllers();
+});
+
+app.Logger.LogInformation("Seeding Database...");
+
+using (var scope = app.Services.CreateScope())
+{
+ var scopedProvider = scope.ServiceProvider;
+ try
+ {
+ var catalogContext = scopedProvider.GetRequiredService();
+ await CatalogContextSeed.SeedAsync(catalogContext, app.Logger);
+
+ var userManager = scopedProvider.GetRequiredService>();
+ var roleManager = scopedProvider.GetRequiredService>();
+ await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
+ }
+ catch (Exception ex)
+ {
+ app.Logger.LogError(ex, "An error occurred seeding the DB.");
+ }
+}
+
+app.Logger.LogInformation("LAUNCHING PublicApi");
+app.Run();
diff --git a/src/PublicApi/PublicApi.csproj b/src/PublicApi/PublicApi.csproj
index 76b3b9ced..72ad7a8b1 100644
--- a/src/PublicApi/PublicApi.csproj
+++ b/src/PublicApi/PublicApi.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/PublicApi/Startup.cs b/src/PublicApi/Startup.cs
deleted file mode 100644
index 2a52fd586..000000000
--- a/src/PublicApi/Startup.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-using AutoMapper;
-using BlazorShared;
-using MediatR;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.eShopWeb.ApplicationCore.Constants;
-using Microsoft.eShopWeb.ApplicationCore.Entities;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Microsoft.eShopWeb.ApplicationCore.Services;
-using Microsoft.eShopWeb.Infrastructure.Data;
-using Microsoft.eShopWeb.Infrastructure.Identity;
-using Microsoft.eShopWeb.Infrastructure.Logging;
-using Microsoft.eShopWeb.Infrastructure.Services;
-using Microsoft.eShopWeb.PublicApi.MiddleWares;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.IdentityModel.Tokens;
-using Microsoft.OpenApi.Models;
-
-namespace Microsoft.eShopWeb.PublicApi;
-
-public class Startup
-{
- private const string CORS_POLICY = "CorsPolicy";
-
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureDevelopmentServices(IServiceCollection services)
- {
- // use in-memory database
- ConfigureInMemoryDatabases(services);
-
- // use real database
- //ConfigureProductionServices(services);
- }
-
- public void ConfigureDockerServices(IServiceCollection services)
- {
- ConfigureDevelopmentServices(services);
- }
-
- private void ConfigureInMemoryDatabases(IServiceCollection services)
- {
- services.AddDbContext(c =>
- c.UseInMemoryDatabase("Catalog"));
-
- services.AddDbContext(options =>
- options.UseInMemoryDatabase("Identity"));
-
- ConfigureServices(services);
- }
-
- public void ConfigureProductionServices(IServiceCollection services)
- {
- // use real database
- // Requires LocalDB which can be installed with SQL Server Express 2016
- // https://www.microsoft.com/en-us/download/details.aspx?id=54284
- services.AddDbContext(c =>
- c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection")));
-
- // Add Identity DbContext
- services.AddDbContext(options =>
- options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
-
- ConfigureServices(services);
- }
-
- public void ConfigureTestingServices(IServiceCollection services)
- {
- ConfigureInMemoryDatabases(services);
- }
-
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddIdentity()
- .AddEntityFrameworkStores()
- .AddDefaultTokenProviders();
-
- services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
- services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
- services.Configure(Configuration);
- services.AddSingleton(new UriComposer(Configuration.Get()));
- services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
- services.AddScoped();
-
- var configSection = Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME);
- services.Configure(configSection);
- var baseUrlConfig = configSection.Get();
-
- services.AddMemoryCache();
-
- var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY);
- services.AddAuthentication(config =>
- {
- config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
- })
- .AddJwtBearer(config =>
- {
- config.RequireHttpsMetadata = false;
- config.SaveToken = true;
- config.TokenValidationParameters = new TokenValidationParameters
- {
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(key),
- ValidateIssuer = false,
- ValidateAudience = false
- };
- });
-
- services.AddCors(options =>
- {
- options.AddPolicy(name: CORS_POLICY,
- builder =>
- {
- builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/'));
- builder.AllowAnyMethod();
- builder.AllowAnyHeader();
- });
- });
-
- services.AddControllers();
- services.AddMediatR(typeof(CatalogItem).Assembly);
-
- services.AddAutoMapper(typeof(Startup).Assembly);
- services.AddSwaggerGen(c =>
- {
- c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
- c.EnableAnnotations();
- c.SchemaFilter();
- c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
- {
- Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
- Enter 'Bearer' [space] and then your token in the text input below.
- \r\n\r\nExample: 'Bearer 12345abcdef'",
- Name = "Authorization",
- In = ParameterLocation.Header,
- Type = SecuritySchemeType.ApiKey,
- Scheme = "Bearer"
- });
-
- c.AddSecurityRequirement(new OpenApiSecurityRequirement()
- {
- {
- new OpenApiSecurityScheme
- {
- Reference = new OpenApiReference
- {
- Type = ReferenceType.SecurityScheme,
- Id = "Bearer"
- },
- Scheme = "oauth2",
- Name = "Bearer",
- In = ParameterLocation.Header,
-
- },
- new List()
- }
- });
- });
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseMiddleware();
-
- app.UseHttpsRedirection();
-
- app.UseRouting();
-
- app.UseCors(CORS_POLICY);
-
- app.UseAuthorization();
-
- // Enable middleware to serve generated Swagger as a JSON endpoint.
- app.UseSwagger();
-
- // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
- // specifying the Swagger JSON endpoint.
- app.UseSwaggerUI(c =>
- {
- c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
- });
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
-}
diff --git a/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs
index 4d04c069e..eed08342e 100644
--- a/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs
+++ b/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs
@@ -53,7 +53,7 @@ public class InputModel
public bool RememberMe { get; set; }
}
- public async Task OnGetAsync(string returnUrl = null)
+ public async Task OnGetAsync(string? returnUrl = null)
{
if (!string.IsNullOrEmpty(ErrorMessage))
{
@@ -70,7 +70,7 @@ public async Task OnGetAsync(string returnUrl = null)
ReturnUrl = returnUrl;
}
- public async Task OnPostAsync(string returnUrl = null)
+ public async Task OnPostAsync(string? returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
diff --git a/src/Web/Interfaces/IBasketViewModelService.cs b/src/Web/Interfaces/IBasketViewModelService.cs
index 85d1c8773..d7042deaa 100644
--- a/src/Web/Interfaces/IBasketViewModelService.cs
+++ b/src/Web/Interfaces/IBasketViewModelService.cs
@@ -1,5 +1,4 @@
-using System.Threading.Tasks;
-using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
+using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.Web.Pages.Basket;
namespace Microsoft.eShopWeb.Web.Interfaces;
diff --git a/src/Web/Program.cs b/src/Web/Program.cs
index 6455a29b6..c3944a97d 100644
--- a/src/Web/Program.cs
+++ b/src/Web/Program.cs
@@ -1,49 +1,198 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting;
+using System.Net.Mime;
+using Ardalis.ListStartupServices;
+using BlazorAdmin;
+using BlazorAdmin.Services;
+using Blazored.LocalStorage;
+using BlazorShared;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc.ApplicationModels;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.eShopWeb;
+using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.Identity;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
+using Microsoft.eShopWeb.Web;
+using Microsoft.eShopWeb.Web.Configuration;
+using Microsoft.eShopWeb.Web.HealthChecks;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
-namespace Microsoft.eShopWeb.Web;
+var builder = WebApplication.CreateBuilder(args);
-public class Program
+builder.Logging.AddConsole();
+
+// use real database
+// Requires LocalDB which can be installed with SQL Server Express 2016
+// https://www.microsoft.com/en-us/download/details.aspx?id=54284
+builder.Services.AddDbContext(c =>
+ c.UseSqlServer(builder.Configuration.GetConnectionString("CatalogConnection")));
+
+// Add Identity DbContext
+builder.Services.AddDbContext(options =>
+ options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection")));
+
+builder.Services.AddCookieSettings();
+
+builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
+ .AddCookie(options =>
+ {
+ options.Cookie.HttpOnly = true;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
+ options.Cookie.SameSite = SameSiteMode.Lax;
+ });
+
+builder.Services.AddIdentity()
+ .AddDefaultUI()
+ .AddEntityFrameworkStores()
+ .AddDefaultTokenProviders();
+
+builder.Services.AddScoped();
+
+builder.Services.AddCoreServices(builder.Configuration);
+builder.Services.AddWebServices(builder.Configuration);
+
+// Add memory cache services
+builder.Services.AddMemoryCache();
+builder.Services.AddRouting(options =>
+{
+ // Replace the type and the name used to refer to it with your own
+ // IOutboundParameterTransformer implementation
+ options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
+});
+
+builder.Services.AddMvc(options =>
+{
+ options.Conventions.Add(new RouteTokenTransformerConvention(
+ new SlugifyParameterTransformer()));
+
+});
+builder.Services.AddControllersWithViews();
+builder.Services.AddRazorPages(options =>
+{
+ options.Conventions.AuthorizePage("/Basket/Checkout");
+});
+builder.Services.AddHttpContextAccessor();
+builder.Services
+ .AddHealthChecks()
+ .AddCheck("api_health_check", tags: new[] { "apiHealthCheck" })
+ .AddCheck("home_page_health_check", tags: new[] { "homePageHealthCheck" });
+builder.Services.Configure(config =>
+{
+ config.Services = new List(builder.Services);
+ config.Path = "/allservices";
+});
+
+// blazor configuration
+var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME);
+builder.Services.Configure(configSection);
+var baseUrlConfig = configSection.Get();
+
+// Blazor Admin Required Services for Prerendering
+builder.Services.AddScoped(s => new HttpClient
+{
+ BaseAddress = new Uri(baseUrlConfig.WebBase)
+});
+
+// add blazor services
+builder.Services.AddBlazoredLocalStorage();
+builder.Services.AddServerSideBlazor();
+
+
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddBlazorServices();
+
+builder.Services.AddDatabaseDeveloperPageExceptionFilter();
+
+//_services = services; // used to debug registered services
+
+var app = builder.Build();
+
+app.Logger.LogInformation("App created...");
+
+var catalogBaseUrl = builder.Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string;
+if (!string.IsNullOrEmpty(catalogBaseUrl))
{
- public static async Task Main(string[] args)
+ app.Use((context, next) =>
{
- var host = CreateHostBuilder(args)
- .Build();
+ context.Request.PathBase = new PathString(catalogBaseUrl);
+ return next();
+ });
+}
- using (var scope = host.Services.CreateScope())
+app.UseHealthChecks("/health",
+ new HealthCheckOptions
+ {
+ ResponseWriter = async (context, report) =>
{
- var services = scope.ServiceProvider;
- var loggerFactory = services.GetRequiredService();
- try
- {
- var catalogContext = services.GetRequiredService();
- await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory);
-
- var userManager = services.GetRequiredService>();
- var roleManager = services.GetRequiredService>();
- await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
- }
- catch (Exception ex)
+ var result = new
{
- var logger = loggerFactory.CreateLogger();
- logger.LogError(ex, "An error occurred seeding the DB.");
- }
+ status = report.Status.ToString(),
+ errors = report.Entries.Select(e => new
+ {
+ key = e.Key,
+ value = Enum.GetName(typeof(HealthStatus), e.Value.Status)
+ })
+ }.ToJson();
+ context.Response.ContentType = MediaTypeNames.Application.Json;
+ await context.Response.WriteAsync(result);
}
+ });
+if (app.Environment.IsDevelopment())
+{
+ app.Logger.LogInformation("Adding Development middleware...");
+ app.UseDeveloperExceptionPage();
+ app.UseShowAllServicesMiddleware();
+ app.UseMigrationsEndPoint();
+ app.UseWebAssemblyDebugging();
+}
+else
+{
+ app.Logger.LogInformation("Adding non-Development middleware...");
+ app.UseExceptionHandler("/Error");
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+}
- host.Run();
- }
+app.UseHttpsRedirection();
+app.UseBlazorFrameworkFiles();
+app.UseStaticFiles();
+app.UseRouting();
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup();
- });
+app.UseCookiePolicy();
+app.UseAuthentication();
+app.UseAuthorization();
+
+app.UseEndpoints(endpoints =>
+{
+ endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
+ endpoints.MapRazorPages();
+ endpoints.MapHealthChecks("home_page_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("homePageHealthCheck") });
+ endpoints.MapHealthChecks("api_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("apiHealthCheck") });
+ //endpoints.MapBlazorHub("/admin");
+ endpoints.MapFallbackToFile("index.html");
+});
+
+app.Logger.LogInformation("Seeding Database...");
+
+using (var scope = app.Services.CreateScope())
+{
+ var scopedProvider = scope.ServiceProvider;
+ try
+ {
+ var catalogContext = scopedProvider.GetRequiredService();
+ await CatalogContextSeed.SeedAsync(catalogContext, app.Logger);
+
+ var userManager = scopedProvider.GetRequiredService>();
+ var roleManager = scopedProvider.GetRequiredService>();
+ await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
+ }
+ catch (Exception ex)
+ {
+ app.Logger.LogError(ex, "An error occurred seeding the DB.");
+ }
}
+
+app.Logger.LogInformation("LAUNCHING");
+app.Run();
diff --git a/src/Web/Startup.cs b/src/Web/Startup.cs
deleted file mode 100644
index d854a6ffd..000000000
--- a/src/Web/Startup.cs
+++ /dev/null
@@ -1,241 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Net.Mime;
-using Ardalis.ListStartupServices;
-using BlazorAdmin;
-using BlazorAdmin.Services;
-using Blazored.LocalStorage;
-using BlazorShared;
-using Microsoft.AspNetCore.Authentication.Cookies;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.DataProtection;
-using Microsoft.AspNetCore.Diagnostics.HealthChecks;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.eShopWeb.ApplicationCore.Interfaces;
-using Microsoft.eShopWeb.Infrastructure.Data;
-using Microsoft.eShopWeb.Infrastructure.Identity;
-using Microsoft.eShopWeb.Web.Configuration;
-using Microsoft.eShopWeb.Web.HealthChecks;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Diagnostics.HealthChecks;
-using Microsoft.Extensions.Hosting;
-
-namespace Microsoft.eShopWeb.Web;
-
-public class Startup
-{
- private IServiceCollection _services;
-
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureDevelopmentServices(IServiceCollection services)
- {
- // use in-memory database
- ConfigureInMemoryDatabases(services);
-
- // use real database
- //ConfigureProductionServices(services);
- }
-
- public void ConfigureDockerServices(IServiceCollection services)
- {
- services.AddDataProtection()
- .SetApplicationName("eshopwebmvc")
- .PersistKeysToFileSystem(new DirectoryInfo(@"./"));
-
- ConfigureDevelopmentServices(services);
- }
-
- private void ConfigureInMemoryDatabases(IServiceCollection services)
- {
- // use in-memory database
- services.AddDbContext(c =>
- c.UseInMemoryDatabase("Catalog"));
-
- // Add Identity DbContext
- services.AddDbContext(options =>
- options.UseInMemoryDatabase("Identity"));
-
- ConfigureServices(services);
- }
-
- public void ConfigureProductionServices(IServiceCollection services)
- {
- // use real database
- // Requires LocalDB which can be installed with SQL Server Express 2016
- // https://www.microsoft.com/en-us/download/details.aspx?id=54284
- services.AddDbContext(c =>
- c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection")));
-
- // Add Identity DbContext
- services.AddDbContext(options =>
- options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
-
- ConfigureServices(services);
- }
-
- public void ConfigureTestingServices(IServiceCollection services)
- {
- ConfigureInMemoryDatabases(services);
- }
-
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddCookieSettings();
-
-
- services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
- .AddCookie(options =>
- {
- options.Cookie.HttpOnly = true;
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
- options.Cookie.SameSite = SameSiteMode.Lax;
- });
-
- services.AddIdentity()
- .AddDefaultUI()
- .AddEntityFrameworkStores()
- .AddDefaultTokenProviders();
-
- services.AddScoped();
-
- services.AddCoreServices(Configuration);
- services.AddWebServices(Configuration);
-
- // Add memory cache services
- services.AddMemoryCache();
- services.AddRouting(options =>
- {
- // Replace the type and the name used to refer to it with your own
- // IOutboundParameterTransformer implementation
- options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
- });
- services.AddMvc(options =>
- {
- options.Conventions.Add(new RouteTokenTransformerConvention(
- new SlugifyParameterTransformer()));
-
- });
- services.AddControllersWithViews();
- services.AddRazorPages(options =>
- {
- options.Conventions.AuthorizePage("/Basket/Checkout");
- });
- services.AddHttpContextAccessor();
- services
- .AddHealthChecks()
- .AddCheck("api_health_check", tags: new[] { "apiHealthCheck" })
- .AddCheck("home_page_health_check", tags: new[] { "homePageHealthCheck" });
- services.Configure(config =>
- {
- config.Services = new List(services);
-
- config.Path = "/allservices";
- });
-
- 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)
- });
-
- // add blazor services
- services.AddBlazoredLocalStorage();
- services.AddServerSideBlazor();
-
-
- services.AddScoped();
- services.AddScoped();
- services.AddBlazorServices();
-
- services.AddDatabaseDeveloperPageExceptionFilter();
-
- _services = services; // used to debug registered services
- }
-
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- var catalogBaseUrl = Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string;
- if (!string.IsNullOrEmpty(catalogBaseUrl))
- {
- app.Use((context, next) =>
- {
- context.Request.PathBase = new PathString(catalogBaseUrl);
- return next();
- });
- }
-
- app.UseHealthChecks("/health",
- new HealthCheckOptions
- {
- ResponseWriter = async (context, report) =>
- {
- var result = new
- {
- status = report.Status.ToString(),
- errors = report.Entries.Select(e => new
- {
- key = e.Key,
- value = Enum.GetName(typeof(HealthStatus), e.Value.Status)
- })
- }.ToJson();
- context.Response.ContentType = MediaTypeNames.Application.Json;
- await context.Response.WriteAsync(result);
- }
- });
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- app.UseShowAllServicesMiddleware();
- app.UseMigrationsEndPoint();
- app.UseWebAssemblyDebugging();
- }
- else
- {
- app.UseExceptionHandler("/Error");
- // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
- app.UseHsts();
- }
-
- app.UseHttpsRedirection();
- app.UseBlazorFrameworkFiles();
- app.UseStaticFiles();
- app.UseRouting();
-
- app.UseCookiePolicy();
- app.UseAuthentication();
- app.UseAuthorization();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
- endpoints.MapRazorPages();
- endpoints.MapHealthChecks("home_page_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("homePageHealthCheck") });
- endpoints.MapHealthChecks("api_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("apiHealthCheck") });
- //endpoints.MapBlazorHub("/admin");
- endpoints.MapFallbackToFile("index.html");
- });
- }
-
-}
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj
index 93d6f8229..53b237438 100644
--- a/src/Web/Web.csproj
+++ b/src/Web/Web.csproj
@@ -2,23 +2,17 @@
net6.0
+ enable
+ enable
Microsoft.eShopWeb.Web
aspnet-Web2-1FA3F72E-E7E3-4360-9E49-1CCCD7FE85F7
latest
-
-
-
-
-
-
-
-
diff --git a/tests/FunctionalTests/PublicApi/ApiTestFixture.cs b/tests/FunctionalTests/PublicApi/ApiTestFixture.cs
index 5435d4330..4513284eb 100644
--- a/tests/FunctionalTests/PublicApi/ApiTestFixture.cs
+++ b/tests/FunctionalTests/PublicApi/ApiTestFixture.cs
@@ -1,78 +1,43 @@
-using System;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.Identity;
-using Microsoft.eShopWeb.PublicApi;
+using Microsoft.eShopWeb.PublicApi.AuthEndpoints;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Hosting;
namespace Microsoft.eShopWeb.FunctionalTests.PublicApi;
-public class ApiTestFixture : WebApplicationFactory
+public class TestApiApplication : WebApplicationFactory
{
- protected override void ConfigureWebHost(IWebHostBuilder builder)
+ private readonly string _environment = "Testing";
+
+ protected override IHost CreateHost(IHostBuilder builder)
{
- builder.UseEnvironment("Testing");
+ builder.UseEnvironment(_environment);
+ // Add mock/test services to the builder here
builder.ConfigureServices(services =>
{
- services.AddEntityFrameworkInMemoryDatabase();
-
- // Create a new service provider.
- var provider = services
- .AddEntityFrameworkInMemoryDatabase()
- .BuildServiceProvider();
-
- // Add a database context (ApplicationDbContext) using an in-memory
- // database for testing.
- services.AddDbContext(options =>
+ services.AddScoped(sp =>
{
- options.UseInMemoryDatabase("InMemoryDbForTesting");
- options.UseInternalServiceProvider(provider);
+ // Replace SQLite with in-memory database for tests
+ return new DbContextOptionsBuilder()
+ .UseInMemoryDatabase("DbForPublicApi")
+ .UseApplicationServiceProvider(sp)
+ .Options;
});
-
- services.AddDbContext(options =>
+ services.AddScoped(sp =>
{
- options.UseInMemoryDatabase("Identity");
- options.UseInternalServiceProvider(provider);
+ // Replace SQLite with in-memory database for tests
+ return new DbContextOptionsBuilder()
+ .UseInMemoryDatabase("IdentityDbForPublicApi")
+ .UseApplicationServiceProvider(sp)
+ .Options;
});
-
- // Build the service provider.
- var sp = services.BuildServiceProvider();
-
- // Create a scope to obtain a reference to the database
- // context (ApplicationDbContext).
- using (var scope = sp.CreateScope())
- {
- var scopedServices = scope.ServiceProvider;
- var db = scopedServices.GetRequiredService();
- var loggerFactory = scopedServices.GetRequiredService();
-
- var logger = scopedServices
- .GetRequiredService>();
-
- // Ensure the database is created.
- db.Database.EnsureCreated();
-
- try
- {
- // Seed the database with test data.
- CatalogContextSeed.SeedAsync(db, loggerFactory).Wait();
-
- // seed sample user data
- var userManager = scopedServices.GetRequiredService>();
- var roleManager = scopedServices.GetRequiredService>();
- AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait();
- }
- catch (Exception ex)
- {
- logger.LogError(ex, $"An error occurred seeding the " +
- "database with test messages. Error: {ex.Message}");
- }
- }
});
+
+ return base.CreateHost(builder);
}
}
diff --git a/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs b/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
index ead822eba..d695245ba 100644
--- a/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
+++ b/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
@@ -10,11 +10,11 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class AuthenticateEndpoint : IClassFixture
+public class AuthenticateEndpoint : IClassFixture
{
JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
- public AuthenticateEndpoint(ApiTestFixture factory)
+ public AuthenticateEndpoint(TestApiApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs
index 514e29e05..1870e731f 100644
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs
+++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs
@@ -8,9 +8,9 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class ApiCatalogControllerList : IClassFixture
+public class ApiCatalogControllerList : IClassFixture
{
- public ApiCatalogControllerList(ApiTestFixture factory)
+ public ApiCatalogControllerList(TestApiApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs
index 855ea3663..3b75095ea 100644
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs
+++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs
@@ -12,7 +12,7 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class CreateEndpoint : IClassFixture
+public class CreateEndpoint : IClassFixture
{
JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
private int _testBrandId = 1;
@@ -21,7 +21,7 @@ public class CreateEndpoint : IClassFixture
private string _testName = "test name";
private decimal _testPrice = 1.23m;
- public CreateEndpoint(ApiTestFixture factory)
+ public CreateEndpoint(TestApiApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs
index dde7e280c..5d62e1279 100644
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs
+++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs
@@ -11,11 +11,11 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class DeleteEndpoint : IClassFixture
+public class DeleteEndpoint : IClassFixture
{
JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
- public DeleteEndpoint(ApiTestFixture factory)
+ public DeleteEndpoint(TestApiApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs
index c8a3b4070..0d7e1b436 100644
--- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs
+++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs
@@ -9,11 +9,11 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class GetByIdEndpoint : IClassFixture
+public class GetByIdEndpoint : IClassFixture
{
JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
- public GetByIdEndpoint(ApiTestFixture factory)
+ public GetByIdEndpoint(TestApiApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs b/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs
index 1625c7fd7..37d8a1a98 100644
--- a/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs
+++ b/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs
@@ -11,9 +11,9 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class AccountControllerSignIn : IClassFixture
+public class AccountControllerSignIn : IClassFixture
{
- public AccountControllerSignIn(WebTestFixture factory)
+ public AccountControllerSignIn(TestApplication factory)
{
Client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
diff --git a/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs b/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs
index c2f26b456..82c804b5c 100644
--- a/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs
+++ b/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs
@@ -5,9 +5,9 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class CatalogControllerIndex : IClassFixture
+public class CatalogControllerIndex : IClassFixture
{
- public CatalogControllerIndex(WebTestFixture factory)
+ public CatalogControllerIndex(TestApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs b/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs
index e739f484b..72d1f23c8 100644
--- a/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs
+++ b/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs
@@ -7,9 +7,9 @@
namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers;
[Collection("Sequential")]
-public class OrderIndexOnGet : IClassFixture
+public class OrderIndexOnGet : IClassFixture
{
- public OrderIndexOnGet(WebTestFixture factory)
+ public OrderIndexOnGet(TestApplication factory)
{
Client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
diff --git a/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs b/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs
index 8014c04b1..202334618 100644
--- a/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs
+++ b/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs
@@ -10,9 +10,9 @@
namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages;
[Collection("Sequential")]
-public class BasketPageCheckout : IClassFixture
+public class BasketPageCheckout : IClassFixture
{
- public BasketPageCheckout(WebTestFixture factory)
+ public BasketPageCheckout(TestApplication factory)
{
Client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
diff --git a/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs b/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs
index 873d3eaa5..76cc135ed 100644
--- a/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs
+++ b/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs
@@ -6,9 +6,9 @@
namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages;
[Collection("Sequential")]
-public class HomePageOnGet : IClassFixture
+public class HomePageOnGet : IClassFixture
{
- public HomePageOnGet(WebTestFixture factory)
+ public HomePageOnGet(TestApplication factory)
{
Client = factory.CreateClient();
}
diff --git a/tests/FunctionalTests/Web/WebTestFixture.cs b/tests/FunctionalTests/Web/WebTestFixture.cs
index e7194f751..55b2e7412 100644
--- a/tests/FunctionalTests/Web/WebTestFixture.cs
+++ b/tests/FunctionalTests/Web/WebTestFixture.cs
@@ -5,74 +5,42 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.Identity;
-using Microsoft.eShopWeb.Web;
+using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Microsoft.eShopWeb.FunctionalTests.Web;
-public class WebTestFixture : WebApplicationFactory
+public class TestApplication : WebApplicationFactory
{
- protected override void ConfigureWebHost(IWebHostBuilder builder)
+ private readonly string _environment = "Development";
+
+ protected override IHost CreateHost(IHostBuilder builder)
{
- builder.UseEnvironment("Testing");
+ builder.UseEnvironment(_environment);
+ // Add mock/test services to the builder here
builder.ConfigureServices(services =>
{
- services.AddEntityFrameworkInMemoryDatabase();
-
- // Create a new service provider.
- var provider = services
- .AddEntityFrameworkInMemoryDatabase()
- .BuildServiceProvider();
-
- // Add a database context (ApplicationDbContext) using an in-memory
- // database for testing.
- services.AddDbContext(options =>
+ services.AddScoped(sp =>
{
- options.UseInMemoryDatabase("InMemoryDbForTesting");
- options.UseInternalServiceProvider(provider);
+ // Replace SQLite with in-memory database for tests
+ return new DbContextOptionsBuilder()
+ .UseInMemoryDatabase("InMemoryDbForTesting")
+ .UseApplicationServiceProvider(sp)
+ .Options;
});
-
- services.AddDbContext(options =>
+ services.AddScoped(sp =>
{
- options.UseInMemoryDatabase("Identity");
- options.UseInternalServiceProvider(provider);
+ // Replace SQLite with in-memory database for tests
+ return new DbContextOptionsBuilder()
+ .UseInMemoryDatabase("Identity")
+ .UseApplicationServiceProvider(sp)
+ .Options;
});
-
- // Build the service provider.
- var sp = services.BuildServiceProvider();
-
- // Create a scope to obtain a reference to the database
- // context (ApplicationDbContext).
- using (var scope = sp.CreateScope())
- {
- var scopedServices = scope.ServiceProvider;
- var db = scopedServices.GetRequiredService();
- var loggerFactory = scopedServices.GetRequiredService();
-
- var logger = scopedServices
- .GetRequiredService>();
-
- // Ensure the database is created.
- db.Database.EnsureCreated();
-
- try
- {
- // Seed the database with test data.
- CatalogContextSeed.SeedAsync(db, loggerFactory).Wait();
-
- // seed sample user data
- var userManager = scopedServices.GetRequiredService>();
- var roleManager = scopedServices.GetRequiredService>();
- AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait();
- }
- catch (Exception ex)
- {
- logger.LogError(ex, $"An error occurred seeding the " +
- "database with test messages. Error: {ex.Message}");
- }
- }
});
+
+ return base.CreateHost(builder);
}
}