diff --git a/AuthService.Test/AuthService.Test.csproj b/AuthService.Test/AuthService.Test.csproj
index e64cc4f9..58b81b27 100644
--- a/AuthService.Test/AuthService.Test.csproj
+++ b/AuthService.Test/AuthService.Test.csproj
@@ -20,6 +20,7 @@
+
diff --git a/AuthService.Test/AuthServiceTests.cs b/AuthService.Test/AuthServiceTests.cs
index 1cb310ee..c81577ed 100644
--- a/AuthService.Test/AuthServiceTests.cs
+++ b/AuthService.Test/AuthServiceTests.cs
@@ -1,6 +1,9 @@
using System;
-using AuthService.DataAccess;
+using System.Collections.Generic;
+using AuthService.DataAccess.EF;
+using AuthService.Domain;
using FluentAssertions;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
@@ -11,21 +14,37 @@ namespace AuthService.Test;
public class AuthServiceTests
{
private readonly Domain.AuthService authService;
- private readonly InsuranceAgentsInMemoryDb agentsDb;
+ private readonly IInsuranceAgents agentsRepository;
private readonly AppSettings appSettings;
private readonly ITestOutputHelper output;
public AuthServiceTests(ITestOutputHelper output)
{
this.output = output;
- agentsDb = new InsuranceAgentsInMemoryDb();
+
+ // Setup EF Core in-memory database
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
+ .Options;
+
+ var dbContext = new AuthDbContext(options);
+ agentsRepository = new InsuranceAgentRepository(dbContext);
+
+ // Seed test data
+ agentsRepository.Add(new InsuranceAgent("jimmy.solid", "secret", "static/avatars/jimmy_solid.png",
+ new List { "TRI", "HSI", "FAI", "CAR" }));
+ agentsRepository.Add(new InsuranceAgent("danny.solid", "secret", "static/avatars/danny.solid.png",
+ new List { "TRI", "HSI", "FAI", "CAR" }));
+ agentsRepository.Add(new InsuranceAgent("admin", "admin", "static/avatars/admin.png",
+ new List { "TRI", "HSI", "FAI", "CAR" }));
+
appSettings = new AppSettings
{
Secret = "ThisIsASecretKeyForJWTTokenGeneration123456789"
};
- var options = Options.Create(appSettings);
+ var appSettingsOptions = Options.Create(appSettings);
- authService = new Domain.AuthService(agentsDb, options);
+ authService = new Domain.AuthService(agentsRepository, appSettingsOptions);
}
[Theory]
diff --git a/AuthService/AuthService.csproj b/AuthService/AuthService.csproj
index 18a0c100..4a3d3617 100644
--- a/AuthService/AuthService.csproj
+++ b/AuthService/AuthService.csproj
@@ -8,6 +8,8 @@
+
+
diff --git a/AuthService/DataAccess/EF/AuthDbContext.cs b/AuthService/DataAccess/EF/AuthDbContext.cs
new file mode 100644
index 00000000..b21ef9a4
--- /dev/null
+++ b/AuthService/DataAccess/EF/AuthDbContext.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+using AuthService.Domain;
+
+namespace AuthService.DataAccess.EF;
+
+public class AuthDbContext : DbContext
+{
+ public AuthDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ public DbSet InsuranceAgents { get; set; }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ optionsBuilder.EnableSensitiveDataLogging();
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasKey(e => e.Id);
+ entity.HasIndex(e => e.Login).IsUnique();
+ entity.Property(e => e.Login).IsRequired();
+ entity.Property(e => e.Password).IsRequired();
+ entity.Property(e => e.Avatar).IsRequired();
+ entity.Property(e => e.AvailableProducts)
+ .HasConversion(
+ v => string.Join(',', v),
+ v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList()
+ );
+ });
+ }
+}
diff --git a/AuthService/DataAccess/EF/EFInstaller.cs b/AuthService/DataAccess/EF/EFInstaller.cs
new file mode 100644
index 00000000..07239ed8
--- /dev/null
+++ b/AuthService/DataAccess/EF/EFInstaller.cs
@@ -0,0 +1,20 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using AuthService.Domain;
+
+namespace AuthService.DataAccess.EF;
+
+public static class EFInstaller
+{
+ public static IServiceCollection AddEFConfiguration(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddDbContext(options =>
+ {
+ options.UseInMemoryDatabase("InsuranceAgents");
+ });
+
+ services.AddScoped();
+ return services;
+ }
+}
diff --git a/AuthService/DataAccess/EF/InsuranceAgentRepository.cs b/AuthService/DataAccess/EF/InsuranceAgentRepository.cs
new file mode 100644
index 00000000..04906ab5
--- /dev/null
+++ b/AuthService/DataAccess/EF/InsuranceAgentRepository.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+using AuthService.Domain;
+
+namespace AuthService.DataAccess.EF;
+
+public class InsuranceAgentRepository : IInsuranceAgents
+{
+ private readonly AuthDbContext dbContext;
+
+ public InsuranceAgentRepository(AuthDbContext dbContext)
+ {
+ this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
+ }
+
+ public void Add(InsuranceAgent agent)
+ {
+ dbContext.InsuranceAgents.Add(agent);
+ }
+
+ public InsuranceAgent FindByLogin(string login)
+ {
+ return dbContext.InsuranceAgents
+ .FirstOrDefault(a => a.Login == login);
+ }
+}
diff --git a/AuthService/DataAccess/InsuranceAgentsInMemoryDb.cs b/AuthService/DataAccess/InsuranceAgentsInMemoryDb.cs
deleted file mode 100644
index 26c8c88c..00000000
--- a/AuthService/DataAccess/InsuranceAgentsInMemoryDb.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using AuthService.Domain;
-
-namespace AuthService.DataAccess;
-
-public class InsuranceAgentsInMemoryDb : IInsuranceAgents
-{
- private readonly IDictionary db = new ConcurrentDictionary();
-
- public InsuranceAgentsInMemoryDb()
- {
- Add(new InsuranceAgent("jimmy.solid", "secret", "static/avatars/jimmy_solid.png",
- new List { "TRI", "HSI", "FAI", "CAR" }));
- Add(new InsuranceAgent("danny.solid", "secret", "static/avatars/danny.solid.png",
- new List { "TRI", "HSI", "FAI", "CAR" }));
- Add(new InsuranceAgent("admin", "admin", "static/avatars/admin.png",
- new List { "TRI", "HSI", "FAI", "CAR" }));
- }
-
- public void Add(InsuranceAgent agent)
- {
- db[agent.Login] = agent;
- }
-
- public InsuranceAgent FindByLogin(string login)
- {
- return db.TryGetValue(login, out var agent) ? agent : null;
- }
-}
\ No newline at end of file
diff --git a/AuthService/Domain/InsuranceAgent.cs b/AuthService/Domain/InsuranceAgent.cs
index cc2543cf..0bd6206d 100644
--- a/AuthService/Domain/InsuranceAgent.cs
+++ b/AuthService/Domain/InsuranceAgent.cs
@@ -4,6 +4,11 @@ namespace AuthService.Domain;
public class InsuranceAgent
{
+ // EF Core requires a parameterless constructor
+ private InsuranceAgent()
+ {
+ }
+
public InsuranceAgent(string login, string password, string avatar, List availableProducts)
{
Login = login;
@@ -12,13 +17,14 @@ public InsuranceAgent(string login, string password, string avatar, List
AvailableProducts = availableProducts;
}
- public string Login { get; }
- public string Password { get; }
- public string Avatar { get; }
- public List AvailableProducts { get; }
+ public int Id { get; private set; }
+ public string Login { get; private set; }
+ public string Password { get; private set; }
+ public string Avatar { get; private set; }
+ public List AvailableProducts { get; private set; }
public bool PasswordMatches(string passwordToTest)
{
return Password == passwordToTest;
}
-}
\ No newline at end of file
+}
diff --git a/AuthService/Init/ApplicationBuilderExtensions.cs b/AuthService/Init/ApplicationBuilderExtensions.cs
new file mode 100644
index 00000000..307f4333
--- /dev/null
+++ b/AuthService/Init/ApplicationBuilderExtensions.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AuthService.Init;
+
+public static class ApplicationBuilderExtensions
+{
+ public static void UseInitializer(this IApplicationBuilder app)
+ {
+ using (var scope = app.ApplicationServices.CreateScope())
+ {
+ var initializer = scope.ServiceProvider.GetService();
+ initializer.Seed();
+ }
+ }
+}
diff --git a/AuthService/Init/DataLoader.cs b/AuthService/Init/DataLoader.cs
new file mode 100644
index 00000000..a126f9a4
--- /dev/null
+++ b/AuthService/Init/DataLoader.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Linq;
+using AuthService.DataAccess.EF;
+using AuthService.Domain;
+
+namespace AuthService.Init;
+
+public class DataLoader
+{
+ private readonly AuthDbContext dbContext;
+
+ public DataLoader(AuthDbContext context)
+ {
+ dbContext = context;
+ }
+
+ public void Seed()
+ {
+ dbContext.Database.EnsureCreated();
+ if (dbContext.InsuranceAgents.Any()) return;
+
+ dbContext.InsuranceAgents.Add(new InsuranceAgent("jimmy.solid", "secret", "static/avatars/jimmy_solid.png",
+ new List { "TRI", "HSI", "FAI", "CAR" }));
+ dbContext.InsuranceAgents.Add(new InsuranceAgent("danny.solid", "secret", "static/avatars/danny.solid.png",
+ new List { "TRI", "HSI", "FAI", "CAR" }));
+ dbContext.InsuranceAgents.Add(new InsuranceAgent("admin", "admin", "static/avatars/admin.png",
+ new List { "TRI", "HSI", "FAI", "CAR" }));
+
+ dbContext.SaveChanges();
+ }
+}
diff --git a/AuthService/Init/DataLoaderInstaller.cs b/AuthService/Init/DataLoaderInstaller.cs
new file mode 100644
index 00000000..6f00c364
--- /dev/null
+++ b/AuthService/Init/DataLoaderInstaller.cs
@@ -0,0 +1,12 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AuthService.Init;
+
+public static class DataLoaderInstaller
+{
+ public static IServiceCollection AddAuthDemoInitializer(this IServiceCollection services)
+ {
+ services.AddScoped();
+ return services;
+ }
+}
diff --git a/AuthService/Startup.cs b/AuthService/Startup.cs
index 725a2f47..9dfef9d1 100644
--- a/AuthService/Startup.cs
+++ b/AuthService/Startup.cs
@@ -1,6 +1,7 @@
using System.Text;
-using AuthService.DataAccess;
+using AuthService.DataAccess.EF;
using AuthService.Domain;
+using AuthService.Init;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -59,7 +60,8 @@ public void ConfigureServices(IServiceCollection services)
});
services.AddSingleton();
- services.AddSingleton();
+ services.AddEFConfiguration(Configuration);
+ services.AddAuthDemoInitializer();
services.AddSwaggerGen();
}
@@ -84,6 +86,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAuthorization();
app.UseHttpsRedirection();
+
+ // Ensure initializer is awaited so seeding completes before the app starts handling requests
+ app.UseInitializer();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
\ No newline at end of file