Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a configurable limit to the number of groups a user can create … #105

Merged
merged 7 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Src/DeUrgenta.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void ConfigureServices(IServiceCollection services)

services.AddUserApiServices();
services.AddBackpackApiServices();
services.AddGroupApiServices();
services.AddGroupApiServices(Configuration);
services.AddCertificationsApiServices();
services.AddEventsApiServices();
services.AddAdminApiServices();
Expand Down
4 changes: 4 additions & 0 deletions Src/DeUrgenta.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,9 @@
"Region": "",
"AWS_ACCESS_KEY_ID": "",
"AWS_SECRET_ACCESS_KEY": ""
},
"Group": {
"MaxJoinedGroupsPerUser": 5,
"MaxCreatedGroupsPerUser": 5
}
}
6 changes: 5 additions & 1 deletion Src/DeUrgenta.Group.Api/BootstrappingExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
using DeUrgenta.Common.Validation;
using DeUrgenta.Group.Api.Commands;
using DeUrgenta.Group.Api.Models;
using DeUrgenta.Group.Api.Options;
using DeUrgenta.Group.Api.Queries;
using DeUrgenta.Group.Api.Validators;
using DeUrgenta.Group.Api.Validators.RequestValidators;
using FluentValidation;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace DeUrgenta.Group.Api
{
public static class BootstrappingExtensions
{
public static IServiceCollection AddGroupApiServices(this IServiceCollection services)
public static IServiceCollection AddGroupApiServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<IValidateRequest<AddGroup>, AddGroupValidator>();
services.AddTransient<IValidateRequest<AddSafeLocation>, AddSafeLocationValidator>();
Expand All @@ -29,6 +31,8 @@ public static IServiceCollection AddGroupApiServices(this IServiceCollection ser
services.AddTransient<IValidator<GroupRequest>, GroupRequestValidator>();
services.AddTransient<IValidator<SafeLocationRequest>, SafeLocationRequestValidator>();

services.Configure<GroupsConfig>(configuration.GetSection(GroupsConfig.SectionName));

return services;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public async Task<Result<GroupModel>> Handle(AddGroup request, CancellationToken
}

var user = await _context.Users.FirstAsync(u => u.Sub == request.UserSub, cancellationToken);

var newBackpack = new Backpack
{
Name = $"Backpack for {request.Group.Name}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Threading;
using System.Configuration;
using System.Threading;
using System.Threading.Tasks;
using CSharpFunctionalExtensions;
using DeUrgenta.Common.Validation;
using DeUrgenta.Domain;
using DeUrgenta.Domain.Entities;
using DeUrgenta.Group.Api.Commands;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace DeUrgenta.Group.Api.CommandHandlers
Expand Down
10 changes: 10 additions & 0 deletions Src/DeUrgenta.Group.Api/Options/GroupsConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace DeUrgenta.Group.Api.Options
{
public class GroupsConfig
{
public const string SectionName = "Groups";

public int MaxJoinedGroupsPerUser { get; set; }
public int MaxCreatedGroupsPerUser { get; set; }
}
}
17 changes: 16 additions & 1 deletion Src/DeUrgenta.Group.Api/Validators/AddGroupValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@
using DeUrgenta.Common.Validation;
using DeUrgenta.Domain;
using DeUrgenta.Group.Api.Commands;
using DeUrgenta.Group.Api.Options;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace DeUrgenta.Group.Api.Validators
{
public class AddGroupValidator : IValidateRequest<AddGroup>
{
private readonly DeUrgentaContext _context;
private readonly GroupsConfig _groupsConfig;

public AddGroupValidator(DeUrgentaContext context)
public AddGroupValidator(DeUrgentaContext context, GroupsConfig groupsConfig)
{
_context = context;
_groupsConfig = groupsConfig;
}

public AddGroupValidator(DeUrgentaContext context, IOptions<GroupsConfig> groupsConfig)
{
_context = context;
_groupsConfig = groupsConfig.Value;
}

public async Task<bool> IsValidAsync(AddGroup request)
Expand All @@ -22,6 +32,11 @@ public async Task<bool> IsValidAsync(AddGroup request)
{
return false;
}

if (user.GroupsAdministered.Count >= _groupsConfig.MaxCreatedGroupsPerUser)
{
return false;
}

return true;
}
Expand Down
17 changes: 16 additions & 1 deletion Src/DeUrgenta.Group.Api/Validators/InviteToGroupValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@
using DeUrgenta.Common.Validation;
using DeUrgenta.Domain;
using DeUrgenta.Group.Api.Commands;
using DeUrgenta.Group.Api.Options;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace DeUrgenta.Group.Api.Validators
{
public class InviteToGroupValidator : IValidateRequest<InviteToGroup>
{
private readonly DeUrgentaContext _context;
private readonly GroupsConfig _groupsConfig;

public InviteToGroupValidator(DeUrgentaContext context)
public InviteToGroupValidator(DeUrgentaContext context, GroupsConfig groupsConfig)
{
_context = context;
_groupsConfig = groupsConfig;
}

public InviteToGroupValidator(DeUrgentaContext context, IOptions<GroupsConfig> groupsConfig)
{
_context = context;
_groupsConfig = groupsConfig.Value;
}

public async Task<bool> IsValidAsync(InviteToGroup request)
Expand Down Expand Up @@ -53,6 +63,11 @@ public async Task<bool> IsValidAsync(InviteToGroup request)
{
return false;
}

if (invitedUser.GroupsMember.Count >= _groupsConfig.MaxJoinedGroupsPerUser)
{
return false;
}

return true;
}
Expand Down
4 changes: 2 additions & 2 deletions Src/DeUrgenta.User.Api/Controller/UserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,14 @@ public async Task<ActionResult> DeleteLocationAsync([FromRoute] Guid locationId)
/// </summary>
/// <returns></returns>
[HttpGet("group-invites")]
[SwaggerResponse(StatusCodes.Status200OK, "Get group invites for current user", typeof(IImmutableList<GropInviteModel>))]
[SwaggerResponse(StatusCodes.Status200OK, "Get group invites for current user", typeof(IImmutableList<GroupInviteModel>))]
[SwaggerResponse(StatusCodes.Status400BadRequest, "A business rule was violated", typeof(ProblemDetails))]
[SwaggerResponse(StatusCodes.Status500InternalServerError, "Something bad happened", typeof(ProblemDetails))]

[SwaggerResponseExample(StatusCodes.Status200OK, typeof(GetGroupInvitesResponseExample))]
[SwaggerResponseExample(StatusCodes.Status400BadRequest, typeof(BusinessRuleViolationResponseExample))]
[SwaggerResponseExample(StatusCodes.Status500InternalServerError, typeof(ApplicationErrorResponseExample))]
public async Task<ActionResult<GropInviteModel>> GetGroupInvitesAsync()
public async Task<ActionResult<GroupInviteModel>> GetGroupInvitesAsync()
{
var sub = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
var query = new GetGroupInvites(sub);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace DeUrgenta.User.Api.Models
{
public sealed record GropInviteModel
public sealed record GroupInviteModel
{
public Guid InviteId { get; init; }
public Guid GroupId { get; init; }
Expand Down
2 changes: 1 addition & 1 deletion Src/DeUrgenta.User.Api/Queries/GetGroupInvites.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace DeUrgenta.User.Api.Queries
{
public class GetGroupInvites : IRequest<Result<IImmutableList<GropInviteModel>>>
public class GetGroupInvites : IRequest<Result<IImmutableList<GroupInviteModel>>>
{
public string UserSub { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace DeUrgenta.User.Api.QueryHandlers
{
public class GetGroupInvitesHandler : IRequestHandler<GetGroupInvites, Result<IImmutableList<GropInviteModel>>>
public class GetGroupInvitesHandler : IRequestHandler<GetGroupInvites, Result<IImmutableList<GroupInviteModel>>>
{
private readonly IValidateRequest<GetGroupInvites> _validator;
private readonly DeUrgentaContext _context;
Expand All @@ -23,18 +23,18 @@ public GetGroupInvitesHandler(IValidateRequest<GetGroupInvites> validator, DeUrg
_context = context;
}

public async Task<Result<IImmutableList<GropInviteModel>>> Handle(GetGroupInvites request, CancellationToken cancellationToken)
public async Task<Result<IImmutableList<GroupInviteModel>>> Handle(GetGroupInvites request, CancellationToken cancellationToken)
{
var isValid = await _validator.IsValidAsync(request);
if (!isValid)
{
return Result.Failure<IImmutableList<GropInviteModel>>("Validation failed");
return Result.Failure<IImmutableList<GroupInviteModel>>("Validation failed");
}

var groupInvites = await _context
.GroupInvites
.Where(gi => gi.InvitationReceiver.Sub == request.UserSub)
.Select(x => new GropInviteModel
.Select(x => new GroupInviteModel
{
SenderId = x.InvitationSenderId,
GroupId = x.GroupId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

namespace DeUrgenta.User.Api.Swagger
{
public class GetGroupInvitesResponseExample : IExamplesProvider<IImmutableList<GropInviteModel>>
public class GetGroupInvitesResponseExample : IExamplesProvider<IImmutableList<GroupInviteModel>>
{
public IImmutableList<GropInviteModel> GetExamples()
public IImmutableList<GroupInviteModel> GetExamples()
{
return new List<GropInviteModel>
return new List<GroupInviteModel>
{
new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace DeUrgenta.Group.Api.Tests.CommandsHandlers
public class AddGroupHandlerShould
{
private readonly DeUrgentaContext _dbContext;

public AddGroupHandlerShould(DatabaseFixture fixture)
{
_dbContext = fixture.Context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public async Task Return_failed_result_when_validation_fails()
var sut = new InviteToGroupHandler(validator, _dbContext);

// Act
var result = await sut.Handle(new InviteToGroup("a-sub", Guid.NewGuid(), Guid.NewGuid()), CancellationToken.None);
var result = await sut.Handle(new InviteToGroup("a-sub", Guid.NewGuid(), Guid.NewGuid()),
CancellationToken.None);

// Assert
result.IsFailure.ShouldBeTrue();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Threading.Tasks;
using DeUrgenta.Domain;
using DeUrgenta.Domain.Entities;
using DeUrgenta.Group.Api.Commands;
using DeUrgenta.Group.Api.Models;
using DeUrgenta.Group.Api.Options;
using DeUrgenta.Group.Api.Validators;
using DeUrgenta.Tests.Helpers;
using DeUrgenta.Tests.Helpers.Builders;
Expand All @@ -15,10 +17,12 @@ namespace DeUrgenta.Group.Api.Tests.Validators
public class AddGroupValidatorShould
{
private readonly DeUrgentaContext _dbContext;
private readonly GroupsConfig _groupsConfig;

public AddGroupValidatorShould(DatabaseFixture fixture)
{
_dbContext = fixture.Context;
_groupsConfig = new GroupsConfig {MaxCreatedGroupsPerUser = 5};
}

[Theory]
Expand All @@ -28,7 +32,7 @@ public AddGroupValidatorShould(DatabaseFixture fixture)
public async Task Invalidate_request_when_no_user_found_by_sub(string sub)
{
// Arrange
var sut = new AddGroupValidator(_dbContext);
var sut = new AddGroupValidator(_dbContext, _groupsConfig);

// Act
var isValid = await sut.IsValidAsync(new AddGroup(sub, new GroupRequest()));
Expand All @@ -40,7 +44,7 @@ public async Task Invalidate_request_when_no_user_found_by_sub(string sub)
[Fact]
public async Task Validate_when_user_was_found_by_sub()
{
var sut = new AddGroupValidator(_dbContext);
var sut = new AddGroupValidator(_dbContext, _groupsConfig);

// Arrange
var userSub = Guid.NewGuid().ToString();
Expand All @@ -55,5 +59,33 @@ public async Task Validate_when_user_was_found_by_sub()
// Assert
isValid.ShouldBeTrue();
}

[Fact]
public async Task Invalidate_when_user_exceeds_group_creation_limit()
{
// Arrange
var sut = new AddGroupValidator(_dbContext, _groupsConfig);

// Seed user
var userSub = Guid.NewGuid().ToString();
var user = new UserBuilder().WithSub(userSub).Build();
await _dbContext.SaveChangesAsync();

// Seed groups
for (int i = 0; i < 5; i++)
{
await _dbContext.Groups.AddAsync(new Domain.Entities.Group
{
Name = i.ToString(), Admin = user
});
}
await _dbContext.SaveChangesAsync();

// Act
var result = await sut.IsValidAsync(new AddGroup(user.Sub, new GroupRequest {Name = "TestGroup"}));

// Assert
result.ShouldBeFalse();
}
}
}
Loading