From 8f842cd1721290b69928d582cd9f3ced7ea2b15e Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Thu, 14 Nov 2024 03:38:38 -0700 Subject: [PATCH 1/9] Interim commit --- .../Interfaces/IConfigService.cs | 16 +++ RateLimiter.Tests/RateLimiter.Tests.csproj | 1 + RateLimiter.Tests/Services/ConfigService.cs | 38 ++++++ .../Tests/Data/RequestsDataServiceTest.cs | 56 ++++++++ .../Tests/Data/ResourcesDataServiceTest.cs | 46 +++++++ .../Tests/Data/StatusesDataServiceTest.cs | 46 +++++++ .../Tests/Data/UsersDataServiceTest.cs | 46 +++++++ .../RateLimiter/LimiterConfigServiceTest.cs | 58 +++++++++ .../Tests/RateLimiter/LimiterServiceTest.cs | 58 +++++++++ .../RateLimiter/RequestHandlerServiceTest.cs | 58 +++++++++ .../RateLimiter/RequestsAuditServiceTest.cs | 58 +++++++++ .../Data/DbContexts/RateLimiterDbContext.cs | 18 +++ RateLimiter/Data/Interfaces/IDataService.cs | 24 ++++ RateLimiter/Data/Models/Data/BaseModel.cs | 26 ++++ RateLimiter/Data/Models/Data/Request.cs | 16 +++ RateLimiter/Data/Models/Data/Resource.cs | 22 ++++ RateLimiter/Data/Models/Data/Status.cs | 14 ++ RateLimiter/Data/Models/Data/User.cs | 15 +++ RateLimiter/Data/Repositories/DbRepository.cs | 123 ++++++++++++++++++ .../Data/Services/RequestsDataService.cs | 50 +++++++ .../Data/Services/ResourcesDataService.cs | 50 +++++++ .../Data/Services/StatusesDataService.cs | 50 +++++++ RateLimiter/Data/Services/UsersDataService.cs | 50 +++++++ RateLimiter/Interfaces/IAuditService.cs | 15 +++ RateLimiter/Interfaces/ILimiterService.cs | 13 ++ RateLimiter/Models/Filter/RequestsFilter.cs | 14 ++ RateLimiter/Models/Response.cs | 13 ++ RateLimiter/Models/Rule.cs | 12 ++ RateLimiter/RateLimiter.csproj | 6 + RateLimiter/Services/LimiterService.cs | 27 ++++ RateLimiter/Services/RequestsAuditService.cs | 31 +++++ 31 files changed, 1070 insertions(+) create mode 100644 RateLimiter.Tests/Interfaces/IConfigService.cs create mode 100644 RateLimiter.Tests/Services/ConfigService.cs create mode 100644 RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs create mode 100644 RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs create mode 100644 RateLimiter/Data/DbContexts/RateLimiterDbContext.cs create mode 100644 RateLimiter/Data/Interfaces/IDataService.cs create mode 100644 RateLimiter/Data/Models/Data/BaseModel.cs create mode 100644 RateLimiter/Data/Models/Data/Request.cs create mode 100644 RateLimiter/Data/Models/Data/Resource.cs create mode 100644 RateLimiter/Data/Models/Data/Status.cs create mode 100644 RateLimiter/Data/Models/Data/User.cs create mode 100644 RateLimiter/Data/Repositories/DbRepository.cs create mode 100644 RateLimiter/Data/Services/RequestsDataService.cs create mode 100644 RateLimiter/Data/Services/ResourcesDataService.cs create mode 100644 RateLimiter/Data/Services/StatusesDataService.cs create mode 100644 RateLimiter/Data/Services/UsersDataService.cs create mode 100644 RateLimiter/Interfaces/IAuditService.cs create mode 100644 RateLimiter/Interfaces/ILimiterService.cs create mode 100644 RateLimiter/Models/Filter/RequestsFilter.cs create mode 100644 RateLimiter/Models/Response.cs create mode 100644 RateLimiter/Models/Rule.cs create mode 100644 RateLimiter/Services/LimiterService.cs create mode 100644 RateLimiter/Services/RequestsAuditService.cs diff --git a/RateLimiter.Tests/Interfaces/IConfigService.cs b/RateLimiter.Tests/Interfaces/IConfigService.cs new file mode 100644 index 00000000..246e43b6 --- /dev/null +++ b/RateLimiter.Tests/Interfaces/IConfigService.cs @@ -0,0 +1,16 @@ +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Interfaces +{ + public interface IConfigService + { + public Task SeedStatuses(List statuses); + public Task SeedResources(List resources); + public Task SeedUsers(List users); + } +} diff --git a/RateLimiter.Tests/RateLimiter.Tests.csproj b/RateLimiter.Tests/RateLimiter.Tests.csproj index 5cbfc4e8..a90f1cb8 100644 --- a/RateLimiter.Tests/RateLimiter.Tests.csproj +++ b/RateLimiter.Tests/RateLimiter.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/RateLimiter.Tests/Services/ConfigService.cs b/RateLimiter.Tests/Services/ConfigService.cs new file mode 100644 index 00000000..bd1f257f --- /dev/null +++ b/RateLimiter.Tests/Services/ConfigService.cs @@ -0,0 +1,38 @@ +using RateLimiter.Data.Interfaces; +using RateLimiter.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class ConfigService : IConfigService + { + private readonly IDataService _resourceDataService; + private readonly IDataService _userDataService; + private readonly IDataService _statusesDataService; + + public ConfigService(IDataService resourceDataService, IDataService userDataService, IDataService statusesDataService) + { + _resourceDataService = resourceDataService; + _userDataService = userDataService; + _statusesDataService = statusesDataService; + } + + public async Task SeedStatuses(List statuses) + { + throw new NotImplementedException(); + } + public async Task SeedResources(List resources) + { + throw new NotImplementedException(); + } + public async Task SeedUsers(List users) + { + throw new NotImplementedException(); + } + } +} diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs new file mode 100644 index 00000000..630b9e99 --- /dev/null +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -0,0 +1,56 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RateLimiter.Tests; + +[TestFixture] +public class RequestsDataServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, RequestsDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} + +//public Task> Get(); +//public Task> Get(BaseModel searchCriteria); +//public Task Get(int id); +//public Task Get(string identifier); +//public Task Add(T entity); +//public Task Update(int id, T entity); +//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs new file mode 100644 index 00000000..c03642a9 --- /dev/null +++ b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs @@ -0,0 +1,46 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; + +namespace RateLimiter.Tests; + +[TestFixture] +public class ResourcesDataServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, ResourcesDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var resourcesDataService = _serviceProvider.GetService>(); + + try + { + var requests = resourcesDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs new file mode 100644 index 00000000..955c5b50 --- /dev/null +++ b/RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs @@ -0,0 +1,46 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; + +namespace RateLimiter.Tests; + +[TestFixture] +public class StatusesDataServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, StatusesDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var statusesDataService = _serviceProvider.GetService>(); + + try + { + var requests = statusesDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs new file mode 100644 index 00000000..3f45d3a6 --- /dev/null +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -0,0 +1,46 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; + +namespace RateLimiter.Tests; + +[TestFixture] +public class UsersDataServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, UsersDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var requestDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs new file mode 100644 index 00000000..a53670b2 --- /dev/null +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs @@ -0,0 +1,58 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RateLimiter.Tests; + +[TestFixture] +public class RequestsAuditServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, RequestsDataService>(); + services.AddScoped, ResourcesDataService>(); + services.AddScoped, UsersDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} + +//public Task> Get(); +//public Task> Get(BaseModel searchCriteria); +//public Task Get(int id); +//public Task Get(string identifier); +//public Task Add(T entity); +//public Task Update(int id, T entity); +//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs new file mode 100644 index 00000000..a53670b2 --- /dev/null +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -0,0 +1,58 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RateLimiter.Tests; + +[TestFixture] +public class RequestsAuditServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, RequestsDataService>(); + services.AddScoped, ResourcesDataService>(); + services.AddScoped, UsersDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} + +//public Task> Get(); +//public Task> Get(BaseModel searchCriteria); +//public Task Get(int id); +//public Task Get(string identifier); +//public Task Add(T entity); +//public Task Update(int id, T entity); +//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs new file mode 100644 index 00000000..a53670b2 --- /dev/null +++ b/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs @@ -0,0 +1,58 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RateLimiter.Tests; + +[TestFixture] +public class RequestsAuditServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, RequestsDataService>(); + services.AddScoped, ResourcesDataService>(); + services.AddScoped, UsersDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} + +//public Task> Get(); +//public Task> Get(BaseModel searchCriteria); +//public Task Get(int id); +//public Task Get(string identifier); +//public Task Add(T entity); +//public Task Update(int id, T entity); +//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs new file mode 100644 index 00000000..a53670b2 --- /dev/null +++ b/RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs @@ -0,0 +1,58 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace RateLimiter.Tests; + +[TestFixture] +public class RequestsAuditServiceTest +{ + ServiceProvider _serviceProvider; + + [SetUp] + public void SetUp() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, RequestsDataService>(); + services.AddScoped, ResourcesDataService>(); + services.AddScoped, UsersDataService>(); + + _serviceProvider = services.BuildServiceProvider(); + + } + [Test] + public void GetTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.Get(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} + +//public Task> Get(); +//public Task> Get(BaseModel searchCriteria); +//public Task Get(int id); +//public Task Get(string identifier); +//public Task Add(T entity); +//public Task Update(int id, T entity); +//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs new file mode 100644 index 00000000..7fc01602 --- /dev/null +++ b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Data +{ + public class RateLimiterDbContext : DbContext + { + public DbSet Users { get; set; } + public DbSet Resources { get; set; } + public DbSet Requests { get; set; } + public DbSet Statuses { get; set; } + } +} diff --git a/RateLimiter/Data/Interfaces/IDataService.cs b/RateLimiter/Data/Interfaces/IDataService.cs new file mode 100644 index 00000000..db97db70 --- /dev/null +++ b/RateLimiter/Data/Interfaces/IDataService.cs @@ -0,0 +1,24 @@ +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Data.Interfaces +{ + public interface IDataService where T : BaseModel + { + public Task> Get(); + + public Task> Get(BaseModel searchCriteria); + + public Task Get(int id); + + public Task Get(string identifier); + + public Task Add(T entity); + public Task Update(int id, T entity); + public Task Delete(int id); + } +} diff --git a/RateLimiter/Data/Models/Data/BaseModel.cs b/RateLimiter/Data/Models/Data/BaseModel.cs new file mode 100644 index 00000000..8e4d60dd --- /dev/null +++ b/RateLimiter/Data/Models/Data/BaseModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + public class BaseModel + { + [Key] + public int Id { get; set; } + + [Required] + public string Identifier { get; set; } + + [Required] + public DateTime CreatedDate { get; set; } + + [Required] + public string CreatedBy { get; set; } + public DateTime? Updated { get; set; } + public string? UpdatedBy { get; set; } + } +} diff --git a/RateLimiter/Data/Models/Data/Request.cs b/RateLimiter/Data/Models/Data/Request.cs new file mode 100644 index 00000000..3d15f2b0 --- /dev/null +++ b/RateLimiter/Data/Models/Data/Request.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + public class Request : BaseModel + { + public int UserId { get; set; } + public int ResourceId { get; set; } + public DateTime RequestDate { get; set; } + public bool WasHandled { get; set; } + } +} diff --git a/RateLimiter/Data/Models/Data/Resource.cs b/RateLimiter/Data/Models/Data/Resource.cs new file mode 100644 index 00000000..1e682946 --- /dev/null +++ b/RateLimiter/Data/Models/Data/Resource.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + public class Resource : BaseModel + { + public string Name { get; set; } + public int StatusId { get; set; } + public string Description { get; set; } + + // Need to put the rules that will deterine whether to limit + // usage of a resource + + //public List LimiterRules { get; set; } + + public Status Status { get; set; } + } +} diff --git a/RateLimiter/Data/Models/Data/Status.cs b/RateLimiter/Data/Models/Data/Status.cs new file mode 100644 index 00000000..e001d4e6 --- /dev/null +++ b/RateLimiter/Data/Models/Data/Status.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + public class Status : BaseModel + { + public string Name { get; set; } + + } +} diff --git a/RateLimiter/Data/Models/Data/User.cs b/RateLimiter/Data/Models/Data/User.cs new file mode 100644 index 00000000..48209da8 --- /dev/null +++ b/RateLimiter/Data/Models/Data/User.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + public class User : BaseModel + { + public string Name { get; set; } + public string Email { get; set; } + public string Token { get; set; } + } +} diff --git a/RateLimiter/Data/Repositories/DbRepository.cs b/RateLimiter/Data/Repositories/DbRepository.cs new file mode 100644 index 00000000..352b8582 --- /dev/null +++ b/RateLimiter/Data/Repositories/DbRepository.cs @@ -0,0 +1,123 @@ +using Microsoft.EntityFrameworkCore; +using RateLimiter.Data; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace M42.Data.Repositories +{ + public class DbRepository where Entity : BaseModel + { + private readonly RateLimiterDbContext _context; + private DbSet _dbSet; + + public DbRepository(RateLimiterDbContext context) + { + _context = context; + _dbSet = _context.Set(); + } + + public async Task> GetAllAsync(string[] includes) + { + var dbSet = _dbSet.AsQueryable(); + + foreach (var include in includes) + { + if (include != "") + { + dbSet = dbSet.Include(include); + } + } + + return await dbSet.ToListAsync(); + } + + public async Task SingleAsync(int id, string[] includes) + { + + var dbSet = _dbSet.AsQueryable(); + + foreach (var include in includes) + { + if (include != "") + { + dbSet = dbSet.Include(include); + } + } + + + var entity = await dbSet.SingleOrDefaultAsync(e => e.Id == id); + + if (entity == null) + { + throw new InvalidOperationException("Invalid Id value"); + } + + return entity; + } + + public async Task SingleAsync(string identifier, string[] includes) + { + + var dbSet = _dbSet.AsQueryable(); + + foreach (var include in includes) + { + if (include != "") + { + dbSet = dbSet.Include(include); + } + } + + var entity = await dbSet.SingleOrDefaultAsync(e => e.Identifier == identifier); + + if (entity == null) + { + throw new InvalidOperationException("Invalid identifier value"); + } + + return entity; + } + + public async Task SingleOrDefaultAsync(int id) + { + var dbSet = _dbSet.AsQueryable(); + var entity = await dbSet.SingleOrDefaultAsync(e => e.Id == id); + + if (entity == null) + { + throw new Exception("Invalid id"); + } + + return entity; + } + + public async Task Add(Entity entity) + { + var result = _dbSet.Add(entity); + + return true; + } + + public async Task Update(Entity entity) + { + var result = _dbSet.Update(entity); + + return true; + } + + public async Task Remove(Entity entity) + { + throw new NotImplementedException("Audit data should not be deleted."); + } + + public async Task SaveChangesAsync() + { + await _context.SaveChangesAsync(); + } + + } +} + diff --git a/RateLimiter/Data/Services/RequestsDataService.cs b/RateLimiter/Data/Services/RequestsDataService.cs new file mode 100644 index 00000000..20d62cdd --- /dev/null +++ b/RateLimiter/Data/Services/RequestsDataService.cs @@ -0,0 +1,50 @@ +using M42.Data.Repositories; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class RequestsDataService : IDataService + { + private readonly DbRepository _requestRepository; + + public RequestsDataService(DbRepository requestRepository) + { + _requestRepository = requestRepository; + } + + public async Task> Get() + { + throw new NotImplementedException(); + } + public async Task> Get(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } + public async Task Get(int id) + { + throw new NotImplementedException(); + } + public async Task Get(string identifier) + { + throw new NotImplementedException(); + } + public async Task Add(Request request) + { + throw new NotImplementedException(); + } + public async Task Update(int id, Request request) + { + throw new NotImplementedException(); + } + public async Task Delete(int id) + { + throw new NotImplementedException(); + } + } +} diff --git a/RateLimiter/Data/Services/ResourcesDataService.cs b/RateLimiter/Data/Services/ResourcesDataService.cs new file mode 100644 index 00000000..1a75cfeb --- /dev/null +++ b/RateLimiter/Data/Services/ResourcesDataService.cs @@ -0,0 +1,50 @@ +using M42.Data.Repositories; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class ResourcesDataService : IDataService + { + private readonly DbRepository _resourceRepository; + + public ResourcesDataService(DbRepository resourceRepository) + { + _resourceRepository = resourceRepository; + } + + public async Task> Get() + { + throw new NotImplementedException(); + } + public async Task> Get(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } + public async Task Get(int id) + { + throw new NotImplementedException(); + } + public async Task Get(string identifier) + { + throw new NotImplementedException(); + } + public async Task Add(Resource resource) + { + throw new NotImplementedException(); + } + public async Task Update(int id, Resource resource) + { + throw new NotImplementedException(); + } + public async Task Delete(int id) + { + throw new NotImplementedException(); + } + } +} diff --git a/RateLimiter/Data/Services/StatusesDataService.cs b/RateLimiter/Data/Services/StatusesDataService.cs new file mode 100644 index 00000000..abe1f8bd --- /dev/null +++ b/RateLimiter/Data/Services/StatusesDataService.cs @@ -0,0 +1,50 @@ +using M42.Data.Repositories; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class StatusesDataService : IDataService + { + private readonly DbRepository _statusesRepository; + + public StatusesDataService(DbRepository statusesRepository) + { + _statusesRepository = statusesRepository; + } + + public async Task> Get() + { + throw new NotImplementedException(); + } + public async Task> Get(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } + public async Task Get(int id) + { + throw new NotImplementedException(); + } + public async Task Get(string identifier) + { + throw new NotImplementedException(); + } + public async Task Add(Status user) + { + throw new NotImplementedException(); + } + public async Task Update(int id, Status user) + { + throw new NotImplementedException(); + } + public async Task Delete(int id) + { + throw new NotImplementedException(); + } + } +} diff --git a/RateLimiter/Data/Services/UsersDataService.cs b/RateLimiter/Data/Services/UsersDataService.cs new file mode 100644 index 00000000..057f8e7b --- /dev/null +++ b/RateLimiter/Data/Services/UsersDataService.cs @@ -0,0 +1,50 @@ +using M42.Data.Repositories; +using RateLimiter.Data.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class UsersDataService : IDataService + { + private readonly DbRepository _userRepository; + + public UsersDataService(DbRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task> Get() + { + throw new NotImplementedException(); + } + public async Task> Get(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } + public async Task Get(int id) + { + throw new NotImplementedException(); + } + public async Task Get(string identifier) + { + throw new NotImplementedException(); + } + public async Task Add(User user) + { + throw new NotImplementedException(); + } + public async Task Update(int id, User user) + { + throw new NotImplementedException(); + } + public async Task Delete(int id) + { + throw new NotImplementedException(); + } + } +} diff --git a/RateLimiter/Interfaces/IAuditService.cs b/RateLimiter/Interfaces/IAuditService.cs new file mode 100644 index 00000000..0b58fb12 --- /dev/null +++ b/RateLimiter/Interfaces/IAuditService.cs @@ -0,0 +1,15 @@ +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Interfaces +{ + public interface IAuditService + { + public Task Log(T data); + public Task> GetHistory(F filter); + } +} diff --git a/RateLimiter/Interfaces/ILimiterService.cs b/RateLimiter/Interfaces/ILimiterService.cs new file mode 100644 index 00000000..35ae9c84 --- /dev/null +++ b/RateLimiter/Interfaces/ILimiterService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Interfaces +{ + public interface ILimiterService + { + public Task LimitAccess(int ResourceId, int UserId); + } +} diff --git a/RateLimiter/Models/Filter/RequestsFilter.cs b/RateLimiter/Models/Filter/RequestsFilter.cs new file mode 100644 index 00000000..ac67dbd5 --- /dev/null +++ b/RateLimiter/Models/Filter/RequestsFilter.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Data.Models.Filter +{ + public class RequestsFilter + { + public int? RequestId { get; set; } + public int? UserId { get; set; } + } +} diff --git a/RateLimiter/Models/Response.cs b/RateLimiter/Models/Response.cs new file mode 100644 index 00000000..19f42436 --- /dev/null +++ b/RateLimiter/Models/Response.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + public class Response + { + public string HttpResponseCode { get; set; } + } +} diff --git a/RateLimiter/Models/Rule.cs b/RateLimiter/Models/Rule.cs new file mode 100644 index 00000000..4899461b --- /dev/null +++ b/RateLimiter/Models/Rule.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Models +{ + internal class Rule + { + } +} diff --git a/RateLimiter/RateLimiter.csproj b/RateLimiter/RateLimiter.csproj index 19962f52..66f39c4f 100644 --- a/RateLimiter/RateLimiter.csproj +++ b/RateLimiter/RateLimiter.csproj @@ -4,4 +4,10 @@ latest enable + + + + + + \ No newline at end of file diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs new file mode 100644 index 00000000..5e104bcc --- /dev/null +++ b/RateLimiter/Services/LimiterService.cs @@ -0,0 +1,27 @@ +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Filter; +using RateLimiter.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class LimiterService : ILimiterService + { + private readonly IAuditService _requestAuditService; + + public LimiterService(IAuditService requestAuditService) + { + _requestAuditService = requestAuditService; + } + + public async Task LimitAccess(int ResourceId, int UserId) + { + throw new NotImplementedException(); + } + } +} diff --git a/RateLimiter/Services/RequestsAuditService.cs b/RateLimiter/Services/RequestsAuditService.cs new file mode 100644 index 00000000..bc8ddbac --- /dev/null +++ b/RateLimiter/Services/RequestsAuditService.cs @@ -0,0 +1,31 @@ +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Filter; +using RateLimiter.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class RequestsAuditService : IAuditService + { + protected IDataService _requestDataService; + + public RequestsAuditService(IDataService requestDataService) + { + _requestDataService = requestDataService; + } + + public async Task Log(Request request) + { + throw new NotImplementedException(); + } + public async Task> GetHistory(RequestsFilter filter) + { + throw new NotImplementedException(); + } + } +} From dd8a47ea329976f12b876f35adf3fe9b96d7c386 Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Sat, 16 Nov 2024 09:54:24 -0700 Subject: [PATCH 2/9] Interim checkin #2 --- .../Interfaces/IConfigService.cs | 9 +- .../Interfaces/IDataGeneratorService.cs | 16 ++ RateLimiter.Tests/Services/ConfigService.cs | 67 +++++++-- .../Services/DataGeneratorService.cs | 60 ++++++++ .../Tests/Data/RequestsDataServiceTest.cs | 141 ++++++++++++++++-- .../Tests/Data/StatusesDataServiceTest.cs | 46 ------ .../Tests/Data/UsersDataServiceTest.cs | 129 ++++++++++++++-- ...figServiceTest.cs => ConfigServiceTest.cs} | 10 +- .../Tests/RateLimiter/LimiterServiceTest.cs | 2 +- .../RateLimiter/RequestHandlerServiceTest.cs | 2 +- RateLimiter/Data/Interfaces/IDataService.cs | 17 +-- RateLimiter/Data/Models/Data/BaseModel.cs | 2 +- RateLimiter/Data/Models/Data/Request.cs | 3 + RateLimiter/Data/Repositories/DbRepository.cs | 35 +++-- .../Data/Services/RequestsDataService.cs | 48 +++--- .../Data/Services/ResourcesDataService.cs | 62 +++++--- .../Data/Services/StatusesDataService.cs | 50 ------- RateLimiter/Data/Services/UsersDataService.cs | 62 +++++--- RateLimiter/Data/Statuses.cs | 19 +++ RateLimiter/Interfaces/ILimiterService.cs | 2 +- RateLimiter/Services/LimiterService.cs | 2 +- 21 files changed, 557 insertions(+), 227 deletions(-) create mode 100644 RateLimiter.Tests/Interfaces/IDataGeneratorService.cs create mode 100644 RateLimiter.Tests/Services/DataGeneratorService.cs delete mode 100644 RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs rename RateLimiter.Tests/Tests/RateLimiter/{LimiterConfigServiceTest.cs => ConfigServiceTest.cs} (81%) delete mode 100644 RateLimiter/Data/Services/StatusesDataService.cs create mode 100644 RateLimiter/Data/Statuses.cs diff --git a/RateLimiter.Tests/Interfaces/IConfigService.cs b/RateLimiter.Tests/Interfaces/IConfigService.cs index 246e43b6..81c7a546 100644 --- a/RateLimiter.Tests/Interfaces/IConfigService.cs +++ b/RateLimiter.Tests/Interfaces/IConfigService.cs @@ -7,10 +7,13 @@ namespace RateLimiter.Interfaces { + // The config service is intended to add data to the database + // for testing purposes. + public interface IConfigService { - public Task SeedStatuses(List statuses); - public Task SeedResources(List resources); - public Task SeedUsers(List users); + public Task Reset(); + public Task SeedResources(List resources); + public Task SeedUsers(List users); } } diff --git a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs new file mode 100644 index 00000000..b184e4db --- /dev/null +++ b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs @@ -0,0 +1,16 @@ +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Tests.Interfaces +{ + public interface IDataGeneratorService + { + public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool wasHandled); + public Resource GenerateResource(int id, string name, Status status); + public User GenerateUser(int id, string name, Guid token); + } +} diff --git a/RateLimiter.Tests/Services/ConfigService.cs b/RateLimiter.Tests/Services/ConfigService.cs index bd1f257f..1b36cdcc 100644 --- a/RateLimiter.Tests/Services/ConfigService.cs +++ b/RateLimiter.Tests/Services/ConfigService.cs @@ -1,4 +1,6 @@ -using RateLimiter.Data.Interfaces; +using Microsoft.EntityFrameworkCore; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; using RateLimiter.Interfaces; using RateLimiter.Models; using System; @@ -11,28 +13,63 @@ namespace RateLimiter.Services { public class ConfigService : IConfigService { - private readonly IDataService _resourceDataService; - private readonly IDataService _userDataService; - private readonly IDataService _statusesDataService; + private readonly RateLimiterDbContext _context; - public ConfigService(IDataService resourceDataService, IDataService userDataService, IDataService statusesDataService) + public ConfigService(RateLimiterDbContext context) { - _resourceDataService = resourceDataService; - _userDataService = userDataService; - _statusesDataService = statusesDataService; - } + _context = context; + + // Make sure statuses are in the db. Should be handled in a db initializer but ran out of time. + var statusesCount = _context.Statuses.Count(); - public async Task SeedStatuses(List statuses) + if (statusesCount == 0) + { + foreach (var status in CodeValues.Statuses) + { + _context.Statuses.Add(status); + } + _context.SaveChanges(); + } + } + public async Task Reset() + { + foreach (var request in _context.Requests) + { + _context.Requests.Remove(request); + } + foreach (var resource in _context.Resources) + { + _context.Resources.Remove(resource); + } + foreach (var user in _context.Users) + { + _context.Users.Remove(user); + } + await _context.SaveChangesAsync(); + } + public async Task SeedResources(List resources) { - throw new NotImplementedException(); + foreach (var resource in resources) + { + _context.Resources.Add(resource); + } + await _context.SaveChangesAsync(); } - public async Task SeedResources(List resources) + public async Task SeedUsers(List users) { - throw new NotImplementedException(); + foreach (var user in users) + { + _context.Users.Add(user); + } + await _context.SaveChangesAsync(); } - public async Task SeedUsers(List users) + public async Task SeedRequests(List requests) { - throw new NotImplementedException(); + foreach (var request in requests) + { + _context.Add(request); + }; + await _context.SaveChangesAsync(); } } } diff --git a/RateLimiter.Tests/Services/DataGeneratorService.cs b/RateLimiter.Tests/Services/DataGeneratorService.cs new file mode 100644 index 00000000..ecbed297 --- /dev/null +++ b/RateLimiter.Tests/Services/DataGeneratorService.cs @@ -0,0 +1,60 @@ +using RateLimiter.Models; +using RateLimiter.Tests.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Tests.Services +{ + internal class DataGeneratorService : IDataGeneratorService + { + public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool wasHandled) + { + var request = new Request + { + Id = id, + Identifier = identifier, + RequestDate = DateTime.Now, + Resource = resource, + User = user, + WasHandled = wasHandled, + CreatedBy = "DataGenerator", + CreatedDate = DateTime.Now + }; + + return request; + } + public Resource GenerateResource(int id, string name, Status status) + { + var resource = new Resource + { + Id = id, + Identifier = name, + Name = name, + Description = name, + Status = status, + CreatedBy = "DataGenerator", + CreatedDate = DateTime.Now + }; + + return resource; + } + public User GenerateUser(int id, string username, Guid token) + { + var user = new User + { + Id = id, + Identifier = username, + Name = username, + Token = token.ToString(), + Email = string.Format("{0}@phonyEmail.com"), + CreatedBy = "DataGenerator", + CreatedDate = DateTime.Now + }; + + return user; + } + } +} diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs index 630b9e99..39407318 100644 --- a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -4,10 +4,15 @@ using NUnit.Framework; using RateLimiter.Data; using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Filter; +using RateLimiter.Interfaces; using RateLimiter.Models; using RateLimiter.Services; +using RateLimiter.Tests.Interfaces; +using RateLimiter.Tests.Services; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace RateLimiter.Tests; @@ -24,14 +29,38 @@ public void SetUp() services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); services.AddTransient(typeof(DbRepository<>)); + services.AddScoped(); + services.AddScoped(); services.AddScoped, RequestsDataService>(); _serviceProvider = services.BuildServiceProvider(); + } + protected void DataSetup() + { + var dataGeneratorService = _serviceProvider.GetService(); + var configService = _serviceProvider.GetService(); + + var users = new List() + { + dataGeneratorService.GenerateUser("ResourceUser1", Guid.NewGuid()) + }; + configService.SeedUsers(users); + + var resources = new List() + { + dataGeneratorService.GenerateResource("Resource1", CodeValues.Statuses.Single(x => x.Identifier == "")) + }; + + configService.SeedResources(resources); + } + + + [Test] - public void GetTest() - { + public void GetAllTest() + { var requestsDataService = _serviceProvider.GetService>(); try @@ -44,13 +73,103 @@ public void GetTest() { Assert.That(true, Is.True); } - } -} + } + [Test] + public void FindTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requestSearchFilter = new BaseModel(); + + var requests = requestsDataService.FindAsync(requestSearchFilter); -//public Task> Get(); -//public Task> Get(BaseModel searchCriteria); -//public Task Get(int id); -//public Task Get(string identifier); -//public Task Add(T entity); -//public Task Update(int id, T entity); -//public Task Delete(int id); \ No newline at end of file + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public void GetByIdTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.SingleAsync(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public void GetByIdentifierTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.SingleAsync(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public void AddTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.AddAsync(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public void UpdateTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.UpdateAsync(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public void RemoveTest() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var requests = requestsDataService.RemoveAsync(); + + Assert.That(true, Is.False); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} diff --git a/RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs deleted file mode 100644 index 955c5b50..00000000 --- a/RateLimiter.Tests/Tests/Data/StatusesDataServiceTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using RateLimiter.Data; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; -using System; - -namespace RateLimiter.Tests; - -[TestFixture] -public class StatusesDataServiceTest -{ - ServiceProvider _serviceProvider; - - [SetUp] - public void SetUp() - { - var services = new ServiceCollection(); - - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); - services.AddTransient(typeof(DbRepository<>)); - services.AddScoped, StatusesDataService>(); - - _serviceProvider = services.BuildServiceProvider(); - - } - [Test] - public void GetTest() - { - var statusesDataService = _serviceProvider.GetService>(); - - try - { - var requests = statusesDataService.Get(); - - Assert.That(true, Is.False); - } - catch (NotImplementedException ex) - { - Assert.That(true, Is.True); - } - } -} \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs index 3f45d3a6..a7854997 100644 --- a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -4,9 +4,14 @@ using NUnit.Framework; using RateLimiter.Data; using RateLimiter.Data.Interfaces; +using RateLimiter.Interfaces; using RateLimiter.Models; using RateLimiter.Services; +using RateLimiter.Tests.Interfaces; +using RateLimiter.Tests.Services; using System; +using System.Collections.Generic; +using System.Linq; namespace RateLimiter.Tests; @@ -15,32 +20,136 @@ public class UsersDataServiceTest { ServiceProvider _serviceProvider; - [SetUp] - public void SetUp() + private readonly IDataService _usersDataService; + private readonly IDataGeneratorService _dataGeneratorService; + private readonly IConfigService _configService; + + public UsersDataServiceTest() { var services = new ServiceCollection(); services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); services.AddTransient(typeof(DbRepository<>)); services.AddScoped, UsersDataService>(); + services.AddScoped(); + services.AddScoped(); _serviceProvider = services.BuildServiceProvider(); + _usersDataService = _serviceProvider.GetService>(); + _dataGeneratorService = _serviceProvider.GetService(); + _configService = _serviceProvider.GetService(); + + } + + [SetUp] + public void SetUp() + { + _configService.Reset(); } + + [Test] - public void GetTest() - { - var requestDataService = _serviceProvider.GetService>(); + public async void GetAllTest() + { + var seedUsers = new List() + { + _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()), + _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()), + _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()) + }; + + await _configService.SeedUsers(seedUsers); try { - var requests = requestDataService.Get(); + var users = await _usersDataService.GetAllAsync(); - Assert.That(true, Is.False); + Assert.AreEqual(users.Count, 3); } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); + Assert.That(false, Is.True); } - } + } + //[Test] + //public void GetByIdTest() + //{ + // var usersDataService = _serviceProvider.GetService>(); + + // try + // { + // var users = usersDataService.SingleAsync(); + + // Assert.That(true, Is.False); + // } + // catch (NotImplementedException ex) + // { + // Assert.That(true, Is.True); + // } + //} + //[Test] + //public void GetByIdentifierTest() + //{ + // var usersDataService = _serviceProvider.GetService>(); + + // try + // { + // var users = usersDataService.SingleAsync(); + + // Assert.That(true, Is.False); + // } + // catch (NotImplementedException ex) + // { + // Assert.That(true, Is.True); + // } + //} + //[Test] + //public void AddTest() + //{ + // var usersDataService = _serviceProvider.GetService>(); + + // try + // { + // var users = usersDataService.AddAsync(); + + // Assert.That(true, Is.False); + // } + // catch (NotImplementedException ex) + // { + // Assert.That(true, Is.True); + // } + //} + //[Test] + //public void UpdateTest() + //{ + // var usersDataService = _serviceProvider.GetService>(); + + // try + // { + // var users = usersDataService.UpdateAsync(); + + // Assert.That(true, Is.False); + // } + // catch (NotImplementedException ex) + // { + // Assert.That(true, Is.True); + // } + //} + //[Test] + //public async void RemoveTest() + //{ + // var usersDataService = _serviceProvider.GetService>(); + + // try + // { + // var users = await usersDataService.RemoveAsync(); + + // Assert.That(true, Is.False); + // } + // catch (NotImplementedException ex) + // { + // Assert.That(true, Is.True); + // } + //} } \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs similarity index 81% rename from RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs rename to RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs index a53670b2..de6e4ddc 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterConfigServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs @@ -13,7 +13,7 @@ namespace RateLimiter.Tests; [TestFixture] -public class RequestsAuditServiceTest +public class ConfigServiceTest { ServiceProvider _serviceProvider; @@ -48,11 +48,3 @@ public void GetTest() } } } - -//public Task> Get(); -//public Task> Get(BaseModel searchCriteria); -//public Task Get(int id); -//public Task Get(string identifier); -//public Task Add(T entity); -//public Task Update(int id, T entity); -//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index a53670b2..f22eb71b 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -13,7 +13,7 @@ namespace RateLimiter.Tests; [TestFixture] -public class RequestsAuditServiceTest +public class LimiterServiceTest { ServiceProvider _serviceProvider; diff --git a/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs index a53670b2..f6369577 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs @@ -13,7 +13,7 @@ namespace RateLimiter.Tests; [TestFixture] -public class RequestsAuditServiceTest +public class RequestsHandlerServiceTest { ServiceProvider _serviceProvider; diff --git a/RateLimiter/Data/Interfaces/IDataService.cs b/RateLimiter/Data/Interfaces/IDataService.cs index db97db70..a45d57e0 100644 --- a/RateLimiter/Data/Interfaces/IDataService.cs +++ b/RateLimiter/Data/Interfaces/IDataService.cs @@ -9,16 +9,11 @@ namespace RateLimiter.Data.Interfaces { public interface IDataService where T : BaseModel { - public Task> Get(); - - public Task> Get(BaseModel searchCriteria); - - public Task Get(int id); - - public Task Get(string identifier); - - public Task Add(T entity); - public Task Update(int id, T entity); - public Task Delete(int id); + public Task> GetAllAsync(); + public Task SingleAsync(int id); + public Task SingleAsync(string identifier); + public Task AddAsync(T entity); + public Task UpdateAsync(int id, T entity); + public Task RemoveAsync(int id); } } diff --git a/RateLimiter/Data/Models/Data/BaseModel.cs b/RateLimiter/Data/Models/Data/BaseModel.cs index 8e4d60dd..f91e8774 100644 --- a/RateLimiter/Data/Models/Data/BaseModel.cs +++ b/RateLimiter/Data/Models/Data/BaseModel.cs @@ -20,7 +20,7 @@ public class BaseModel [Required] public string CreatedBy { get; set; } - public DateTime? Updated { get; set; } + public DateTime? UpdatedDate { get; set; } public string? UpdatedBy { get; set; } } } diff --git a/RateLimiter/Data/Models/Data/Request.cs b/RateLimiter/Data/Models/Data/Request.cs index 3d15f2b0..ec57feec 100644 --- a/RateLimiter/Data/Models/Data/Request.cs +++ b/RateLimiter/Data/Models/Data/Request.cs @@ -12,5 +12,8 @@ public class Request : BaseModel public int ResourceId { get; set; } public DateTime RequestDate { get; set; } public bool WasHandled { get; set; } + + public User User { get; set; } + public Resource Resource { get; set; } } } diff --git a/RateLimiter/Data/Repositories/DbRepository.cs b/RateLimiter/Data/Repositories/DbRepository.cs index 352b8582..558d99ba 100644 --- a/RateLimiter/Data/Repositories/DbRepository.cs +++ b/RateLimiter/Data/Repositories/DbRepository.cs @@ -33,7 +33,6 @@ public async Task> GetAllAsync(string[] includes) return await dbSet.ToListAsync(); } - public async Task SingleAsync(int id, string[] includes) { @@ -94,28 +93,44 @@ public async Task SingleOrDefaultAsync(int id) return entity; } - public async Task Add(Entity entity) + public async Task AddAsync(Entity entity) { + // Need to add logic that insures unique Identifer + + var result = _dbSet.Add(entity); + await _context.SaveChangesAsync(); return true; } - public async Task Update(Entity entity) + public async Task UpdateAsync(Entity entity) { - var result = _dbSet.Update(entity); + var existing = await _dbSet.SingleOrDefaultAsync(x => x.Id == entity.Id); + + if (existing == null) + { + throw new ArgumentException("Invalid id provided."); + } + + var result = _dbSet.Update(entity); // do i need to copy to existing? + await _context.SaveChangesAsync(); return true; } - public async Task Remove(Entity entity) + public async Task RemoveAsync(int id) { - throw new NotImplementedException("Audit data should not be deleted."); - } + var entity = await _dbSet.SingleOrDefaultAsync(x => x.Id == id); - public async Task SaveChangesAsync() - { - await _context.SaveChangesAsync(); + if ( entity == null) + { + throw new ArgumentException("Invalid id provided."); + } + _context.Remove(entity); + _context.SaveChanges(); + + return true; } } diff --git a/RateLimiter/Data/Services/RequestsDataService.cs b/RateLimiter/Data/Services/RequestsDataService.cs index 20d62cdd..0b605cda 100644 --- a/RateLimiter/Data/Services/RequestsDataService.cs +++ b/RateLimiter/Data/Services/RequestsDataService.cs @@ -11,40 +11,50 @@ namespace RateLimiter.Services { public class RequestsDataService : IDataService { - private readonly DbRepository _requestRepository; + private readonly DbRepository _requestsRepository; - public RequestsDataService(DbRepository requestRepository) + public RequestsDataService(DbRepository requestsRepository) { - _requestRepository = requestRepository; + _requestsRepository = requestsRepository; } - public async Task> Get() + public async Task> GetAllAsync() { - throw new NotImplementedException(); - } - public async Task> Get(BaseModel searchCriteria) - { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var requests = await _requestsRepository.GetAllAsync(includes); + + return requests; } - public async Task Get(int id) + public async Task SingleAsync(int id) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var request = await _requestsRepository.SingleAsync(id, includes); + + return request; } - public async Task Get(string identifier) + public async Task SingleAsync(string identifier) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var request = await _requestsRepository.SingleAsync(identifier, includes); + + return request; } - public async Task Add(Request request) + public async Task AddAsync(Request request) { - throw new NotImplementedException(); + var newRequest = await _requestsRepository.AddAsync(request); + + return true; } - public async Task Update(int id, Request request) + public async Task UpdateAsync(int id, Request request) { - throw new NotImplementedException(); + throw new NotImplementedException("Cannot update a request."); } - public async Task Delete(int id) + public async Task RemoveAsync(int id) { - throw new NotImplementedException(); + throw new NotImplementedException("Cannot remove a request."); } } } diff --git a/RateLimiter/Data/Services/ResourcesDataService.cs b/RateLimiter/Data/Services/ResourcesDataService.cs index 1a75cfeb..8f140fbf 100644 --- a/RateLimiter/Data/Services/ResourcesDataService.cs +++ b/RateLimiter/Data/Services/ResourcesDataService.cs @@ -11,40 +11,64 @@ namespace RateLimiter.Services { public class ResourcesDataService : IDataService { - private readonly DbRepository _resourceRepository; + private readonly DbRepository _resourcesRepository; - public ResourcesDataService(DbRepository resourceRepository) + public ResourcesDataService(DbRepository resourcesRepository) { - _resourceRepository = resourceRepository; + _resourcesRepository = resourcesRepository; } - public async Task> Get() + public async Task> GetAllAsync() { - throw new NotImplementedException(); - } - public async Task> Get(BaseModel searchCriteria) - { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var resources = await _resourcesRepository.GetAllAsync(includes); + + return resources; } - public async Task Get(int id) + public async Task SingleAsync(int id) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var resource = await _resourcesRepository.SingleAsync(id, includes); + + return resource; } - public async Task Get(string identifier) + public async Task SingleAsync(string identifier) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var resource = await _resourcesRepository.SingleAsync(identifier, includes); + + return resource; } - public async Task Add(Resource resource) + public async Task AddAsync(Resource resource) { - throw new NotImplementedException(); + var newResource = await _resourcesRepository.AddAsync(resource); + + return true; } - public async Task Update(int id, Resource resource) + public async Task UpdateAsync(int id, Resource resource) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var existingResource = await _resourcesRepository.SingleAsync(id, includes); + + existingResource.Name = resource.Name; + existingResource.Description = resource.Description; + existingResource.StatusId = resource.StatusId; + existingResource.UpdatedBy = resource.UpdatedBy; + existingResource.UpdatedDate = DateTime.Now; + + await _resourcesRepository.UpdateAsync(existingResource); + + return true; } - public async Task Delete(int id) + public async Task RemoveAsync(int id) { - throw new NotImplementedException(); + var newResource = await _resourcesRepository.RemoveAsync(id); + + return true; } } } diff --git a/RateLimiter/Data/Services/StatusesDataService.cs b/RateLimiter/Data/Services/StatusesDataService.cs deleted file mode 100644 index abe1f8bd..00000000 --- a/RateLimiter/Data/Services/StatusesDataService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using M42.Data.Repositories; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Services -{ - public class StatusesDataService : IDataService - { - private readonly DbRepository _statusesRepository; - - public StatusesDataService(DbRepository statusesRepository) - { - _statusesRepository = statusesRepository; - } - - public async Task> Get() - { - throw new NotImplementedException(); - } - public async Task> Get(BaseModel searchCriteria) - { - throw new NotImplementedException(); - } - public async Task Get(int id) - { - throw new NotImplementedException(); - } - public async Task Get(string identifier) - { - throw new NotImplementedException(); - } - public async Task Add(Status user) - { - throw new NotImplementedException(); - } - public async Task Update(int id, Status user) - { - throw new NotImplementedException(); - } - public async Task Delete(int id) - { - throw new NotImplementedException(); - } - } -} diff --git a/RateLimiter/Data/Services/UsersDataService.cs b/RateLimiter/Data/Services/UsersDataService.cs index 057f8e7b..61a45030 100644 --- a/RateLimiter/Data/Services/UsersDataService.cs +++ b/RateLimiter/Data/Services/UsersDataService.cs @@ -11,40 +11,64 @@ namespace RateLimiter.Services { public class UsersDataService : IDataService { - private readonly DbRepository _userRepository; + private readonly DbRepository _usersRepository; - public UsersDataService(DbRepository userRepository) + public UsersDataService(DbRepository usersRepository) { - _userRepository = userRepository; + _usersRepository = usersRepository; } - public async Task> Get() + public async Task> GetAllAsync() { - throw new NotImplementedException(); - } - public async Task> Get(BaseModel searchCriteria) - { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var users = await _usersRepository.GetAllAsync(includes); + + return users; } - public async Task Get(int id) + public async Task SingleAsync(int id) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var user = await _usersRepository.SingleAsync(id, includes); + + return user; } - public async Task Get(string identifier) + public async Task SingleAsync(string identifier) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var user = await _usersRepository.SingleAsync(identifier, includes); + + return user; } - public async Task Add(User user) + public async Task AddAsync(User user) { - throw new NotImplementedException(); + var newUser = await _usersRepository.AddAsync(user); + + return true; } - public async Task Update(int id, User user) + public async Task UpdateAsync(int id, User user) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var existingUser = await _usersRepository.SingleAsync(id, includes); + + existingUser.Name = user.Name; + existingUser.Email = user.Email; + existingUser.Token = user.Token; + existingUser.UpdatedBy = user.UpdatedBy; + existingUser.UpdatedDate = DateTime.Now; + + await _usersRepository.UpdateAsync(existingUser); + + return true; } - public async Task Delete(int id) + public async Task RemoveAsync(int id) { - throw new NotImplementedException(); + var newUser = await _usersRepository.RemoveAsync(id); + + return true; } } } diff --git a/RateLimiter/Data/Statuses.cs b/RateLimiter/Data/Statuses.cs new file mode 100644 index 00000000..b10a7e66 --- /dev/null +++ b/RateLimiter/Data/Statuses.cs @@ -0,0 +1,19 @@ +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Data +{ + public static class CodeValues + { + public static List Statuses = new List + { + new Status { Id = 1, Name = "Normal", Identifier = "Normal", CreatedBy = "SeedUser", CreatedDate = DateTime.Now }, + new Status { Id = 2, Name = "Maintenance", Identifier = "Maintenance", CreatedBy = "SeedUser", CreatedDate = DateTime.Now }, + new Status { Id = 3, Name = "Offline", Identifier = "Offline", CreatedBy = "SeedUser", CreatedDate = DateTime.Now } + }; + } +} diff --git a/RateLimiter/Interfaces/ILimiterService.cs b/RateLimiter/Interfaces/ILimiterService.cs index 35ae9c84..06167fa5 100644 --- a/RateLimiter/Interfaces/ILimiterService.cs +++ b/RateLimiter/Interfaces/ILimiterService.cs @@ -8,6 +8,6 @@ namespace RateLimiter.Interfaces { public interface ILimiterService { - public Task LimitAccess(int ResourceId, int UserId); + public Task AllowAccess(int ResourceId, int UserId); } } diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs index 5e104bcc..b24df2ba 100644 --- a/RateLimiter/Services/LimiterService.cs +++ b/RateLimiter/Services/LimiterService.cs @@ -19,7 +19,7 @@ public LimiterService(IAuditService requestAuditService _requestAuditService = requestAuditService; } - public async Task LimitAccess(int ResourceId, int UserId) + public async Task AllowAccess(int ResourceId, int UserId) { throw new NotImplementedException(); } From 3e31346b68f77c62f5f55bc1f8d8a500f955677c Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Sun, 17 Nov 2024 03:01:17 -0700 Subject: [PATCH 3/9] Working code. Data access tests all succeed. --- .../Interfaces/IConfigService.cs | 1 + RateLimiter.Tests/RateLimiter.Tests.csproj | 5 + RateLimiter.Tests/RateLimiterTest.cs | 13 - .../Services/DataGeneratorService.cs | 2 +- .../Tests/Data/RequestsDataServiceTest.cs | 219 +++++++++++---- .../Tests/Data/ResourcesDataServiceTest.cs | 205 +++++++++++++- .../Tests/Data/UsersDataServiceTest.cs | 255 ++++++++++++------ .../Tests/RateLimiter/LimiterServiceTest.cs | 10 +- .../Data/DbContexts/RateLimiterDbContext.cs | 4 + RateLimiter/Data/Interfaces/IDataService.cs | 3 + RateLimiter/Data/Repositories/DbRepository.cs | 27 +- .../Data/Services/RequestsDataService.cs | 16 ++ .../Data/Services/ResourcesDataService.cs | 12 + RateLimiter/Data/Services/UsersDataService.cs | 12 + 14 files changed, 603 insertions(+), 181 deletions(-) delete mode 100644 RateLimiter.Tests/RateLimiterTest.cs diff --git a/RateLimiter.Tests/Interfaces/IConfigService.cs b/RateLimiter.Tests/Interfaces/IConfigService.cs index 81c7a546..ae294e5a 100644 --- a/RateLimiter.Tests/Interfaces/IConfigService.cs +++ b/RateLimiter.Tests/Interfaces/IConfigService.cs @@ -15,5 +15,6 @@ public interface IConfigService public Task Reset(); public Task SeedResources(List resources); public Task SeedUsers(List users); + public Task SeedRequests(List requests); } } diff --git a/RateLimiter.Tests/RateLimiter.Tests.csproj b/RateLimiter.Tests/RateLimiter.Tests.csproj index a90f1cb8..225b577e 100644 --- a/RateLimiter.Tests/RateLimiter.Tests.csproj +++ b/RateLimiter.Tests/RateLimiter.Tests.csproj @@ -4,6 +4,11 @@ latest enable + + + + + diff --git a/RateLimiter.Tests/RateLimiterTest.cs b/RateLimiter.Tests/RateLimiterTest.cs deleted file mode 100644 index 172d44a7..00000000 --- a/RateLimiter.Tests/RateLimiterTest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NUnit.Framework; - -namespace RateLimiter.Tests; - -[TestFixture] -public class RateLimiterTest -{ - [Test] - public void Example() - { - Assert.That(true, Is.True); - } -} \ No newline at end of file diff --git a/RateLimiter.Tests/Services/DataGeneratorService.cs b/RateLimiter.Tests/Services/DataGeneratorService.cs index ecbed297..a08b2cc7 100644 --- a/RateLimiter.Tests/Services/DataGeneratorService.cs +++ b/RateLimiter.Tests/Services/DataGeneratorService.cs @@ -49,7 +49,7 @@ public User GenerateUser(int id, string username, Guid token) Identifier = username, Name = username, Token = token.ToString(), - Email = string.Format("{0}@phonyEmail.com"), + Email = string.Format("{0}@phonyEmail.com", username), CreatedBy = "DataGenerator", CreatedDate = DateTime.Now }; diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs index 39407318..567c0aec 100644 --- a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -1,10 +1,10 @@ using M42.Data.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources; using NUnit.Framework; using RateLimiter.Data; using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Filter; using RateLimiter.Interfaces; using RateLimiter.Models; using RateLimiter.Services; @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Intrinsics.X86; using System.Threading.Tasks; namespace RateLimiter.Tests; @@ -22,134 +23,219 @@ public class RequestsDataServiceTest { ServiceProvider _serviceProvider; - [SetUp] - public void SetUp() + private readonly IDataService _requestsDataService; + private readonly IDataGeneratorService _dataGeneratorService; + private readonly IConfigService _configService; + + private Resource resource1; + private Resource resource2; + private User user1; + private User user2; + + public RequestsDataServiceTest() { var services = new ServiceCollection(); - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RequestsDataServiceTest")); services.AddTransient(typeof(DbRepository<>)); - services.AddScoped(); - services.AddScoped(); services.AddScoped, RequestsDataService>(); + services.AddScoped(); + services.AddScoped(); _serviceProvider = services.BuildServiceProvider(); - + _requestsDataService = _serviceProvider.GetService>(); + _dataGeneratorService = _serviceProvider.GetService(); + _configService = _serviceProvider.GetService(); + } - protected void DataSetup() + + [SetUp] + public async Task SetUp() { - var dataGeneratorService = _serviceProvider.GetService(); - var configService = _serviceProvider.GetService(); + await _configService.Reset(); - var users = new List() + resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); + resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + + var seedResources = new List() { - dataGeneratorService.GenerateUser("ResourceUser1", Guid.NewGuid()) + resource1, + resource2 }; - configService.SeedUsers(users); - var resources = new List() + await _configService.SeedResources(seedResources); + + user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); + user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + var seedUsers = new List() { - dataGeneratorService.GenerateResource("Resource1", CodeValues.Statuses.Single(x => x.Identifier == "")) + user1, + user2 }; - configService.SeedResources(resources); + await _configService.SeedUsers(seedUsers); } - [Test] - public void GetAllTest() + public async Task GetAllTest() { - var requestsDataService = _serviceProvider.GetService>(); + + var request1 = _dataGeneratorService.GenerateRequest(1, resource1, user1, "Request1", true); + var request2 = _dataGeneratorService.GenerateRequest(2, resource1, user1, "Request2", false); + var request3 = _dataGeneratorService.GenerateRequest(3, resource2, user1, "Request3", true); + + var seedRequests = new List() + { + request1, + request2, + request3 + }; + + await _configService.SeedRequests(seedRequests); try { - var requests = requestsDataService.Get(); + var requests = await _requestsDataService.GetAllAsync(); - Assert.That(true, Is.False); + Assert.AreEqual(requests.Count, 3); } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); + Assert.That(false, Is.True); } } [Test] - public void FindTest() + public async Task FindTest() { - var requestsDataService = _serviceProvider.GetService>(); + var request1 = _dataGeneratorService.GenerateRequest(1, resource1, user1, "Request1", true); + var request2 = _dataGeneratorService.GenerateRequest(2, resource1, user1, "Request2", false); + var request3 = _dataGeneratorService.GenerateRequest(3, resource2, user1, "Request3", true); + + var seedRequests = new List() + { + request1, + request2, + request3 + }; + + await _configService.SeedRequests(seedRequests); try { - var requestSearchFilter = new BaseModel(); + var searchCriteria = new BaseModel { CreatedBy = "FredSmith" }; + var requests = await _requestsDataService.FindAsync(searchCriteria); + + Assert.AreEqual(requests.Count, 0); // should never reach this assertion - var requests = requestsDataService.FindAsync(requestSearchFilter); + searchCriteria = new BaseModel { CreatedBy = "DataGenerator" }; + requests = await _requestsDataService.FindAsync(searchCriteria); - Assert.That(true, Is.False); + Assert.AreEqual(requests.Count, 3); // should never reach this assertion } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); + Assert.That(true, Is.True); // NotImplementedException is the expected result. } } [Test] - public void GetByIdTest() + public async Task GetByIdTest() { - var requestsDataService = _serviceProvider.GetService>(); + var request1 = _dataGeneratorService.GenerateRequest(1, resource1, user1, "Request1", true); + var request2 = _dataGeneratorService.GenerateRequest(2, resource1, user1, "Request2", false); + var request3 = _dataGeneratorService.GenerateRequest(3, resource2, user1, "Request3", true); + + var seedRequests = new List() + { + request1, + request2, + request3 + }; + + await _configService.SeedRequests(seedRequests); try { - var requests = requestsDataService.SingleAsync(); + var retrievedRequest = await _requestsDataService.SingleAsync(request2.Id); - Assert.That(true, Is.False); + Assert.AreEqual(retrievedRequest.Identifier, request2.Identifier); } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); + Assert.That(false, Is.True); } } [Test] - public void GetByIdentifierTest() + public async Task GetByIdentifierTest() { - var requestsDataService = _serviceProvider.GetService>(); + var request1 = _dataGeneratorService.GenerateRequest(1, resource1, user1, "Request1", true); + var request2 = _dataGeneratorService.GenerateRequest(2, resource1, user1, "Request2", false); + var request3 = _dataGeneratorService.GenerateRequest(3, resource2, user1, "Request3", true); + + var seedRequests = new List() + { + request1, + request2, + request3 + }; + + await _configService.SeedRequests(seedRequests); try { - var requests = requestsDataService.SingleAsync(); + var retrievedRequest = await _requestsDataService.SingleAsync(request2.Identifier); - Assert.That(true, Is.False); + Assert.AreEqual(retrievedRequest.Id, request2.Id); } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); + Assert.That(false, Is.True); } } [Test] - public void AddTest() + public async Task AddTest() { - var requestsDataService = _serviceProvider.GetService>(); + var requestToAdd = _dataGeneratorService.GenerateRequest(1, resource1, user1, "RequestToAdd", true); try { - var requests = requestsDataService.AddAsync(); + var request = await _requestsDataService.AddAsync(requestToAdd); - Assert.That(true, Is.False); + var retrievedRequest = await _requestsDataService.SingleAsync(requestToAdd.Id); + + Assert.AreEqual(requestToAdd.Identifier, retrievedRequest.Identifier); } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); + Assert.That(false, Is.True); } } [Test] - public void UpdateTest() + public async Task UpdateTest() { - var requestsDataService = _serviceProvider.GetService>(); + var request1 = _dataGeneratorService.GenerateRequest(1, resource1, user1, "Request1", true); + var request2 = _dataGeneratorService.GenerateRequest(2, resource1, user1, "Request2", false); + var request3 = _dataGeneratorService.GenerateRequest(3, resource2, user1, "Request3", true); + + var seedRequests = new List() + { + request1, + request2, + request3 + }; + + await _configService.SeedRequests(seedRequests); try { - var requests = requestsDataService.UpdateAsync(); + request2.Identifier = "Fred"; + + var result = _requestsDataService.UpdateAsync(request2.Id, request2); + + var updatedRequest = await _requestsDataService.SingleAsync(request2.Id); - Assert.That(true, Is.False); + Assert.AreEqual(updatedRequest.Identifier, "Fred"); } catch (NotImplementedException ex) { @@ -157,19 +243,38 @@ public void UpdateTest() } } [Test] - public void RemoveTest() + public async Task RemoveTest() { - var requestsDataService = _serviceProvider.GetService>(); + var request1 = _dataGeneratorService.GenerateRequest(1, resource1, user1, "Request1", true); + var request2 = _dataGeneratorService.GenerateRequest(2, resource1, user1, "Request2", false); + var request3 = _dataGeneratorService.GenerateRequest(3, resource2, user1, "Request3", true); + + var seedRequests = new List() + { + request1, + request2, + request3 + }; + + await _configService.SeedRequests(seedRequests); try { - var requests = requestsDataService.RemoveAsync(); + var retrievedRequests = await _requestsDataService.GetAllAsync(); + Assert.AreEqual(retrievedRequests.Count, 3); + + var requests = await _requestsDataService.RemoveAsync(request2.Id); + + retrievedRequests = await _requestsDataService.GetAllAsync(); + Assert.AreEqual(retrievedRequests.Count, 2); + + var retrievedRequest = await _requestsDataService.SingleOrDefaultAsync(request2.Id); - Assert.That(true, Is.False); + Assert.IsNull(retrievedRequest); } catch (NotImplementedException ex) { Assert.That(true, Is.True); } } -} +} \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs index c03642a9..15516f8c 100644 --- a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs @@ -4,9 +4,15 @@ using NUnit.Framework; using RateLimiter.Data; using RateLimiter.Data.Interfaces; +using RateLimiter.Interfaces; using RateLimiter.Models; using RateLimiter.Services; +using RateLimiter.Tests.Interfaces; +using RateLimiter.Tests.Services; using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace RateLimiter.Tests; @@ -15,32 +21,213 @@ public class ResourcesDataServiceTest { ServiceProvider _serviceProvider; - [SetUp] - public void SetUp() + private readonly IDataService _resourcesDataService; + private readonly IDataGeneratorService _dataGeneratorService; + private readonly IConfigService _configService; + + public ResourcesDataServiceTest() { var services = new ServiceCollection(); - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "ResourcesTest")); services.AddTransient(typeof(DbRepository<>)); services.AddScoped, ResourcesDataService>(); + services.AddScoped(); + services.AddScoped(); _serviceProvider = services.BuildServiceProvider(); + _resourcesDataService = _serviceProvider.GetService>(); + _dataGeneratorService = _serviceProvider.GetService(); + _configService = _serviceProvider.GetService(); + + } + + [SetUp] + public async Task SetUp() + { + await _configService.Reset(); + } + + + [Test] + public async Task GetAllTest() + { + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + + var seedResources = new List() + { + resource1, + resource2, + resource3 + }; + + await _configService.SeedResources(seedResources); + + try + { + var resources = await _resourcesDataService.GetAllAsync(); + + Assert.AreEqual(resources.Count, 3); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task FindTest() + { + try + { + var searchCriteria = new BaseModel(); + var resources = await _resourcesDataService.FindAsync(searchCriteria); + + Assert.AreEqual(resources.Count, -1); // should never reach this assertion + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); // NotImplementedException is the expected result. + } + } + [Test] + public async Task GetByIdTest() + { + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + + var seedResources = new List() + { + resource1, + resource2, + resource3 + }; + + await _configService.SeedResources(seedResources); + + try + { + var retrievedResource = await _resourcesDataService.SingleAsync(resource2.Id); + + Assert.AreEqual(retrievedResource.Name, resource2.Name); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task GetByIdentifierTest() + { + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + + var seedResources = new List() + { + resource1, + resource2, + resource3 + }; + + await _configService.SeedResources(seedResources); + + try + { + var retrievedResource = await _resourcesDataService.SingleAsync(resource2.Identifier); + + Assert.AreEqual(retrievedResource.Id, resource2.Id); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task AddTest() + { + var resourceToAdd = _dataGeneratorService.GenerateResource(1, "ResourceToAdd", CodeValues.Statuses.Single(x => x.Name == "Normal")); + + try + { + var resource = await _resourcesDataService.AddAsync(resourceToAdd); + + var retrievedResource = await _resourcesDataService.SingleAsync(resourceToAdd.Id); + + Assert.AreEqual(resourceToAdd.Name, retrievedResource.Name); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task UpdateTest() + { + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + + var seedResources = new List() + { + resource1, + resource2, + resource3 + }; + + await _configService.SeedResources(seedResources); + + try + { + resource2.Name = "Fred"; + + var result = _resourcesDataService.UpdateAsync(resource2.Id, resource2); + + var updatedResource = await _resourcesDataService.SingleAsync(resource2.Id); + + Assert.AreEqual(updatedResource.Name, "Fred"); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } } [Test] - public void GetTest() - { - var resourcesDataService = _serviceProvider.GetService>(); + public async Task RemoveTest() + { + + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var seedResources = new List() + { + resource1, + resource2, + resource3 + }; + + await _configService.SeedResources(seedResources); try { - var requests = resourcesDataService.Get(); + var retrievedResources = await _resourcesDataService.GetAllAsync(); + Assert.AreEqual(retrievedResources.Count, 3); + + var resources = await _resourcesDataService.RemoveAsync(resource2.Id); + + retrievedResources = await _resourcesDataService.GetAllAsync(); + Assert.AreEqual(retrievedResources.Count, 2); - Assert.That(true, Is.False); + var retrievedResource = await _resourcesDataService.SingleOrDefaultAsync(resource2.Id); + + Assert.IsNull(retrievedResource); } catch (NotImplementedException ex) { Assert.That(true, Is.True); } - } + } } \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs index a7854997..ead6490d 100644 --- a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace RateLimiter.Tests; @@ -28,7 +29,7 @@ public UsersDataServiceTest() { var services = new ServiceCollection(); - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "UsersDataServiceTest")); services.AddTransient(typeof(DbRepository<>)); services.AddScoped, UsersDataService>(); services.AddScoped(); @@ -43,23 +44,27 @@ public UsersDataServiceTest() } [SetUp] - public void SetUp() + public async Task SetUp() { - _configService.Reset(); + await _configService.Reset(); } [Test] - public async void GetAllTest() + public async Task GetAllTest() { + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + var seedUsers = new List() { - _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()), - _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()), - _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()) + user1, + user2, + user3 }; - await _configService.SeedUsers(seedUsers); + await _configService.SeedUsers(seedUsers); try { @@ -72,84 +77,158 @@ public async void GetAllTest() Assert.That(false, Is.True); } } - //[Test] - //public void GetByIdTest() - //{ - // var usersDataService = _serviceProvider.GetService>(); - - // try - // { - // var users = usersDataService.SingleAsync(); - - // Assert.That(true, Is.False); - // } - // catch (NotImplementedException ex) - // { - // Assert.That(true, Is.True); - // } - //} - //[Test] - //public void GetByIdentifierTest() - //{ - // var usersDataService = _serviceProvider.GetService>(); - - // try - // { - // var users = usersDataService.SingleAsync(); - - // Assert.That(true, Is.False); - // } - // catch (NotImplementedException ex) - // { - // Assert.That(true, Is.True); - // } - //} - //[Test] - //public void AddTest() - //{ - // var usersDataService = _serviceProvider.GetService>(); - - // try - // { - // var users = usersDataService.AddAsync(); - - // Assert.That(true, Is.False); - // } - // catch (NotImplementedException ex) - // { - // Assert.That(true, Is.True); - // } - //} - //[Test] - //public void UpdateTest() - //{ - // var usersDataService = _serviceProvider.GetService>(); - - // try - // { - // var users = usersDataService.UpdateAsync(); - - // Assert.That(true, Is.False); - // } - // catch (NotImplementedException ex) - // { - // Assert.That(true, Is.True); - // } - //} - //[Test] - //public async void RemoveTest() - //{ - // var usersDataService = _serviceProvider.GetService>(); - - // try - // { - // var users = await usersDataService.RemoveAsync(); - - // Assert.That(true, Is.False); - // } - // catch (NotImplementedException ex) - // { - // Assert.That(true, Is.True); - // } - //} + [Test] + public async Task FindTest() + { + try + { + var searchCriteria = new BaseModel(); + var users = await _usersDataService.FindAsync(searchCriteria); + + Assert.AreEqual(users.Count, -1); // should never reach this assertion + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); // NotImplementedException is the expected result. + } + } + [Test] + public async Task GetByIdTest() + { + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + + var seedUsers = new List() + { + user1, + user2, + user3 + }; + + await _configService.SeedUsers(seedUsers); + + try + { + var retrievedUser = await _usersDataService.SingleAsync(user2.Id); + + Assert.AreEqual(retrievedUser.Name, user2.Name); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task GetByIdentifierTest() + { + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + + var seedUsers = new List() + { + user1, + user2, + user3 + }; + + await _configService.SeedUsers(seedUsers); + + try + { + var retrievedUser = await _usersDataService.SingleAsync(user2.Identifier); + + Assert.AreEqual(retrievedUser.Id, user2.Id); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task AddTest() + { + var userToAdd = _dataGeneratorService.GenerateUser(100, "UserToAdd", Guid.NewGuid()); + + try + { + var user = await _usersDataService.AddAsync(userToAdd); + + var retrievedUser = await _usersDataService.SingleAsync(userToAdd.Id); + + Assert.AreEqual(userToAdd.Name, retrievedUser.Name); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task UpdateTest() + { + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + + var seedUsers = new List() + { + user1, + user2, + user3 + }; + + await _configService.SeedUsers(seedUsers); + + try + { + user2.Name = "Fred"; + + var result = _usersDataService.UpdateAsync(user2.Id, user2); + + var updatedUser = await _usersDataService.SingleAsync(user2.Id); + + Assert.AreEqual(updatedUser.Name, "Fred"); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public async Task RemoveTest() + { + + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + + var seedUsers = new List() + { + user1, + user2, + user3 + }; + + await _configService.SeedUsers(seedUsers); + + try + { + var retrievedUsers = await _usersDataService.GetAllAsync(); + Assert.AreEqual(retrievedUsers.Count, 3); + + var users = await _usersDataService.RemoveAsync(user2.Id); + + retrievedUsers = await _usersDataService.GetAllAsync(); + Assert.AreEqual(retrievedUsers.Count, 2); + + var retrievedUser = await _usersDataService.SingleOrDefaultAsync(user2.Id); + + Assert.IsNull(retrievedUser); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } } \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index f22eb71b..05b04bdc 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -22,7 +22,7 @@ public void SetUp() { var services = new ServiceCollection(); - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "LimiterServiceTest")); services.AddTransient(typeof(DbRepository<>)); services.AddScoped, RequestsDataService>(); services.AddScoped, ResourcesDataService>(); @@ -48,11 +48,3 @@ public void GetTest() } } } - -//public Task> Get(); -//public Task> Get(BaseModel searchCriteria); -//public Task Get(int id); -//public Task Get(string identifier); -//public Task Add(T entity); -//public Task Update(int id, T entity); -//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs index 7fc01602..b4d7e044 100644 --- a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs +++ b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs @@ -10,6 +10,10 @@ namespace RateLimiter.Data { public class RateLimiterDbContext : DbContext { + public RateLimiterDbContext(DbContextOptions options) : base(options) + { + Database.EnsureCreated(); + } public DbSet Users { get; set; } public DbSet Resources { get; set; } public DbSet Requests { get; set; } diff --git a/RateLimiter/Data/Interfaces/IDataService.cs b/RateLimiter/Data/Interfaces/IDataService.cs index a45d57e0..e5dc2daa 100644 --- a/RateLimiter/Data/Interfaces/IDataService.cs +++ b/RateLimiter/Data/Interfaces/IDataService.cs @@ -10,7 +10,10 @@ namespace RateLimiter.Data.Interfaces public interface IDataService where T : BaseModel { public Task> GetAllAsync(); + public Task> FindAsync(BaseModel searchCriteria); public Task SingleAsync(int id); + public Task SingleOrDefaultAsync(int id); + public Task SingleAsync(string identifier); public Task AddAsync(T entity); public Task UpdateAsync(int id, T entity); diff --git a/RateLimiter/Data/Repositories/DbRepository.cs b/RateLimiter/Data/Repositories/DbRepository.cs index 558d99ba..e1fb17de 100644 --- a/RateLimiter/Data/Repositories/DbRepository.cs +++ b/RateLimiter/Data/Repositories/DbRepository.cs @@ -33,6 +33,22 @@ public async Task> GetAllAsync(string[] includes) return await dbSet.ToListAsync(); } + public async Task> FindAsync(BaseModel searchCriteria, string[] includes) + { + var dbSet = _dbSet.AsQueryable(); + foreach (var include in includes) + { + if (include != "") + { + dbSet = dbSet.Include(include); + } + } + + var entities = await dbSet.Where(x => searchCriteria.CreatedBy == null || x.CreatedBy == searchCriteria.CreatedBy).ToListAsync(); + + + return entities; + } public async Task SingleAsync(int id, string[] includes) { @@ -80,15 +96,18 @@ public async Task SingleAsync(string identifier, string[] includes) return entity; } - public async Task SingleOrDefaultAsync(int id) + public async Task SingleOrDefaultAsync(int id, string[] includes) { var dbSet = _dbSet.AsQueryable(); - var entity = await dbSet.SingleOrDefaultAsync(e => e.Id == id); - if (entity == null) + foreach (var include in includes) { - throw new Exception("Invalid id"); + if (include != "") + { + dbSet = dbSet.Include(include); + } } + var entity = await dbSet.SingleOrDefaultAsync(e => e.Id == id); return entity; } diff --git a/RateLimiter/Data/Services/RequestsDataService.cs b/RateLimiter/Data/Services/RequestsDataService.cs index 0b605cda..c75670fc 100644 --- a/RateLimiter/Data/Services/RequestsDataService.cs +++ b/RateLimiter/Data/Services/RequestsDataService.cs @@ -26,6 +26,14 @@ public async Task> GetAllAsync() return requests; } + public async Task> FindAsync(BaseModel searchCriteria) + { + string[] includes = new string[] { "" }; + + var requests = await _requestsRepository.FindAsync(searchCriteria, includes); + + return requests; + } public async Task SingleAsync(int id) { string[] includes = new string[] { "" }; @@ -34,6 +42,14 @@ public async Task SingleAsync(int id) return request; } + public async Task SingleOrDefaultAsync(int id) + { + string[] includes = new string[] { "" }; + + var request = await _requestsRepository.SingleOrDefaultAsync(id, includes); + + return request; + } public async Task SingleAsync(string identifier) { string[] includes = new string[] { "" }; diff --git a/RateLimiter/Data/Services/ResourcesDataService.cs b/RateLimiter/Data/Services/ResourcesDataService.cs index 8f140fbf..d9a13719 100644 --- a/RateLimiter/Data/Services/ResourcesDataService.cs +++ b/RateLimiter/Data/Services/ResourcesDataService.cs @@ -26,6 +26,10 @@ public async Task> GetAllAsync() return resources; } + public async Task> FindAsync(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } public async Task SingleAsync(int id) { string[] includes = new string[] { "" }; @@ -34,6 +38,14 @@ public async Task SingleAsync(int id) return resource; } + public async Task SingleOrDefaultAsync(int id) + { + string[] includes = new string[] { "" }; + + var resource = await _resourcesRepository.SingleOrDefaultAsync(id, includes); + + return resource; + } public async Task SingleAsync(string identifier) { string[] includes = new string[] { "" }; diff --git a/RateLimiter/Data/Services/UsersDataService.cs b/RateLimiter/Data/Services/UsersDataService.cs index 61a45030..4e50e1c6 100644 --- a/RateLimiter/Data/Services/UsersDataService.cs +++ b/RateLimiter/Data/Services/UsersDataService.cs @@ -26,6 +26,10 @@ public async Task> GetAllAsync() return users; } + public async Task> FindAsync(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } public async Task SingleAsync(int id) { string[] includes = new string[] { "" }; @@ -34,6 +38,14 @@ public async Task SingleAsync(int id) return user; } + public async Task SingleOrDefaultAsync(int id) + { + string[] includes = new string[] { "" }; + + var user = await _usersRepository.SingleOrDefaultAsync(id, includes); + + return user; + } public async Task SingleAsync(string identifier) { string[] includes = new string[] { "" }; From 50682ce6ee8de2812125fac96dbc7c7a8a9bbcce Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Sun, 17 Nov 2024 05:36:33 -0700 Subject: [PATCH 4/9] Added logic for limiter --- .../Interfaces/IConfigService.cs | 4 +- .../Interfaces/IDataGeneratorService.cs | 6 +- RateLimiter.Tests/Services/ConfigService.cs | 15 +- .../Services/DataGeneratorService.cs | 23 +- .../Tests/Data/LimiterRulesDataServiceTest.cs | 234 ++++++++++++++++++ .../Tests/Data/RequestsDataServiceTest.cs | 4 +- .../Tests/Data/ResourcesDataServiceTest.cs | 8 +- .../Tests/Data/UsersDataServiceTest.cs | 34 +-- .../Tests/RateLimiter/LimiterServiceTest.cs | 8 +- .../Data/{Statuses.cs => CodeValues.cs} | 0 .../Data/DbContexts/RateLimiterDbContext.cs | 2 + RateLimiter/Data/Models/Data/LimiterRule.cs | 23 ++ RateLimiter/Data/Models/Data/Resource.cs | 10 +- RateLimiter/Data/Models/Data/User.cs | 1 + .../Data/Services/LimiterRulesDataService.cs | 87 +++++++ RateLimiter/Interfaces/IAuditService.cs | 15 -- RateLimiter/Interfaces/ILimiterService.cs | 5 +- RateLimiter/RateLimiter.csproj | 1 + RateLimiter/RateLimiterMiddleware.cs | 66 +++++ RateLimiter/Services/LimiterService.cs | 63 ++++- RateLimiter/Services/RequestsAuditService.cs | 31 --- 21 files changed, 549 insertions(+), 91 deletions(-) create mode 100644 RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs rename RateLimiter/Data/{Statuses.cs => CodeValues.cs} (100%) create mode 100644 RateLimiter/Data/Models/Data/LimiterRule.cs create mode 100644 RateLimiter/Data/Services/LimiterRulesDataService.cs delete mode 100644 RateLimiter/Interfaces/IAuditService.cs create mode 100644 RateLimiter/RateLimiterMiddleware.cs delete mode 100644 RateLimiter/Services/RequestsAuditService.cs diff --git a/RateLimiter.Tests/Interfaces/IConfigService.cs b/RateLimiter.Tests/Interfaces/IConfigService.cs index ae294e5a..ccca4f64 100644 --- a/RateLimiter.Tests/Interfaces/IConfigService.cs +++ b/RateLimiter.Tests/Interfaces/IConfigService.cs @@ -1,4 +1,5 @@ -using RateLimiter.Models; +using RateLimiter.Data.Models.Data; +using RateLimiter.Models; using System; using System.Collections.Generic; using System.Linq; @@ -16,5 +17,6 @@ public interface IConfigService public Task SeedResources(List resources); public Task SeedUsers(List users); public Task SeedRequests(List requests); + public Task SeedLimiterRules(List limiterRules); } } diff --git a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs index b184e4db..539e4413 100644 --- a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs +++ b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs @@ -1,4 +1,5 @@ -using RateLimiter.Models; +using RateLimiter.Data.Models.Data; +using RateLimiter.Models; using System; using System.Collections.Generic; using System.Linq; @@ -11,6 +12,7 @@ public interface IDataGeneratorService { public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool wasHandled); public Resource GenerateResource(int id, string name, Status status); - public User GenerateUser(int id, string name, Guid token); + public User GenerateUser(int id, string name, Guid token, string tokenSource); + public LimiterRule GenerateLimiterRule(int id, string name, string? tokenSource, int? resourceStatusId, int numPerTimespan, int numSeconds); } } diff --git a/RateLimiter.Tests/Services/ConfigService.cs b/RateLimiter.Tests/Services/ConfigService.cs index 1b36cdcc..2f69e0c4 100644 --- a/RateLimiter.Tests/Services/ConfigService.cs +++ b/RateLimiter.Tests/Services/ConfigService.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using RateLimiter.Data; using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Data; using RateLimiter.Interfaces; using RateLimiter.Models; using System; @@ -45,6 +46,10 @@ public async Task Reset() { _context.Users.Remove(user); } + foreach (var limiterRule in _context.LimiterRules) + { + _context.LimiterRules.Remove(limiterRule); + } await _context.SaveChangesAsync(); } public async Task SeedResources(List resources) @@ -67,7 +72,15 @@ public async Task SeedRequests(List requests) { foreach (var request in requests) { - _context.Add(request); + _context.Requests.Add(request); + }; + await _context.SaveChangesAsync(); + } + public async Task SeedLimiterRules(List limiterRules) + { + foreach (var limiterRule in limiterRules) + { + _context.LimiterRules.Add(limiterRule); }; await _context.SaveChangesAsync(); } diff --git a/RateLimiter.Tests/Services/DataGeneratorService.cs b/RateLimiter.Tests/Services/DataGeneratorService.cs index a08b2cc7..6eb6bc1b 100644 --- a/RateLimiter.Tests/Services/DataGeneratorService.cs +++ b/RateLimiter.Tests/Services/DataGeneratorService.cs @@ -1,4 +1,5 @@ -using RateLimiter.Models; +using RateLimiter.Data.Models.Data; +using RateLimiter.Models; using RateLimiter.Tests.Interfaces; using System; using System.Collections.Generic; @@ -41,7 +42,7 @@ public Resource GenerateResource(int id, string name, Status status) return resource; } - public User GenerateUser(int id, string username, Guid token) + public User GenerateUser(int id, string username, Guid token, string tokenSource) { var user = new User { @@ -49,6 +50,7 @@ public User GenerateUser(int id, string username, Guid token) Identifier = username, Name = username, Token = token.ToString(), + TokenSource = tokenSource, Email = string.Format("{0}@phonyEmail.com", username), CreatedBy = "DataGenerator", CreatedDate = DateTime.Now @@ -56,5 +58,22 @@ public User GenerateUser(int id, string username, Guid token) return user; } + public LimiterRule GenerateLimiterRule(int id, string name, string? tokenSource, int? resourceStatusId, int numPerTimespan, int numSeconds) + { + var limiterRule = new LimiterRule + { + Id = id , + Identifier = name , + Name = name , + TokenSource = tokenSource , + ResourceStatusId = resourceStatusId , + NumPerTimespan = numPerTimespan , + NumSeconds = numSeconds, + CreatedBy = "DataGenerator", + CreatedDate = DateTime.Now + }; + + return limiterRule; + } } } diff --git a/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs new file mode 100644 index 00000000..30187ae3 --- /dev/null +++ b/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs @@ -0,0 +1,234 @@ +using M42.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Data; +using RateLimiter.Interfaces; +using RateLimiter.Models; +using RateLimiter.Services; +using RateLimiter.Tests.Interfaces; +using RateLimiter.Tests.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RateLimiter.Tests; + +[TestFixture] +public class LimiterRulesDataServiceTest +{ + ServiceProvider _serviceProvider; + + private readonly IDataService _limiterRulesDataService; + private readonly IDataGeneratorService _dataGeneratorService; + private readonly IConfigService _configService; + + public LimiterRulesDataServiceTest() + { + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "LimiterRulesDataServiceTest")); + services.AddTransient(typeof(DbRepository<>)); + services.AddScoped, LimiterRulesDataService>(); + services.AddScoped(); + services.AddScoped(); + + _serviceProvider = services.BuildServiceProvider(); + + _limiterRulesDataService = _serviceProvider.GetService>(); + _dataGeneratorService = _serviceProvider.GetService(); + _configService = _serviceProvider.GetService(); + + } + + [SetUp] + public async Task SetUp() + { + await _configService.Reset(); + } + + + [Test] + public async Task GetAllTest() + { + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + + var seedLimiterRules = new List() + { + limiterRule1, + limiterRule2, + limiterRule3 + }; + + await _configService.SeedLimiterRules(seedLimiterRules); + + try + { + var limiterRules = await _limiterRulesDataService.GetAllAsync(); + + Assert.AreEqual(limiterRules.Count, 3); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task FindTest() + { + try + { + var searchCriteria = new BaseModel(); + var limiterRules = await _limiterRulesDataService.FindAsync(searchCriteria); + + Assert.AreEqual(limiterRules.Count, -1); // should never reach this assertion + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); // NotImplementedException is the expected result. + } + } + [Test] + public async Task GetByIdTest() + { + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + + var seedLimiterRules = new List() + { + limiterRule1, + limiterRule2, + limiterRule3 + }; + + await _configService.SeedLimiterRules(seedLimiterRules); + + try + { + var retrievedLimiterRule = await _limiterRulesDataService.SingleAsync(limiterRule2.Id); + + Assert.AreEqual(retrievedLimiterRule.Name, limiterRule2.Name); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task GetByIdentifierTest() + { + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + + var seedLimiterRules = new List() + { + limiterRule1, + limiterRule2, + limiterRule3 + }; + + await _configService.SeedLimiterRules(seedLimiterRules); + + try + { + var retrievedLimiterRule = await _limiterRulesDataService.SingleAsync(limiterRule2.Identifier); + + Assert.AreEqual(retrievedLimiterRule.Id, limiterRule2.Id); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task AddTest() + { + var limiterRuleToAdd = _dataGeneratorService.GenerateLimiterRule(100, "US Users", "US", null, 3, 15); + + try + { + var limiterRule = await _limiterRulesDataService.AddAsync(limiterRuleToAdd); + + var retrievedLimiterRule = await _limiterRulesDataService.SingleAsync(limiterRuleToAdd.Id); + + Assert.AreEqual(limiterRuleToAdd.Name, retrievedLimiterRule.Name); + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task UpdateTest() + { + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + + var seedLimiterRules = new List() + { + limiterRule1, + limiterRule2, + limiterRule3 + }; + + await _configService.SeedLimiterRules(seedLimiterRules); + + try + { + limiterRule2.Name = "Fred"; + + var result = _limiterRulesDataService.UpdateAsync(limiterRule2.Id, limiterRule2); + + var updatedLimiterRule = await _limiterRulesDataService.SingleAsync(limiterRule2.Id); + + Assert.AreEqual(updatedLimiterRule.Name, "Fred"); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } + [Test] + public async Task RemoveTest() + { + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + + var seedLimiterRules = new List() + { + limiterRule1, + limiterRule2, + limiterRule3 + }; + + await _configService.SeedLimiterRules(seedLimiterRules); + + try + { + var retrievedLimiterRules = await _limiterRulesDataService.GetAllAsync(); + Assert.AreEqual(retrievedLimiterRules.Count, 3); + + var limiterRules = await _limiterRulesDataService.RemoveAsync(limiterRule2.Id); + + retrievedLimiterRules = await _limiterRulesDataService.GetAllAsync(); + Assert.AreEqual(retrievedLimiterRules.Count, 2); + + var retrievedLimiterRule = await _limiterRulesDataService.SingleOrDefaultAsync(limiterRule2.Id); + + Assert.IsNull(retrievedLimiterRule); + } + catch (NotImplementedException ex) + { + Assert.That(true, Is.True); + } + } +} \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs index 567c0aec..c279f525 100644 --- a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -66,8 +66,8 @@ public async Task SetUp() await _configService.SeedResources(seedResources); - user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); - user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); + user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); var seedUsers = new List() { user1, diff --git a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs index 15516f8c..8cbc36ff 100644 --- a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using RateLimiter.Data; using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Data; using RateLimiter.Interfaces; using RateLimiter.Models; using RateLimiter.Services; @@ -22,6 +23,7 @@ public class ResourcesDataServiceTest ServiceProvider _serviceProvider; private readonly IDataService _resourcesDataService; + private readonly IDataService _limiterRulesDataService; private readonly IDataGeneratorService _dataGeneratorService; private readonly IConfigService _configService; @@ -32,12 +34,14 @@ public ResourcesDataServiceTest() services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "ResourcesTest")); services.AddTransient(typeof(DbRepository<>)); services.AddScoped, ResourcesDataService>(); + services.AddScoped, LimiterRulesDataService>(); services.AddScoped(); services.AddScoped(); _serviceProvider = services.BuildServiceProvider(); _resourcesDataService = _serviceProvider.GetService>(); + _limiterRulesDataService = _serviceProvider.GetService>(); _dataGeneratorService = _serviceProvider.GetService(); _configService = _serviceProvider.GetService(); @@ -150,10 +154,12 @@ public async Task GetByIdentifierTest() public async Task AddTest() { var resourceToAdd = _dataGeneratorService.GenerateResource(1, "ResourceToAdd", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var limiterRule = _dataGeneratorService.GenerateLimiterRule(1, "Maintenance", null, CodeValues.Statuses.Single(x => x.Name == "Maintenance").Id, 1, 5); + resourceToAdd.LimiterRules = new List { limiterRule }; try { - var resource = await _resourcesDataService.AddAsync(resourceToAdd); + var result = await _resourcesDataService.AddAsync(resourceToAdd); var retrievedResource = await _resourcesDataService.SingleAsync(resourceToAdd.Id); diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs index ead6490d..0d02387f 100644 --- a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -53,9 +53,9 @@ public async Task SetUp() [Test] public async Task GetAllTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); var seedUsers = new List() { @@ -93,11 +93,11 @@ public async Task FindTest() } } [Test] - public async Task GetByIdTest() + public async Task GetByIdTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); var seedUsers = new List() { @@ -122,9 +122,9 @@ public async Task GetByIdTest() [Test] public async Task GetByIdentifierTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); var seedUsers = new List() { @@ -149,7 +149,7 @@ public async Task GetByIdentifierTest() [Test] public async Task AddTest() { - var userToAdd = _dataGeneratorService.GenerateUser(100, "UserToAdd", Guid.NewGuid()); + var userToAdd = _dataGeneratorService.GenerateUser(100, "UserToAdd", Guid.NewGuid(), "US"); try { @@ -167,9 +167,9 @@ public async Task AddTest() [Test] public async Task UpdateTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); var seedUsers = new List() { @@ -199,9 +199,9 @@ public async Task UpdateTest() public async Task RemoveTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid()); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid()); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid()); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); var seedUsers = new List() { diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index 05b04bdc..25f4f47d 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -32,19 +32,17 @@ public void SetUp() } [Test] - public void GetTest() + public void AllowAccess_Test_1() { var requestsDataService = _serviceProvider.GetService>(); try { - var requests = requestsDataService.Get(); - - Assert.That(true, Is.False); + Assert.That(true, Is.True); // currently, all requests are allowed } catch (NotImplementedException ex) { - Assert.That(true, Is.True); + Assert.That(false, Is.True); } } } diff --git a/RateLimiter/Data/Statuses.cs b/RateLimiter/Data/CodeValues.cs similarity index 100% rename from RateLimiter/Data/Statuses.cs rename to RateLimiter/Data/CodeValues.cs diff --git a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs index b4d7e044..d2e6fae5 100644 --- a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs +++ b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using RateLimiter.Data.Models.Data; using RateLimiter.Models; using System; using System.Collections.Generic; @@ -18,5 +19,6 @@ public RateLimiterDbContext(DbContextOptions options) : ba public DbSet Resources { get; set; } public DbSet Requests { get; set; } public DbSet Statuses { get; set; } + public DbSet LimiterRules { get; set; } } } diff --git a/RateLimiter/Data/Models/Data/LimiterRule.cs b/RateLimiter/Data/Models/Data/LimiterRule.cs new file mode 100644 index 00000000..d4344edd --- /dev/null +++ b/RateLimiter/Data/Models/Data/LimiterRule.cs @@ -0,0 +1,23 @@ +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Data.Models.Data +{ + public class LimiterRule : BaseModel + { + public string Name { get; set; } + + // conditions to look for + public string? TokenSource { get; set; } + public int? ResourceStatusId { get; set; } + + // limiter + public int NumPerTimespan { get; set; } + public int NumSeconds { get; set; } + + } +} diff --git a/RateLimiter/Data/Models/Data/Resource.cs b/RateLimiter/Data/Models/Data/Resource.cs index 1e682946..db8d88ff 100644 --- a/RateLimiter/Data/Models/Data/Resource.cs +++ b/RateLimiter/Data/Models/Data/Resource.cs @@ -1,4 +1,5 @@ -using System; +using RateLimiter.Data.Models.Data; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,12 +12,9 @@ public class Resource : BaseModel public string Name { get; set; } public int StatusId { get; set; } public string Description { get; set; } + public Status Status { get; set; } - // Need to put the rules that will deterine whether to limit - // usage of a resource - - //public List LimiterRules { get; set; } + public List LimiterRules { get; set; } - public Status Status { get; set; } } } diff --git a/RateLimiter/Data/Models/Data/User.cs b/RateLimiter/Data/Models/Data/User.cs index 48209da8..c8e838ac 100644 --- a/RateLimiter/Data/Models/Data/User.cs +++ b/RateLimiter/Data/Models/Data/User.cs @@ -11,5 +11,6 @@ public class User : BaseModel public string Name { get; set; } public string Email { get; set; } public string Token { get; set; } + public string TokenSource { get; set; } } } diff --git a/RateLimiter/Data/Services/LimiterRulesDataService.cs b/RateLimiter/Data/Services/LimiterRulesDataService.cs new file mode 100644 index 00000000..3e222b21 --- /dev/null +++ b/RateLimiter/Data/Services/LimiterRulesDataService.cs @@ -0,0 +1,87 @@ +using M42.Data.Repositories; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Data; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter.Services +{ + public class LimiterRulesDataService : IDataService + { + private readonly DbRepository _limiterRulesRepository; + + public LimiterRulesDataService(DbRepository limiterRulesRepository) + { + _limiterRulesRepository = limiterRulesRepository; + } + + public async Task> GetAllAsync() + { + string[] includes = new string[] { "" }; + + var limiterRules = await _limiterRulesRepository.GetAllAsync(includes); + + return limiterRules; + } + public async Task> FindAsync(BaseModel searchCriteria) + { + throw new NotImplementedException(); + } + public async Task SingleAsync(int id) + { + string[] includes = new string[] { "" }; + + var limiterRule = await _limiterRulesRepository.SingleAsync(id, includes); + + return limiterRule; + } + public async Task SingleOrDefaultAsync(int id) + { + string[] includes = new string[] { "" }; + + var limiterRule = await _limiterRulesRepository.SingleOrDefaultAsync(id, includes); + + return limiterRule; + } + public async Task SingleAsync(string identifier) + { + string[] includes = new string[] { "" }; + + var limiterRule = await _limiterRulesRepository.SingleAsync(identifier, includes); + + return limiterRule; + } + public async Task AddAsync(LimiterRule limiterRule) + { + var newLimiterRule = await _limiterRulesRepository.AddAsync(limiterRule); + + return true; + } + public async Task UpdateAsync(int id, LimiterRule limiterRule) + { + string[] includes = new string[] { "" }; + + var existingLimiterRule = await _limiterRulesRepository.SingleAsync(id, includes); + + existingLimiterRule.Name = limiterRule.Name; + existingLimiterRule.NumPerTimespan = limiterRule.NumPerTimespan; + existingLimiterRule.NumSeconds = limiterRule.NumSeconds; + existingLimiterRule.UpdatedBy = limiterRule.UpdatedBy; + existingLimiterRule.UpdatedDate = DateTime.Now; + + await _limiterRulesRepository.UpdateAsync(existingLimiterRule); + + return true; + } + public async Task RemoveAsync(int id) + { + var newLimiterRule = await _limiterRulesRepository.RemoveAsync(id); + + return true; + } + } +} diff --git a/RateLimiter/Interfaces/IAuditService.cs b/RateLimiter/Interfaces/IAuditService.cs deleted file mode 100644 index 0b58fb12..00000000 --- a/RateLimiter/Interfaces/IAuditService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using RateLimiter.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Interfaces -{ - public interface IAuditService - { - public Task Log(T data); - public Task> GetHistory(F filter); - } -} diff --git a/RateLimiter/Interfaces/ILimiterService.cs b/RateLimiter/Interfaces/ILimiterService.cs index 06167fa5..7ba745de 100644 --- a/RateLimiter/Interfaces/ILimiterService.cs +++ b/RateLimiter/Interfaces/ILimiterService.cs @@ -1,4 +1,5 @@ -using System; +using RateLimiter.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,6 +9,6 @@ namespace RateLimiter.Interfaces { public interface ILimiterService { - public Task AllowAccess(int ResourceId, int UserId); + public Task AllowAccess(Request request); } } diff --git a/RateLimiter/RateLimiter.csproj b/RateLimiter/RateLimiter.csproj index 66f39c4f..123660e2 100644 --- a/RateLimiter/RateLimiter.csproj +++ b/RateLimiter/RateLimiter.csproj @@ -8,6 +8,7 @@ + \ No newline at end of file diff --git a/RateLimiter/RateLimiterMiddleware.cs b/RateLimiter/RateLimiterMiddleware.cs new file mode 100644 index 00000000..457affa3 --- /dev/null +++ b/RateLimiter/RateLimiterMiddleware.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Http; +using RateLimiter.Data.Interfaces; +using RateLimiter.Interfaces; +using RateLimiter.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace RateLimiter +{ + public class RequestLimiterMiddleware + { + private readonly RequestDelegate _next; + private readonly ILimiterService _limiterService; + private readonly IDataService _resourcesDataService; + private readonly IDataService _userDataService; + + public RequestLimiterMiddleware(RequestDelegate next, ILimiterService limiterService, IDataService resourcesDataService, IDataService usersDataService) + { + _next = next; + _limiterService = limiterService; + _resourcesDataService = resourcesDataService; + _userDataService = usersDataService; + } + + public async Task InvokeAsync(HttpContext context) + { + var resourceName = context.Request.Path; + var resource = await _resourcesDataService.SingleAsync(resourceName); // assumes Identifier matches the pathname of the resource + + var jeff = context.Request.Headers; // Need to pull token from header and match to user token + var username = "jeff"; + + var user = await _userDataService.SingleAsync(username); + + var request = new Request + { + Identifier = Guid.NewGuid().ToString(), + RequestDate = DateTime.Now, + UserId = user.Id, + User = user, + ResourceId = resource.Id, + Resource = resource , + CreatedBy = "jeff", + CreatedDate = DateTime.UtcNow + }; + + try + { + var allowAccess = await _limiterService.AllowAccess(request); + + if (allowAccess) + { + await _next(context); // Call the next middleware component in the pipeline. + } + } + catch + { + throw; // Ensure exceptions are propagated. + } + } + } +} \ No newline at end of file diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs index b24df2ba..9a6c282a 100644 --- a/RateLimiter/Services/LimiterService.cs +++ b/RateLimiter/Services/LimiterService.cs @@ -1,4 +1,6 @@ -using RateLimiter.Data.Interfaces; +using RateLimiter.Data; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models.Data; using RateLimiter.Data.Models.Filter; using RateLimiter.Interfaces; using RateLimiter.Models; @@ -12,16 +14,65 @@ namespace RateLimiter.Services { public class LimiterService : ILimiterService { - private readonly IAuditService _requestAuditService; + protected IDataService _requestsDataService; + protected IDataService _resourcesDataService; + protected IDataService _usersDataSource; - public LimiterService(IAuditService requestAuditService) + public LimiterService(IDataService requestsDataService, IDataService resourcesDataService, IDataService usersDataSource) { - _requestAuditService = requestAuditService; + _requestsDataService = requestsDataService; + _resourcesDataService = resourcesDataService; + _usersDataSource = usersDataSource; } - public async Task AllowAccess(int ResourceId, int UserId) + public async Task AllowAccess(Request request) { - throw new NotImplementedException(); + // By default, access allowed to the resource + bool allowAccess = true; + + var resource = await _resourcesDataService.SingleAsync(request.ResourceId); + var user = await _usersDataSource.SingleAsync(request.UserId); + + if (resource.LimiterRules == null) + { + allowAccess = true; // no rules in place so allow access + } + if (resource.StatusId == CodeValues.Statuses.Single(x => x.Name == "Offline").Id) + { + allowAccess = false; // resource is offline - unclear if this is the proper place for this logic... + } + + // Identify the rule to use + LimiterRule limiterRule = new LimiterRule { Id = -1, Name = "No rule in effect", NumSeconds = 0, NumPerTimespan = 0 }; + + foreach (var rule in resource.LimiterRules) + { + if (rule.TokenSource == null || rule.TokenSource == user.TokenSource) + { + limiterRule = rule; + } + } + + // If a rule is in effect for the request, evaluate it + if (limiterRule.Id > 0) + { + // if a limiter rule is in effect, then evaluate it here + var userRequests = await _requestsDataService.FindAsync(new BaseModel { CreatedBy = user.Name }); + var userResourceRequests = userRequests.Where(x => x.ResourceId == resource.Id && x.WasHandled).ToList(); + + var numRequests = userResourceRequests.Where(x => x.CreatedDate.AddSeconds(limiterRule.NumSeconds) > DateTime.Now).Count(); + + if (numRequests > limiterRule.NumPerTimespan) + { + allowAccess = false; + } + } + + // Record the request + request.WasHandled = allowAccess; + await _requestsDataService.AddAsync(request); + + return allowAccess; } } } diff --git a/RateLimiter/Services/RequestsAuditService.cs b/RateLimiter/Services/RequestsAuditService.cs deleted file mode 100644 index bc8ddbac..00000000 --- a/RateLimiter/Services/RequestsAuditService.cs +++ /dev/null @@ -1,31 +0,0 @@ -using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Filter; -using RateLimiter.Interfaces; -using RateLimiter.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Services -{ - public class RequestsAuditService : IAuditService - { - protected IDataService _requestDataService; - - public RequestsAuditService(IDataService requestDataService) - { - _requestDataService = requestDataService; - } - - public async Task Log(Request request) - { - throw new NotImplementedException(); - } - public async Task> GetHistory(RequestsFilter filter) - { - throw new NotImplementedException(); - } - } -} From 9844e18b2966392e237474e0c4450269e9b84e4a Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Mon, 18 Nov 2024 02:17:57 -0700 Subject: [PATCH 5/9] Namespacing --- .../Interfaces/IConfigService.cs | 8 +-- .../Interfaces/IDataGeneratorService.cs | 7 +-- RateLimiter.Tests/RateLimiter.Tests.csproj | 5 -- RateLimiter.Tests/Services/ConfigService.cs | 14 ++--- .../Services/DataGeneratorService.cs | 7 +-- .../Tests/Data/LimiterRulesDataServiceTest.cs | 12 ++-- .../Tests/Data/RequestsDataServiceTest.cs | 12 ++-- .../Tests/Data/ResourcesDataServiceTest.cs | 11 ++-- .../Tests/Data/UsersDataServiceTest.cs | 11 ++-- .../Tests/RateLimiter/ConfigServiceTest.cs | 50 ---------------- .../Tests/RateLimiter/LimiterServiceTest.cs | 12 ++-- .../RateLimiter/RequestHandlerServiceTest.cs | 58 ------------------- .../RateLimiter/RequestsAuditServiceTest.cs | 58 ------------------- RateLimiter/Data/CodeValues.cs | 5 +- .../Data/DbContexts/RateLimiterDbContext.cs | 10 +--- RateLimiter/Data/Interfaces/IDataService.cs | 5 +- RateLimiter/Data/Models/Data/BaseModel.cs | 6 +- RateLimiter/Data/Models/Data/LimiterRule.cs | 9 +-- RateLimiter/Data/Models/Data/Request.cs | 6 +- RateLimiter/Data/Models/Data/Resource.cs | 9 +-- RateLimiter/Data/Models/Data/Status.cs | 8 +-- RateLimiter/Data/Models/Data/User.cs | 8 +-- .../Models/Filter/RequestsFilter.cs | 2 +- RateLimiter/Data/Repositories/DbRepository.cs | 6 +- .../Data/Services/LimiterRulesDataService.cs | 11 ++-- .../Data/Services/RequestsDataService.cs | 10 ++-- .../Data/Services/ResourcesDataService.cs | 10 ++-- RateLimiter/Data/Services/UsersDataService.cs | 10 ++-- RateLimiter/Interfaces/ILimiterService.cs | 6 +- RateLimiter/Models/Rule.cs | 12 ---- RateLimiter/RateLimiterMiddleware.cs | 10 +--- RateLimiter/Services/LimiterService.cs | 6 +- 32 files changed, 70 insertions(+), 344 deletions(-) delete mode 100644 RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs delete mode 100644 RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs delete mode 100644 RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs rename RateLimiter/{ => Data}/Models/Filter/RequestsFilter.cs (86%) delete mode 100644 RateLimiter/Models/Rule.cs diff --git a/RateLimiter.Tests/Interfaces/IConfigService.cs b/RateLimiter.Tests/Interfaces/IConfigService.cs index ccca4f64..d29d5e6b 100644 --- a/RateLimiter.Tests/Interfaces/IConfigService.cs +++ b/RateLimiter.Tests/Interfaces/IConfigService.cs @@ -1,12 +1,8 @@ -using RateLimiter.Data.Models.Data; -using RateLimiter.Models; -using System; +using RateLimiter.Data.Models; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Interfaces +namespace RateLimiter.Tests.Interfaces { // The config service is intended to add data to the database // for testing purposes. diff --git a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs index 539e4413..915b4ed4 100644 --- a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs +++ b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs @@ -1,10 +1,5 @@ -using RateLimiter.Data.Models.Data; -using RateLimiter.Models; +using RateLimiter.Data.Models; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace RateLimiter.Tests.Interfaces { diff --git a/RateLimiter.Tests/RateLimiter.Tests.csproj b/RateLimiter.Tests/RateLimiter.Tests.csproj index 225b577e..a90f1cb8 100644 --- a/RateLimiter.Tests/RateLimiter.Tests.csproj +++ b/RateLimiter.Tests/RateLimiter.Tests.csproj @@ -4,11 +4,6 @@ latest enable - - - - - diff --git a/RateLimiter.Tests/Services/ConfigService.cs b/RateLimiter.Tests/Services/ConfigService.cs index 2f69e0c4..b10067bf 100644 --- a/RateLimiter.Tests/Services/ConfigService.cs +++ b/RateLimiter.Tests/Services/ConfigService.cs @@ -1,16 +1,12 @@ -using Microsoft.EntityFrameworkCore; -using RateLimiter.Data; -using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Data; -using RateLimiter.Interfaces; -using RateLimiter.Models; -using System; +using RateLimiter.Data; +using RateLimiter.Data.Contexts; +using RateLimiter.Data.Models; +using RateLimiter.Tests.Interfaces; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Services +namespace RateLimiter.Tests.Services { public class ConfigService : IConfigService { diff --git a/RateLimiter.Tests/Services/DataGeneratorService.cs b/RateLimiter.Tests/Services/DataGeneratorService.cs index 6eb6bc1b..9631a96f 100644 --- a/RateLimiter.Tests/Services/DataGeneratorService.cs +++ b/RateLimiter.Tests/Services/DataGeneratorService.cs @@ -1,11 +1,6 @@ -using RateLimiter.Data.Models.Data; -using RateLimiter.Models; +using RateLimiter.Data.Models; using RateLimiter.Tests.Interfaces; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace RateLimiter.Tests.Services { diff --git a/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs index 30187ae3..600c33b4 100644 --- a/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/LimiterRulesDataServiceTest.cs @@ -1,13 +1,11 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -using RateLimiter.Data; +using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Data; -using RateLimiter.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; +using RateLimiter.Data.Services; using RateLimiter.Tests.Interfaces; using RateLimiter.Tests.Services; using System; diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs index c279f525..46037f91 100644 --- a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -1,19 +1,17 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources; using NUnit.Framework; using RateLimiter.Data; +using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; -using RateLimiter.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; +using RateLimiter.Data.Services; using RateLimiter.Tests.Interfaces; using RateLimiter.Tests.Services; using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Intrinsics.X86; using System.Threading.Tasks; namespace RateLimiter.Tests; diff --git a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs index 8cbc36ff..1a7587fc 100644 --- a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs @@ -1,13 +1,12 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using RateLimiter.Data; +using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Data; -using RateLimiter.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; +using RateLimiter.Data.Services; using RateLimiter.Tests.Interfaces; using RateLimiter.Tests.Services; using System; diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs index 0d02387f..5d488f1a 100644 --- a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -1,12 +1,11 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -using RateLimiter.Data; +using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; -using RateLimiter.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; +using RateLimiter.Data.Services; using RateLimiter.Tests.Interfaces; using RateLimiter.Tests.Services; using System; diff --git a/RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs deleted file mode 100644 index de6e4ddc..00000000 --- a/RateLimiter.Tests/Tests/RateLimiter/ConfigServiceTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using RateLimiter.Data; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace RateLimiter.Tests; - -[TestFixture] -public class ConfigServiceTest -{ - ServiceProvider _serviceProvider; - - [SetUp] - public void SetUp() - { - var services = new ServiceCollection(); - - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); - services.AddTransient(typeof(DbRepository<>)); - services.AddScoped, RequestsDataService>(); - services.AddScoped, ResourcesDataService>(); - services.AddScoped, UsersDataService>(); - - _serviceProvider = services.BuildServiceProvider(); - - } - [Test] - public void GetTest() - { - var requestsDataService = _serviceProvider.GetService>(); - - try - { - var requests = requestsDataService.Get(); - - Assert.That(true, Is.False); - } - catch (NotImplementedException ex) - { - Assert.That(true, Is.True); - } - } -} diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index 25f4f47d..6aabec53 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -1,14 +1,12 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; -using RateLimiter.Data; +using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; +using RateLimiter.Data.Services; using System; -using System.Collections.Generic; -using System.Threading.Tasks; namespace RateLimiter.Tests; diff --git a/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs deleted file mode 100644 index f6369577..00000000 --- a/RateLimiter.Tests/Tests/RateLimiter/RequestHandlerServiceTest.cs +++ /dev/null @@ -1,58 +0,0 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using RateLimiter.Data; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace RateLimiter.Tests; - -[TestFixture] -public class RequestsHandlerServiceTest -{ - ServiceProvider _serviceProvider; - - [SetUp] - public void SetUp() - { - var services = new ServiceCollection(); - - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); - services.AddTransient(typeof(DbRepository<>)); - services.AddScoped, RequestsDataService>(); - services.AddScoped, ResourcesDataService>(); - services.AddScoped, UsersDataService>(); - - _serviceProvider = services.BuildServiceProvider(); - - } - [Test] - public void GetTest() - { - var requestsDataService = _serviceProvider.GetService>(); - - try - { - var requests = requestsDataService.Get(); - - Assert.That(true, Is.False); - } - catch (NotImplementedException ex) - { - Assert.That(true, Is.True); - } - } -} - -//public Task> Get(); -//public Task> Get(BaseModel searchCriteria); -//public Task Get(int id); -//public Task Get(string identifier); -//public Task Add(T entity); -//public Task Update(int id, T entity); -//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs deleted file mode 100644 index a53670b2..00000000 --- a/RateLimiter.Tests/Tests/RateLimiter/RequestsAuditServiceTest.cs +++ /dev/null @@ -1,58 +0,0 @@ -using M42.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using RateLimiter.Data; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; -using RateLimiter.Services; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace RateLimiter.Tests; - -[TestFixture] -public class RequestsAuditServiceTest -{ - ServiceProvider _serviceProvider; - - [SetUp] - public void SetUp() - { - var services = new ServiceCollection(); - - services.AddDbContext(options => options.UseInMemoryDatabase(databaseName: "RateLimitertDatabase")); - services.AddTransient(typeof(DbRepository<>)); - services.AddScoped, RequestsDataService>(); - services.AddScoped, ResourcesDataService>(); - services.AddScoped, UsersDataService>(); - - _serviceProvider = services.BuildServiceProvider(); - - } - [Test] - public void GetTest() - { - var requestsDataService = _serviceProvider.GetService>(); - - try - { - var requests = requestsDataService.Get(); - - Assert.That(true, Is.False); - } - catch (NotImplementedException ex) - { - Assert.That(true, Is.True); - } - } -} - -//public Task> Get(); -//public Task> Get(BaseModel searchCriteria); -//public Task Get(int id); -//public Task Get(string identifier); -//public Task Add(T entity); -//public Task Update(int id, T entity); -//public Task Delete(int id); \ No newline at end of file diff --git a/RateLimiter/Data/CodeValues.cs b/RateLimiter/Data/CodeValues.cs index b10a7e66..4b397798 100644 --- a/RateLimiter/Data/CodeValues.cs +++ b/RateLimiter/Data/CodeValues.cs @@ -1,9 +1,6 @@ -using RateLimiter.Models; +using RateLimiter.Data.Models; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace RateLimiter.Data { diff --git a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs index d2e6fae5..ec5cb5bd 100644 --- a/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs +++ b/RateLimiter/Data/DbContexts/RateLimiterDbContext.cs @@ -1,13 +1,7 @@ using Microsoft.EntityFrameworkCore; -using RateLimiter.Data.Models.Data; -using RateLimiter.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using RateLimiter.Data.Models; -namespace RateLimiter.Data +namespace RateLimiter.Data.Contexts { public class RateLimiterDbContext : DbContext { diff --git a/RateLimiter/Data/Interfaces/IDataService.cs b/RateLimiter/Data/Interfaces/IDataService.cs index e5dc2daa..85770516 100644 --- a/RateLimiter/Data/Interfaces/IDataService.cs +++ b/RateLimiter/Data/Interfaces/IDataService.cs @@ -1,8 +1,5 @@ -using RateLimiter.Models; -using System; +using RateLimiter.Data.Models; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace RateLimiter.Data.Interfaces diff --git a/RateLimiter/Data/Models/Data/BaseModel.cs b/RateLimiter/Data/Models/Data/BaseModel.cs index f91e8774..16eb6f9d 100644 --- a/RateLimiter/Data/Models/Data/BaseModel.cs +++ b/RateLimiter/Data/Models/Data/BaseModel.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace RateLimiter.Models +namespace RateLimiter.Data.Models { public class BaseModel { diff --git a/RateLimiter/Data/Models/Data/LimiterRule.cs b/RateLimiter/Data/Models/Data/LimiterRule.cs index d4344edd..c6bb9012 100644 --- a/RateLimiter/Data/Models/Data/LimiterRule.cs +++ b/RateLimiter/Data/Models/Data/LimiterRule.cs @@ -1,11 +1,4 @@ -using RateLimiter.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Data.Models.Data +namespace RateLimiter.Data.Models { public class LimiterRule : BaseModel { diff --git a/RateLimiter/Data/Models/Data/Request.cs b/RateLimiter/Data/Models/Data/Request.cs index ec57feec..f3f5a116 100644 --- a/RateLimiter/Data/Models/Data/Request.cs +++ b/RateLimiter/Data/Models/Data/Request.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace RateLimiter.Models +namespace RateLimiter.Data.Models { public class Request : BaseModel { diff --git a/RateLimiter/Data/Models/Data/Resource.cs b/RateLimiter/Data/Models/Data/Resource.cs index db8d88ff..4a0a406f 100644 --- a/RateLimiter/Data/Models/Data/Resource.cs +++ b/RateLimiter/Data/Models/Data/Resource.cs @@ -1,11 +1,6 @@ -using RateLimiter.Data.Models.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; -namespace RateLimiter.Models +namespace RateLimiter.Data.Models { public class Resource : BaseModel { diff --git a/RateLimiter/Data/Models/Data/Status.cs b/RateLimiter/Data/Models/Data/Status.cs index e001d4e6..234081c8 100644 --- a/RateLimiter/Data/Models/Data/Status.cs +++ b/RateLimiter/Data/Models/Data/Status.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Models +namespace RateLimiter.Data.Models { public class Status : BaseModel { diff --git a/RateLimiter/Data/Models/Data/User.cs b/RateLimiter/Data/Models/Data/User.cs index c8e838ac..2b5db3c1 100644 --- a/RateLimiter/Data/Models/Data/User.cs +++ b/RateLimiter/Data/Models/Data/User.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Models +namespace RateLimiter.Data.Models { public class User : BaseModel { diff --git a/RateLimiter/Models/Filter/RequestsFilter.cs b/RateLimiter/Data/Models/Filter/RequestsFilter.cs similarity index 86% rename from RateLimiter/Models/Filter/RequestsFilter.cs rename to RateLimiter/Data/Models/Filter/RequestsFilter.cs index ac67dbd5..a302cc7e 100644 --- a/RateLimiter/Models/Filter/RequestsFilter.cs +++ b/RateLimiter/Data/Models/Filter/RequestsFilter.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Data.Models.Filter +namespace RateLimiter.Data.Filters { public class RequestsFilter { diff --git a/RateLimiter/Data/Repositories/DbRepository.cs b/RateLimiter/Data/Repositories/DbRepository.cs index e1fb17de..6647ede0 100644 --- a/RateLimiter/Data/Repositories/DbRepository.cs +++ b/RateLimiter/Data/Repositories/DbRepository.cs @@ -1,12 +1,12 @@ using Microsoft.EntityFrameworkCore; -using RateLimiter.Data; -using RateLimiter.Models; +using RateLimiter.Data.Contexts; +using RateLimiter.Data.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace M42.Data.Repositories +namespace RateLimiter.Data.Repositories { public class DbRepository where Entity : BaseModel { diff --git a/RateLimiter/Data/Services/LimiterRulesDataService.cs b/RateLimiter/Data/Services/LimiterRulesDataService.cs index 3e222b21..56ffbf0c 100644 --- a/RateLimiter/Data/Services/LimiterRulesDataService.cs +++ b/RateLimiter/Data/Services/LimiterRulesDataService.cs @@ -1,14 +1,11 @@ -using M42.Data.Repositories; -using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Data; -using RateLimiter.Models; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Services +namespace RateLimiter.Data.Services { public class LimiterRulesDataService : IDataService { diff --git a/RateLimiter/Data/Services/RequestsDataService.cs b/RateLimiter/Data/Services/RequestsDataService.cs index c75670fc..97eac480 100644 --- a/RateLimiter/Data/Services/RequestsDataService.cs +++ b/RateLimiter/Data/Services/RequestsDataService.cs @@ -1,13 +1,11 @@ -using M42.Data.Repositories; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Services +namespace RateLimiter.Data.Services { public class RequestsDataService : IDataService { diff --git a/RateLimiter/Data/Services/ResourcesDataService.cs b/RateLimiter/Data/Services/ResourcesDataService.cs index d9a13719..44aafe68 100644 --- a/RateLimiter/Data/Services/ResourcesDataService.cs +++ b/RateLimiter/Data/Services/ResourcesDataService.cs @@ -1,13 +1,11 @@ -using M42.Data.Repositories; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Services +namespace RateLimiter.Data.Services { public class ResourcesDataService : IDataService { diff --git a/RateLimiter/Data/Services/UsersDataService.cs b/RateLimiter/Data/Services/UsersDataService.cs index 4e50e1c6..ccbef705 100644 --- a/RateLimiter/Data/Services/UsersDataService.cs +++ b/RateLimiter/Data/Services/UsersDataService.cs @@ -1,13 +1,11 @@ -using M42.Data.Repositories; -using RateLimiter.Data.Interfaces; -using RateLimiter.Models; +using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models; +using RateLimiter.Data.Repositories; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -namespace RateLimiter.Services +namespace RateLimiter.Data.Services { public class UsersDataService : IDataService { diff --git a/RateLimiter/Interfaces/ILimiterService.cs b/RateLimiter/Interfaces/ILimiterService.cs index 7ba745de..979b75f2 100644 --- a/RateLimiter/Interfaces/ILimiterService.cs +++ b/RateLimiter/Interfaces/ILimiterService.cs @@ -1,8 +1,4 @@ -using RateLimiter.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using RateLimiter.Data.Models; using System.Threading.Tasks; namespace RateLimiter.Interfaces diff --git a/RateLimiter/Models/Rule.cs b/RateLimiter/Models/Rule.cs deleted file mode 100644 index 4899461b..00000000 --- a/RateLimiter/Models/Rule.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RateLimiter.Models -{ - internal class Rule - { - } -} diff --git a/RateLimiter/RateLimiterMiddleware.cs b/RateLimiter/RateLimiterMiddleware.cs index 457affa3..cc12ff3b 100644 --- a/RateLimiter/RateLimiterMiddleware.cs +++ b/RateLimiter/RateLimiterMiddleware.cs @@ -1,12 +1,8 @@ using Microsoft.AspNetCore.Http; using RateLimiter.Data.Interfaces; +using RateLimiter.Data.Models; using RateLimiter.Interfaces; -using RateLimiter.Models; using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Text; using System.Threading.Tasks; namespace RateLimiter @@ -50,9 +46,9 @@ public async Task InvokeAsync(HttpContext context) try { - var allowAccess = await _limiterService.AllowAccess(request); + var proceedToNext = await _limiterService.AllowAccess(request); - if (allowAccess) + if (proceedToNext) { await _next(context); // Call the next middleware component in the pipeline. } diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs index 9a6c282a..08e4a5a2 100644 --- a/RateLimiter/Services/LimiterService.cs +++ b/RateLimiter/Services/LimiterService.cs @@ -1,13 +1,9 @@ using RateLimiter.Data; using RateLimiter.Data.Interfaces; -using RateLimiter.Data.Models.Data; -using RateLimiter.Data.Models.Filter; +using RateLimiter.Data.Models; using RateLimiter.Interfaces; -using RateLimiter.Models; using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace RateLimiter.Services From efd056ac71df5497596658c1967f3e26d33cd3ab Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Mon, 18 Nov 2024 04:16:26 -0700 Subject: [PATCH 6/9] LimiterService test for no limiters worked. all requests allowed. --- .../Interfaces/IDataGeneratorService.cs | 5 +- RateLimiter.Tests/Services/ConfigService.cs | 3 +- .../Services/DataGeneratorService.cs | 13 ++-- .../Tests/Data/RequestsDataServiceTest.cs | 5 +- .../Tests/Data/ResourcesDataServiceTest.cs | 35 +++++------ .../Tests/Data/UsersDataServiceTest.cs | 30 ++++++++-- .../Tests/RateLimiter/LimiterServiceTest.cs | 60 +++++++++++++++++-- RateLimiter/Data/CodeValues.cs | 10 +++- RateLimiter/Data/Models/Data/Request.cs | 2 +- RateLimiter/Data/Repositories/DbRepository.cs | 5 +- .../Data/Services/LimiterRulesDataService.cs | 2 +- .../Data/Services/RequestsDataService.cs | 4 +- .../Data/Services/ResourcesDataService.cs | 2 +- RateLimiter/Data/Services/UsersDataService.cs | 6 +- RateLimiter/Services/LimiterService.cs | 13 +++- 15 files changed, 147 insertions(+), 48 deletions(-) diff --git a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs index 915b4ed4..379d035b 100644 --- a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs +++ b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs @@ -1,12 +1,13 @@ using RateLimiter.Data.Models; using System; +using System.Collections.Generic; namespace RateLimiter.Tests.Interfaces { public interface IDataGeneratorService { - public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool wasHandled); - public Resource GenerateResource(int id, string name, Status status); + public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool? wasHandled); + public Resource GenerateResource(int id, string name, Status status, List limiterRules); public User GenerateUser(int id, string name, Guid token, string tokenSource); public LimiterRule GenerateLimiterRule(int id, string name, string? tokenSource, int? resourceStatusId, int numPerTimespan, int numSeconds); } diff --git a/RateLimiter.Tests/Services/ConfigService.cs b/RateLimiter.Tests/Services/ConfigService.cs index b10067bf..8f5fd05a 100644 --- a/RateLimiter.Tests/Services/ConfigService.cs +++ b/RateLimiter.Tests/Services/ConfigService.cs @@ -1,4 +1,5 @@ using RateLimiter.Data; +using RateLimiter.Data.CodeValues; using RateLimiter.Data.Contexts; using RateLimiter.Data.Models; using RateLimiter.Tests.Interfaces; @@ -21,7 +22,7 @@ public ConfigService(RateLimiterDbContext context) if (statusesCount == 0) { - foreach (var status in CodeValues.Statuses) + foreach (var status in Statuses.Values) { _context.Statuses.Add(status); } diff --git a/RateLimiter.Tests/Services/DataGeneratorService.cs b/RateLimiter.Tests/Services/DataGeneratorService.cs index 9631a96f..a04e4d97 100644 --- a/RateLimiter.Tests/Services/DataGeneratorService.cs +++ b/RateLimiter.Tests/Services/DataGeneratorService.cs @@ -1,19 +1,23 @@ -using RateLimiter.Data.Models; +using NUnit.Framework; +using RateLimiter.Data.Models; using RateLimiter.Tests.Interfaces; using System; +using System.Collections.Generic; namespace RateLimiter.Tests.Services { internal class DataGeneratorService : IDataGeneratorService { - public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool wasHandled) + public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool? wasHandled) { var request = new Request { Id = id, Identifier = identifier, RequestDate = DateTime.Now, + ResourceId = resource.Id, Resource = resource, + UserId = user.Id, User = user, WasHandled = wasHandled, CreatedBy = "DataGenerator", @@ -22,7 +26,7 @@ public Request GenerateRequest(int id, Resource resource, User user, string iden return request; } - public Resource GenerateResource(int id, string name, Status status) + public Resource GenerateResource(int id, string name, Status status, List limiterRules) { var resource = new Resource { @@ -31,6 +35,7 @@ public Resource GenerateResource(int id, string name, Status status) Name = name, Description = name, Status = status, + LimiterRules = limiterRules, CreatedBy = "DataGenerator", CreatedDate = DateTime.Now }; @@ -42,7 +47,7 @@ public User GenerateUser(int id, string username, Guid token, string tokenSource var user = new User { Id = id, - Identifier = username, + Identifier = token.ToString(), // small hack - Find() only works with attributes in the BaseModel so user token must reside in the Identifier for now. Name = username, Token = token.ToString(), TokenSource = tokenSource, diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs index 46037f91..c4f3e8d8 100644 --- a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using RateLimiter.Data; +using RateLimiter.Data.CodeValues; using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; using RateLimiter.Data.Models; @@ -53,8 +54,8 @@ public async Task SetUp() { await _configService.Reset(); - resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); - resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); + resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, new List()); + resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", Statuses.Normal, new List()); var seedResources = new List() { diff --git a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs index 1a7587fc..de235420 100644 --- a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using RateLimiter.Data; +using RateLimiter.Data.CodeValues; using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; using RateLimiter.Data.Models; @@ -56,9 +57,9 @@ public async Task SetUp() [Test] public async Task GetAllTest() { - var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, new List()); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", Statuses.Normal, new List()); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", Statuses.Normal, new List()); var seedResources = new List() { @@ -98,9 +99,9 @@ public async Task FindTest() [Test] public async Task GetByIdTest() { - var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, new List()); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", Statuses.Normal, new List()); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", Statuses.Normal, new List()); var seedResources = new List() { @@ -125,9 +126,9 @@ public async Task GetByIdTest() [Test] public async Task GetByIdentifierTest() { - var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, new List()); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", Statuses.Normal, new List()); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", Statuses.Normal, new List()); var seedResources = new List() { @@ -152,8 +153,8 @@ public async Task GetByIdentifierTest() [Test] public async Task AddTest() { - var resourceToAdd = _dataGeneratorService.GenerateResource(1, "ResourceToAdd", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var limiterRule = _dataGeneratorService.GenerateLimiterRule(1, "Maintenance", null, CodeValues.Statuses.Single(x => x.Name == "Maintenance").Id, 1, 5); + var resourceToAdd = _dataGeneratorService.GenerateResource(1, "ResourceToAdd", Statuses.Normal, new List()); + var limiterRule = _dataGeneratorService.GenerateLimiterRule(1, "Maintenance", null, Statuses.Maintenance.Id, 1, 5); resourceToAdd.LimiterRules = new List { limiterRule }; try @@ -172,9 +173,9 @@ public async Task AddTest() [Test] public async Task UpdateTest() { - var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, new List()); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", Statuses.Normal, new List()); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", Statuses.Normal, new List()); var seedResources = new List() { @@ -204,9 +205,9 @@ public async Task UpdateTest() public async Task RemoveTest() { - var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", CodeValues.Statuses.Single(x => x.Name == "Normal")); - var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", CodeValues.Statuses.Single(x => x.Name == "Normal")); + var resource1 = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, new List()); + var resource2 = _dataGeneratorService.GenerateResource(2, "Resource2", Statuses.Normal, new List()); + var resource3 = _dataGeneratorService.GenerateResource(3, "Resource3", Statuses.Normal, new List()); var seedResources = new List() { resource1, diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs index 5d488f1a..55abe016 100644 --- a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -79,16 +79,38 @@ public async Task GetAllTest() [Test] public async Task FindTest() { + // this test verifies that a user can be found using the Identifier criteria + // which is where the token will be stored + + var user2Token = Guid.NewGuid(); + + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", user2Token, "US"); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + + var seedUsers = new List() + { + user1, + user2, + user3 + }; + + await _configService.SeedUsers(seedUsers); + try { - var searchCriteria = new BaseModel(); + var searchCriteria = new BaseModel + { + Identifier = user2Token.ToString() + }; var users = await _usersDataService.FindAsync(searchCriteria); - Assert.AreEqual(users.Count, -1); // should never reach this assertion + Assert.AreEqual(users.Count, 1); // should only find one user + Assert.AreEqual(users.Single().Identifier, user2Token.ToString()); // user's identifier should match the token } - catch (NotImplementedException ex) + catch (Exception ex) { - Assert.That(true, Is.True); // NotImplementedException is the expected result. + Assert.Fail("Exception was thrown during Find() method call."); } } [Test] diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index 6aabec53..c6a60963 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -1,12 +1,21 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using RateLimiter.Data; +using RateLimiter.Data.CodeValues; using RateLimiter.Data.Contexts; using RateLimiter.Data.Interfaces; using RateLimiter.Data.Models; using RateLimiter.Data.Repositories; using RateLimiter.Data.Services; +using RateLimiter.Interfaces; +using RateLimiter.Services; +using RateLimiter.Tests.Interfaces; +using RateLimiter.Tests.Services; using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace RateLimiter.Tests; @@ -15,8 +24,12 @@ public class LimiterServiceTest { ServiceProvider _serviceProvider; - [SetUp] - public void SetUp() + private readonly ILimiterService _limiterService; + private readonly IDataGeneratorService _dataGeneratorService; + private readonly IConfigService _configService; + private readonly IDataService _requestDataService; + + public LimiterServiceTest() { var services = new ServiceCollection(); @@ -25,20 +38,57 @@ public void SetUp() services.AddScoped, RequestsDataService>(); services.AddScoped, ResourcesDataService>(); services.AddScoped, UsersDataService>(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); _serviceProvider = services.BuildServiceProvider(); + _dataGeneratorService = _serviceProvider.GetService(); + _configService = _serviceProvider.GetService(); + _requestDataService = _serviceProvider.GetService >(); + _limiterService = _serviceProvider.GetService(); + } + [SetUp] + public void SetUp() + { + // do I want to clear the db for each test? probably... } [Test] - public void AllowAccess_Test_1() + public async Task AllowAccess_Test_1() { var requestsDataService = _serviceProvider.GetService>(); try { - Assert.That(true, Is.True); // currently, all requests are allowed + var user = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var users = new List { user }; + await _configService.SeedUsers(users); + + var limiterRules = new List(); + var resource = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, limiterRules); + var resources = new List { resource }; + await _configService.SeedResources(resources); + + // handle 10 requests. all should be allowed through. + for (int i = 1; i <= 10; i++) + { + var newRequest = _dataGeneratorService.GenerateRequest(i, resource, user, "Request1", null); + + var result = await _limiterService.AllowAccess(newRequest); + Assert.IsTrue(result); + + var request = await _requestDataService.SingleOrDefaultAsync(i); + Assert.IsNotNull(request); + Assert.AreEqual(request.WasHandled, true); + } + + var requests = await _requestDataService.GetAllAsync(); + + Assert.IsNotNull(requests); + Assert.AreEqual(requests.Count(), 10); } - catch (NotImplementedException ex) + catch (Exception ex) { Assert.That(false, Is.True); } diff --git a/RateLimiter/Data/CodeValues.cs b/RateLimiter/Data/CodeValues.cs index 4b397798..62abc497 100644 --- a/RateLimiter/Data/CodeValues.cs +++ b/RateLimiter/Data/CodeValues.cs @@ -1,16 +1,20 @@ using RateLimiter.Data.Models; using System; using System.Collections.Generic; +using System.Linq; -namespace RateLimiter.Data +namespace RateLimiter.Data.CodeValues { - public static class CodeValues + public static class Statuses { - public static List Statuses = new List + public static List Values = new List { new Status { Id = 1, Name = "Normal", Identifier = "Normal", CreatedBy = "SeedUser", CreatedDate = DateTime.Now }, new Status { Id = 2, Name = "Maintenance", Identifier = "Maintenance", CreatedBy = "SeedUser", CreatedDate = DateTime.Now }, new Status { Id = 3, Name = "Offline", Identifier = "Offline", CreatedBy = "SeedUser", CreatedDate = DateTime.Now } }; + public static Status Normal { get { return Values.Single(x => x.Id == 1); } } + public static Status Maintenance { get { return Values.Single(x => x.Id == 2); } } + public static Status Offline { get { return Values.Single(x => x.Id == 3); } } } } diff --git a/RateLimiter/Data/Models/Data/Request.cs b/RateLimiter/Data/Models/Data/Request.cs index f3f5a116..78dd5ec5 100644 --- a/RateLimiter/Data/Models/Data/Request.cs +++ b/RateLimiter/Data/Models/Data/Request.cs @@ -7,7 +7,7 @@ public class Request : BaseModel public int UserId { get; set; } public int ResourceId { get; set; } public DateTime RequestDate { get; set; } - public bool WasHandled { get; set; } + public bool? WasHandled { get; set; } public User User { get; set; } public Resource Resource { get; set; } diff --git a/RateLimiter/Data/Repositories/DbRepository.cs b/RateLimiter/Data/Repositories/DbRepository.cs index 6647ede0..f2077ebb 100644 --- a/RateLimiter/Data/Repositories/DbRepository.cs +++ b/RateLimiter/Data/Repositories/DbRepository.cs @@ -44,7 +44,10 @@ public async Task> FindAsync(BaseModel searchCriteria, string[] inc } } - var entities = await dbSet.Where(x => searchCriteria.CreatedBy == null || x.CreatedBy == searchCriteria.CreatedBy).ToListAsync(); + var entities = await dbSet.Where(x => + (searchCriteria.CreatedBy == null || x.CreatedBy == searchCriteria.CreatedBy) && + (searchCriteria.Identifier == null || x.Identifier == searchCriteria.Identifier) + ).ToListAsync(); return entities; diff --git a/RateLimiter/Data/Services/LimiterRulesDataService.cs b/RateLimiter/Data/Services/LimiterRulesDataService.cs index 56ffbf0c..d8579483 100644 --- a/RateLimiter/Data/Services/LimiterRulesDataService.cs +++ b/RateLimiter/Data/Services/LimiterRulesDataService.cs @@ -26,7 +26,7 @@ public async Task> GetAllAsync() } public async Task> FindAsync(BaseModel searchCriteria) { - throw new NotImplementedException(); + throw new NotImplementedException("Find() is not supported for LimiterRules"); } public async Task SingleAsync(int id) { diff --git a/RateLimiter/Data/Services/RequestsDataService.cs b/RateLimiter/Data/Services/RequestsDataService.cs index 97eac480..a535039b 100644 --- a/RateLimiter/Data/Services/RequestsDataService.cs +++ b/RateLimiter/Data/Services/RequestsDataService.cs @@ -64,11 +64,11 @@ public async Task AddAsync(Request request) } public async Task UpdateAsync(int id, Request request) { - throw new NotImplementedException("Cannot update a request."); + throw new NotImplementedException("Update() is not supported for Requests."); } public async Task RemoveAsync(int id) { - throw new NotImplementedException("Cannot remove a request."); + throw new NotImplementedException("Remove() is not supported for Requests."); } } } diff --git a/RateLimiter/Data/Services/ResourcesDataService.cs b/RateLimiter/Data/Services/ResourcesDataService.cs index 44aafe68..943a5679 100644 --- a/RateLimiter/Data/Services/ResourcesDataService.cs +++ b/RateLimiter/Data/Services/ResourcesDataService.cs @@ -26,7 +26,7 @@ public async Task> GetAllAsync() } public async Task> FindAsync(BaseModel searchCriteria) { - throw new NotImplementedException(); + throw new NotImplementedException("Find() is not supported for Resources."); } public async Task SingleAsync(int id) { diff --git a/RateLimiter/Data/Services/UsersDataService.cs b/RateLimiter/Data/Services/UsersDataService.cs index ccbef705..90f60d79 100644 --- a/RateLimiter/Data/Services/UsersDataService.cs +++ b/RateLimiter/Data/Services/UsersDataService.cs @@ -26,7 +26,11 @@ public async Task> GetAllAsync() } public async Task> FindAsync(BaseModel searchCriteria) { - throw new NotImplementedException(); + string[] includes = new string[] { "" }; + + var users = await _usersRepository.FindAsync(searchCriteria, includes); + + return users; } public async Task SingleAsync(int id) { diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs index 08e4a5a2..3ab89c9f 100644 --- a/RateLimiter/Services/LimiterService.cs +++ b/RateLimiter/Services/LimiterService.cs @@ -1,4 +1,5 @@ using RateLimiter.Data; +using RateLimiter.Data.CodeValues; using RateLimiter.Data.Interfaces; using RateLimiter.Data.Models; using RateLimiter.Interfaces; @@ -33,7 +34,7 @@ public async Task AllowAccess(Request request) { allowAccess = true; // no rules in place so allow access } - if (resource.StatusId == CodeValues.Statuses.Single(x => x.Name == "Offline").Id) + if (resource.StatusId == Statuses.Offline.Id) { allowAccess = false; // resource is offline - unclear if this is the proper place for this logic... } @@ -54,7 +55,7 @@ public async Task AllowAccess(Request request) { // if a limiter rule is in effect, then evaluate it here var userRequests = await _requestsDataService.FindAsync(new BaseModel { CreatedBy = user.Name }); - var userResourceRequests = userRequests.Where(x => x.ResourceId == resource.Id && x.WasHandled).ToList(); + var userResourceRequests = userRequests.Where(x => x.ResourceId == resource.Id && x.WasHandled == true).ToList(); var numRequests = userResourceRequests.Where(x => x.CreatedDate.AddSeconds(limiterRule.NumSeconds) > DateTime.Now).Count(); @@ -64,7 +65,13 @@ public async Task AllowAccess(Request request) } } - // Record the request + // Record the request + // + // Note that in production, the request auditing would probably be independent of + // the limiter service and the limiter service would likely use that body + // of data in its logic. however, for this application, requests are only + // recorded to support the limiter service so it lives here. + request.WasHandled = allowAccess; await _requestsDataService.AddAsync(request); From 8c8d220bc88188b5c5bdf7938d03e7a07708dee6 Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Mon, 18 Nov 2024 06:40:38 -0700 Subject: [PATCH 7/9] Middleware --- RateLimiter/RateLimiterMiddleware.cs | 74 +++++++++++++++++++++------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/RateLimiter/RateLimiterMiddleware.cs b/RateLimiter/RateLimiterMiddleware.cs index cc12ff3b..be2c3b5f 100644 --- a/RateLimiter/RateLimiterMiddleware.cs +++ b/RateLimiter/RateLimiterMiddleware.cs @@ -3,10 +3,15 @@ using RateLimiter.Data.Models; using RateLimiter.Interfaces; using System; +using System.Linq; +using System.Security.Authentication; using System.Threading.Tasks; namespace RateLimiter { + // this is the class that will be used in an ASP.NET Core Middleware pipeline + // allow access or not based on the logic in the LimiterService. Not sure how to test it. + public class RequestLimiterMiddleware { private readonly RequestDelegate _next; @@ -24,25 +29,10 @@ public RequestLimiterMiddleware(RequestDelegate next, ILimiterService limiterSer public async Task InvokeAsync(HttpContext context) { - var resourceName = context.Request.Path; - var resource = await _resourcesDataService.SingleAsync(resourceName); // assumes Identifier matches the pathname of the resource - - var jeff = context.Request.Headers; // Need to pull token from header and match to user token - var username = "jeff"; + var resource = await GetResource(context.Request.Path); + var user = await GetUser(); - var user = await _userDataService.SingleAsync(username); - - var request = new Request - { - Identifier = Guid.NewGuid().ToString(), - RequestDate = DateTime.Now, - UserId = user.Id, - User = user, - ResourceId = resource.Id, - Resource = resource , - CreatedBy = "jeff", - CreatedDate = DateTime.UtcNow - }; + var request = CreateRequest(resource, user); try { @@ -50,7 +40,7 @@ public async Task InvokeAsync(HttpContext context) if (proceedToNext) { - await _next(context); // Call the next middleware component in the pipeline. + await _next(context); // If the limiter allows access, then call the next middleware component in the pipeline. } } catch @@ -58,5 +48,51 @@ public async Task InvokeAsync(HttpContext context) throw; // Ensure exceptions are propagated. } } + protected async Task GetResource(string resourceName) + { + var resource = await _resourcesDataService.SingleAsync(resourceName); // assumes Identifier matches the pathname of the resource + + return resource; + } + protected async Task GetUser() + { + // Identify the user. A token must exist in the Bearer header of the context.Request.Headers collection. + // Need to write a service to handle this part. + + var userToken = "[Token pulled from header]"; // context.Request.Headers; + var searchCriteria = new BaseModel + { + Identifier = userToken + }; + var users = await _userDataService.FindAsync(searchCriteria); + if (users == null) + { + throw new AuthenticationException("Unknown user. Please authenticate."); + } + if (users.Count > 1) + { + throw new AuthenticationException("Multiple users found with the same authentication token. This should be impossible."); + } + + var user = users.Single(); + + return user; + } + protected Request CreateRequest(Resource resource, User user) + { + var request = new Request + { + Identifier = Guid.NewGuid().ToString(), + RequestDate = DateTime.Now, + UserId = user.Id, + User = user, + ResourceId = resource.Id, + Resource = resource, + CreatedBy = user.Name, + CreatedDate = DateTime.UtcNow + }; + + return request; + } } } \ No newline at end of file From e18c595db13c6bbed38f91c2180259692f4edc8a Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Thu, 21 Nov 2024 08:51:16 -0500 Subject: [PATCH 8/9] All tests work --- .../Interfaces/IDataGeneratorService.cs | 4 +- .../Services/DataGeneratorService.cs | 10 +- .../Tests/Data/LimiterRulesDataServiceTest.cs | 32 ++-- .../Tests/Data/RequestsDataServiceTest.cs | 12 +- .../Tests/Data/ResourcesDataServiceTest.cs | 2 +- .../Tests/Data/UsersDataServiceTest.cs | 38 ++--- .../Tests/RateLimiter/LimiterServiceTest.cs | 154 +++++++++++++++++- RateLimiter/Data/Models/Data/LimiterRule.cs | 5 +- RateLimiter/Data/Models/Data/User.cs | 1 + RateLimiter/Data/Repositories/DbRepository.cs | 5 +- RateLimiter/Data/Services/UsersDataService.cs | 1 + RateLimiter/RateLimiter.csproj | 4 +- RateLimiter/Services/LimiterService.cs | 68 ++++++-- 13 files changed, 270 insertions(+), 66 deletions(-) diff --git a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs index 379d035b..a23fb36b 100644 --- a/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs +++ b/RateLimiter.Tests/Interfaces/IDataGeneratorService.cs @@ -8,7 +8,7 @@ public interface IDataGeneratorService { public Request GenerateRequest(int id, Resource resource, User user, string identifier, bool? wasHandled); public Resource GenerateResource(int id, string name, Status status, List limiterRules); - public User GenerateUser(int id, string name, Guid token, string tokenSource); - public LimiterRule GenerateLimiterRule(int id, string name, string? tokenSource, int? resourceStatusId, int numPerTimespan, int numSeconds); + public User GenerateUser(int id, string name, Guid token, string tokenSource, bool isPriorityUser); + public LimiterRule GenerateLimiterRule(int id, string name, string? tokenSource, int? resourceStatusId, int? numPerTimespan, int? numSeconds, bool? isPriorityUser); } } diff --git a/RateLimiter.Tests/Services/DataGeneratorService.cs b/RateLimiter.Tests/Services/DataGeneratorService.cs index a04e4d97..50854cae 100644 --- a/RateLimiter.Tests/Services/DataGeneratorService.cs +++ b/RateLimiter.Tests/Services/DataGeneratorService.cs @@ -20,7 +20,7 @@ public Request GenerateRequest(int id, Resource resource, User user, string iden UserId = user.Id, User = user, WasHandled = wasHandled, - CreatedBy = "DataGenerator", + CreatedBy = user.Name, CreatedDate = DateTime.Now }; @@ -42,7 +42,7 @@ public Resource GenerateResource(int id, string name, Status status, List() { @@ -94,9 +94,9 @@ public async Task FindTest() [Test] public async Task GetByIdTest() { - var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); - var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); - var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15, null); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5, null); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10, null); var seedLimiterRules = new List() { @@ -121,9 +121,9 @@ public async Task GetByIdTest() [Test] public async Task GetByIdentifierTest() { - var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); - var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); - var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15, null); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5, null); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10, null); var seedLimiterRules = new List() { @@ -148,7 +148,7 @@ public async Task GetByIdentifierTest() [Test] public async Task AddTest() { - var limiterRuleToAdd = _dataGeneratorService.GenerateLimiterRule(100, "US Users", "US", null, 3, 15); + var limiterRuleToAdd = _dataGeneratorService.GenerateLimiterRule(100, "US Users", "US", null, 3, 15, null); try { @@ -166,9 +166,9 @@ public async Task AddTest() [Test] public async Task UpdateTest() { - var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); - var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); - var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15, null); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5, null); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10, null); var seedLimiterRules = new List() { @@ -197,9 +197,9 @@ public async Task UpdateTest() [Test] public async Task RemoveTest() { - var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15); - var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5); - var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10); + var limiterRule1 = _dataGeneratorService.GenerateLimiterRule(1, "US Users", "US", null, 3, 15, null); + var limiterRule2 = _dataGeneratorService.GenerateLimiterRule(2, "EU Users", "EU", null, 1, 5, null); + var limiterRule3 = _dataGeneratorService.GenerateLimiterRule(3, "CN Users", "CN", null, 2, 10, null); var seedLimiterRules = new List() { diff --git a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs index c4f3e8d8..c3a1324b 100644 --- a/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/RequestsDataServiceTest.cs @@ -65,8 +65,8 @@ public async Task SetUp() await _configService.SeedResources(seedResources); - user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); + user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); var seedUsers = new List() { user1, @@ -126,16 +126,16 @@ public async Task FindTest() var searchCriteria = new BaseModel { CreatedBy = "FredSmith" }; var requests = await _requestsDataService.FindAsync(searchCriteria); - Assert.AreEqual(requests.Count, 0); // should never reach this assertion + Assert.AreEqual(requests.Count, 0); - searchCriteria = new BaseModel { CreatedBy = "DataGenerator" }; + searchCriteria = new BaseModel { CreatedBy = "User1" }; requests = await _requestsDataService.FindAsync(searchCriteria); - Assert.AreEqual(requests.Count, 3); // should never reach this assertion + Assert.AreEqual(requests.Count, 3); } catch (Exception ex) { - Assert.That(true, Is.True); // NotImplementedException is the expected result. + Assert.That(true, Is.False); } } [Test] diff --git a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs index de235420..e72c900b 100644 --- a/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/ResourcesDataServiceTest.cs @@ -154,7 +154,7 @@ public async Task GetByIdentifierTest() public async Task AddTest() { var resourceToAdd = _dataGeneratorService.GenerateResource(1, "ResourceToAdd", Statuses.Normal, new List()); - var limiterRule = _dataGeneratorService.GenerateLimiterRule(1, "Maintenance", null, Statuses.Maintenance.Id, 1, 5); + var limiterRule = _dataGeneratorService.GenerateLimiterRule(1, "Maintenance", null, Statuses.Maintenance.Id, 1, 5, null); resourceToAdd.LimiterRules = new List { limiterRule }; try diff --git a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs index 55abe016..b8f2c91f 100644 --- a/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs +++ b/RateLimiter.Tests/Tests/Data/UsersDataServiceTest.cs @@ -52,9 +52,9 @@ public async Task SetUp() [Test] public async Task GetAllTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US", false); var seedUsers = new List() { @@ -84,9 +84,9 @@ public async Task FindTest() var user2Token = Guid.NewGuid(); - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", user2Token, "US"); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", user2Token, "US", false); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US", false); var seedUsers = new List() { @@ -116,9 +116,9 @@ public async Task FindTest() [Test] public async Task GetByIdTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US", false); var seedUsers = new List() { @@ -143,9 +143,9 @@ public async Task GetByIdTest() [Test] public async Task GetByIdentifierTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US", false); var seedUsers = new List() { @@ -170,7 +170,7 @@ public async Task GetByIdentifierTest() [Test] public async Task AddTest() { - var userToAdd = _dataGeneratorService.GenerateUser(100, "UserToAdd", Guid.NewGuid(), "US"); + var userToAdd = _dataGeneratorService.GenerateUser(100, "UserToAdd", Guid.NewGuid(), "US", false); try { @@ -188,9 +188,9 @@ public async Task AddTest() [Test] public async Task UpdateTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US", false); var seedUsers = new List() { @@ -220,9 +220,9 @@ public async Task UpdateTest() public async Task RemoveTest() { - var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); - var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US"); - var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US"); + var user1 = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); + var user2 = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); + var user3 = _dataGeneratorService.GenerateUser(3, "User3", Guid.NewGuid(), "US", false ); var seedUsers = new List() { diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index c6a60963..9d37fbaa 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -15,7 +15,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using System.Timers; namespace RateLimiter.Tests; @@ -47,21 +49,23 @@ public LimiterServiceTest() _configService = _serviceProvider.GetService(); _requestDataService = _serviceProvider.GetService >(); _limiterService = _serviceProvider.GetService(); + } + private static System.Timers.Timer aTimer; [SetUp] public void SetUp() { - // do I want to clear the db for each test? probably... + _configService.Reset(); } [Test] - public async Task AllowAccess_Test_1() + public async Task AllowAccess_Test_1_NoLimiterRules() { var requestsDataService = _serviceProvider.GetService>(); try { - var user = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US"); + var user = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", false); var users = new List { user }; await _configService.SeedUsers(users); @@ -93,4 +97,148 @@ public async Task AllowAccess_Test_1() Assert.That(false, Is.True); } } + + [Test] + public async Task AllowAccess_Test_2_PriorityUser() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + // Create users + var priorityUser = _dataGeneratorService.GenerateUser(1, "User1", Guid.NewGuid(), "US", true); + + var users = new List + { + priorityUser + }; + await _configService.SeedUsers(users); + + // Create a several limiter rules + var priorityUserRule = _dataGeneratorService.GenerateLimiterRule(id: 1, name: "Priority User", tokenSource: null, resourceStatusId: null, numPerTimespan: null, numSeconds: null, isPriorityUser: true); + var resourceMaintenanceRule = _dataGeneratorService.GenerateLimiterRule(id: 2, name: "Resource Maintenance", tokenSource: null, resourceStatusId: Statuses.Maintenance.Id, numPerTimespan: 10, numSeconds: null, isPriorityUser: null); + var usUserRule = _dataGeneratorService.GenerateLimiterRule(id: 3, name: "US User", tokenSource: "US", resourceStatusId: null, numPerTimespan: 5, numSeconds: 60, isPriorityUser: null); + var euUserRule = _dataGeneratorService.GenerateLimiterRule(id: 4, name: "EU User", tokenSource: "EU", resourceStatusId: null, numPerTimespan: null, numSeconds: 20, isPriorityUser: null); + + var limiterRules = new List + { + priorityUserRule , + resourceMaintenanceRule , + usUserRule , + euUserRule + }; + + // Create a resource with limiter rules + var resource = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, limiterRules); + + var resources = new List + { + resource + }; + await _configService.SeedResources(resources); + + // make 120 requests for the priority user against the resource. all should be allowed.. + for (int i = 1; i <= 120; i++) + { + var newRequest = _dataGeneratorService.GenerateRequest(i, resource, priorityUser, "Request1", null); + + var result = await _limiterService.AllowAccess(newRequest); + Assert.IsTrue(result); + + var request = await _requestDataService.SingleOrDefaultAsync(i); + Assert.IsNotNull(request); + Assert.AreEqual(request.WasHandled, true); + + } + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } + [Test] + public async Task AllowAccess_Test_3_USUser() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var usUser = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "US", false); + + var users = new List + { + usUser + }; + await _configService.SeedUsers(users); + + // Create a several limiter rules + var priorityUserRule = _dataGeneratorService.GenerateLimiterRule(id: 1, name: "Priority User", tokenSource: null, resourceStatusId: null, numPerTimespan: null, numSeconds: null, isPriorityUser: true); + var resourceMaintenanceRule = _dataGeneratorService.GenerateLimiterRule(id: 2, name: "Resource Maintenance", tokenSource: null, resourceStatusId: Statuses.Maintenance.Id, numPerTimespan: 10, numSeconds: null, isPriorityUser: null); + var usUserRule = _dataGeneratorService.GenerateLimiterRule(id: 3, name: "US User", tokenSource: "US", resourceStatusId: null, numPerTimespan: 5, numSeconds: 10, isPriorityUser: null); + var euUserRule = _dataGeneratorService.GenerateLimiterRule(id: 4, name: "EU User", tokenSource: "EU", resourceStatusId: null, numPerTimespan: null, numSeconds: 5, isPriorityUser: null); + + var limiterRules = new List + { + priorityUserRule , + resourceMaintenanceRule , + usUserRule , + euUserRule + }; + + // Create a resource with limiter rules + var resource = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, limiterRules); + + var resources = new List + { + resource + }; + await _configService.SeedResources(resources); + + // iterate 120 times and create a request for each of the users + for (int i = 1; i <= 120; i++) + { + if (i == 6) + { + string jeff = ""; + } + var newRequest = _dataGeneratorService.GenerateRequest(i, resource, usUser, "Request1", null); + + var result = await _limiterService.AllowAccess(newRequest); + + if (i <= 5) + { + Assert.IsTrue(result); + } + else + { + Assert.IsFalse(result); + } + + } + Thread.Sleep(10000); // wait 10 seconds + + // iterate another 120 times and create a request for each of the users + for (int i = 121; i <= 240; i++) + { + var newRequest = _dataGeneratorService.GenerateRequest(i, resource, usUser, "Request1", null); + + var result = await _limiterService.AllowAccess(newRequest); + + if (i <= 125) + { + Assert.IsTrue(result); + } + else + { + Assert.IsFalse(result); + } + + } + + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } } diff --git a/RateLimiter/Data/Models/Data/LimiterRule.cs b/RateLimiter/Data/Models/Data/LimiterRule.cs index c6bb9012..98315ccf 100644 --- a/RateLimiter/Data/Models/Data/LimiterRule.cs +++ b/RateLimiter/Data/Models/Data/LimiterRule.cs @@ -6,11 +6,12 @@ public class LimiterRule : BaseModel // conditions to look for public string? TokenSource { get; set; } + public bool? IsPriorityUser { get; set; } public int? ResourceStatusId { get; set; } // limiter - public int NumPerTimespan { get; set; } - public int NumSeconds { get; set; } + public int? NumPerTimespan { get; set; } + public int? NumSeconds { get; set; } } } diff --git a/RateLimiter/Data/Models/Data/User.cs b/RateLimiter/Data/Models/Data/User.cs index 2b5db3c1..77cd9784 100644 --- a/RateLimiter/Data/Models/Data/User.cs +++ b/RateLimiter/Data/Models/Data/User.cs @@ -6,5 +6,6 @@ public class User : BaseModel public string Email { get; set; } public string Token { get; set; } public string TokenSource { get; set; } + public bool IsPriorityUser { get; set; } = false; } } diff --git a/RateLimiter/Data/Repositories/DbRepository.cs b/RateLimiter/Data/Repositories/DbRepository.cs index f2077ebb..d0cc7b34 100644 --- a/RateLimiter/Data/Repositories/DbRepository.cs +++ b/RateLimiter/Data/Repositories/DbRepository.cs @@ -43,11 +43,12 @@ public async Task> FindAsync(BaseModel searchCriteria, string[] inc dbSet = dbSet.Include(include); } } + var entitiesData = await dbSet.ToListAsync(); - var entities = await dbSet.Where(x => + var entities = entitiesData.Where(x => (searchCriteria.CreatedBy == null || x.CreatedBy == searchCriteria.CreatedBy) && (searchCriteria.Identifier == null || x.Identifier == searchCriteria.Identifier) - ).ToListAsync(); + ).ToList(); return entities; diff --git a/RateLimiter/Data/Services/UsersDataService.cs b/RateLimiter/Data/Services/UsersDataService.cs index 90f60d79..288ebbcd 100644 --- a/RateLimiter/Data/Services/UsersDataService.cs +++ b/RateLimiter/Data/Services/UsersDataService.cs @@ -71,6 +71,7 @@ public async Task UpdateAsync(int id, User user) existingUser.Name = user.Name; existingUser.Email = user.Email; existingUser.Token = user.Token; + existingUser.IsPriorityUser = user.IsPriorityUser; existingUser.UpdatedBy = user.UpdatedBy; existingUser.UpdatedDate = DateTime.Now; diff --git a/RateLimiter/RateLimiter.csproj b/RateLimiter/RateLimiter.csproj index 123660e2..bb5184a0 100644 --- a/RateLimiter/RateLimiter.csproj +++ b/RateLimiter/RateLimiter.csproj @@ -5,7 +5,9 @@ enable - + + + diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs index 3ab89c9f..257b87b2 100644 --- a/RateLimiter/Services/LimiterService.cs +++ b/RateLimiter/Services/LimiterService.cs @@ -24,13 +24,13 @@ public LimiterService(IDataService requestsDataService, IDataService AllowAccess(Request request) { - // By default, access allowed to the resource - bool allowAccess = true; + // By default, access is denied to the resource + bool allowAccess = false; var resource = await _resourcesDataService.SingleAsync(request.ResourceId); var user = await _usersDataSource.SingleAsync(request.UserId); - if (resource.LimiterRules == null) + if (resource.LimiterRules == null || resource.LimiterRules.Count == 0) { allowAccess = true; // no rules in place so allow access } @@ -39,29 +39,75 @@ public async Task AllowAccess(Request request) allowAccess = false; // resource is offline - unclear if this is the proper place for this logic... } - // Identify the rule to use + // Identify the rule to use - the first one in the LimiterRules collection that evaluates to true will be the one in effect LimiterRule limiterRule = new LimiterRule { Id = -1, Name = "No rule in effect", NumSeconds = 0, NumPerTimespan = 0 }; foreach (var rule in resource.LimiterRules) { - if (rule.TokenSource == null || rule.TokenSource == user.TokenSource) + if (rule.IsPriorityUser == true && user.IsPriorityUser) { limiterRule = rule; + break; + } + else if (rule.TokenSource != null && rule.TokenSource == user.TokenSource) // does user token match the rule + { + limiterRule = rule; + break; + } + else if (rule.ResourceStatusId == Statuses.Maintenance.Id && resource.StatusId == Statuses.Maintenance.Id) + { + limiterRule = rule; + break; } } // If a rule is in effect for the request, evaluate it if (limiterRule.Id > 0) { - // if a limiter rule is in effect, then evaluate it here - var userRequests = await _requestsDataService.FindAsync(new BaseModel { CreatedBy = user.Name }); + // All rules are based on the number of requests that were allowed in the past so + // so they must be retrieved. I did not implement a Search() method for the + // Requests collections - only a Find() which uses the base model to specify search criteria. + var userRequests = await _requestsDataService.FindAsync(new BaseModel { CreatedBy = user.Name }); var userResourceRequests = userRequests.Where(x => x.ResourceId == resource.Id && x.WasHandled == true).ToList(); - var numRequests = userResourceRequests.Where(x => x.CreatedDate.AddSeconds(limiterRule.NumSeconds) > DateTime.Now).Count(); + if (limiterRule.NumSeconds == null && limiterRule.NumPerTimespan == null) // a rule with no limiter + { + allowAccess = true; + } + else if (limiterRule.NumSeconds == null) + { + var numPerTimespan = limiterRule.NumPerTimespan ?? 0; + // the previous NumPerTimespan - 1 requests must all be unhandled before the limiter will allow the request. + var latestRequests = userRequests.OrderByDescending(x => x.CreatedDate).Take(numPerTimespan-1).Where(x => x.WasHandled == true).ToList(); - if (numRequests > limiterRule.NumPerTimespan) + if (latestRequests.Count() == 0) + { + allowAccess = true; + } + } + else if (limiterRule.NumPerTimespan == null) + { + // implies the user must wait the number of seconds specified. so only allow access after the specified number of seconds have + var numSeconds = limiterRule.NumSeconds ?? 0; + + var latestRequest = userRequests.OrderByDescending(x => x.CreatedDate).Where(x => x.CreatedDate.AddSeconds(numSeconds) < DateTime.Now).FirstOrDefault(); + + if (latestRequest != null) + { + allowAccess = true; + } + } + else { - allowAccess = false; + // implies the number of requests in + var numSeconds = limiterRule.NumSeconds ?? 0; + var latestRequests = userRequests.OrderByDescending(x => x.CreatedDate).Where(x => x.CreatedDate.AddSeconds(numSeconds) > DateTime.Now); + + if (latestRequests.Count() < limiterRule.NumPerTimespan) + { + allowAccess = true; + } + } } @@ -75,6 +121,8 @@ public async Task AllowAccess(Request request) request.WasHandled = allowAccess; await _requestsDataService.AddAsync(request); + var jeff = await _requestsDataService.GetAllAsync(); + return allowAccess; } } From 2c7b57e877a5da621db24e04a9ba824a32c7e81e Mon Sep 17 00:00:00 2001 From: Jeff Campbell Date: Thu, 21 Nov 2024 11:53:52 -0500 Subject: [PATCH 9/9] I think thats all. --- .../Tests/RateLimiter/LimiterServiceTest.cs | 85 ++++++++++++++++++- RateLimiter/Services/LimiterService.cs | 12 --- 2 files changed, 81 insertions(+), 16 deletions(-) diff --git a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs index 9d37fbaa..dbbcfd77 100644 --- a/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs +++ b/RateLimiter.Tests/Tests/RateLimiter/LimiterServiceTest.cs @@ -197,10 +197,6 @@ public async Task AllowAccess_Test_3_USUser() // iterate 120 times and create a request for each of the users for (int i = 1; i <= 120; i++) { - if (i == 6) - { - string jeff = ""; - } var newRequest = _dataGeneratorService.GenerateRequest(i, resource, usUser, "Request1", null); var result = await _limiterService.AllowAccess(newRequest); @@ -241,4 +237,85 @@ public async Task AllowAccess_Test_3_USUser() Assert.That(false, Is.True); } } + [Test] + public async Task AllowAccess_Test_4_euUser() + { + var requestsDataService = _serviceProvider.GetService>(); + + try + { + var usUser = _dataGeneratorService.GenerateUser(2, "User2", Guid.NewGuid(), "EU", false); + + var users = new List + { + usUser + }; + await _configService.SeedUsers(users); + + // Create a several limiter rules + var priorityUserRule = _dataGeneratorService.GenerateLimiterRule(id: 1, name: "Priority User", tokenSource: null, resourceStatusId: null, numPerTimespan: null, numSeconds: null, isPriorityUser: true); + var resourceMaintenanceRule = _dataGeneratorService.GenerateLimiterRule(id: 2, name: "Resource Maintenance", tokenSource: null, resourceStatusId: Statuses.Maintenance.Id, numPerTimespan: 10, numSeconds: null, isPriorityUser: null); + var usUserRule = _dataGeneratorService.GenerateLimiterRule(id: 3, name: "US User", tokenSource: "US", resourceStatusId: null, numPerTimespan: 5, numSeconds: 10, isPriorityUser: null); + var euUserRule = _dataGeneratorService.GenerateLimiterRule(id: 4, name: "EU User", tokenSource: "EU", resourceStatusId: null, numPerTimespan: 1, numSeconds: 10, isPriorityUser: null); + + var limiterRules = new List + { + priorityUserRule , + resourceMaintenanceRule , + usUserRule , + euUserRule + }; + + // Create a resource with limiter rules + var resource = _dataGeneratorService.GenerateResource(1, "Resource1", Statuses.Normal, limiterRules); + + var resources = new List + { + resource + }; + await _configService.SeedResources(resources); + + // iterate 120 times and create a request for each of the users + for (int i = 1; i <= 120; i++) + { + var newRequest = _dataGeneratorService.GenerateRequest(i, resource, usUser, "Request1", null); + + var result = await _limiterService.AllowAccess(newRequest); + + if (i == 1) + { + Assert.IsTrue(result); + } + else + { + Assert.IsFalse(result); + } + + } + Thread.Sleep(10000); // wait 5 seconds + + // iterate another 120 times and create a request for each of the users + for (int i = 121; i <= 240; i++) + { + var newRequest = _dataGeneratorService.GenerateRequest(i, resource, usUser, "Request1", null); + + var result = await _limiterService.AllowAccess(newRequest); + + if (i == 121) + { + Assert.IsTrue(result); + } + else + { + Assert.IsFalse(result); + } + + } + + } + catch (Exception ex) + { + Assert.That(false, Is.True); + } + } } diff --git a/RateLimiter/Services/LimiterService.cs b/RateLimiter/Services/LimiterService.cs index 257b87b2..be4f07d1 100644 --- a/RateLimiter/Services/LimiterService.cs +++ b/RateLimiter/Services/LimiterService.cs @@ -85,18 +85,6 @@ public async Task AllowAccess(Request request) allowAccess = true; } } - else if (limiterRule.NumPerTimespan == null) - { - // implies the user must wait the number of seconds specified. so only allow access after the specified number of seconds have - var numSeconds = limiterRule.NumSeconds ?? 0; - - var latestRequest = userRequests.OrderByDescending(x => x.CreatedDate).Where(x => x.CreatedDate.AddSeconds(numSeconds) < DateTime.Now).FirstOrDefault(); - - if (latestRequest != null) - { - allowAccess = true; - } - } else { // implies the number of requests in