Skip to content

Commit 7388070

Browse files
authored
Feature/list assigned election round form templates (#889)
* adding ElectionRoundFormTemplate #1 Creating entity and starting endpoint * Update .gitignore ignoring /api/src/Vote.Monitor.Api/Uploads * assign templates to election round - endpoint#1 assign templates to election round - endpoint with tests #1 * adding AssignTemplates tests for DeleteRangeAsync and Validator #2 * adjust for ElectionRoundFormTemplateAssignment #3 updates on assigning templates on an ElectionRound consisting of tests, endpoint and configuration files * adjust testValidators ElectionRoundFormTemplateAssignment #4 * Add ListAssignedFormTemplates for NGO Admins #1 #878 - adding new endpoint in project Feature.FormTemplates GET /api/form-templates/election-rounds/{electionRoundId}:available - adding unit testing * fix failing build * deleting redundant IF block IF block which checks if there are no assignments and returns an empty response * fixing bug platformAdmin NotFound fixed platformAdmin issue
1 parent bf1c335 commit 7388070

File tree

8 files changed

+478
-2
lines changed

8 files changed

+478
-2
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Authorization.Policies.Requirements;
2+
using Feature.FormTemplates.Specifications;
3+
using Microsoft.AspNetCore.Authorization;
4+
using Vote.Monitor.Core.Models;
5+
using Vote.Monitor.Core.Services.Security;
6+
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;
7+
8+
namespace Feature.FormTemplates.ListAssignedTemplates;
9+
10+
public class Endpoint(IReadRepository<ElectionRoundFormTemplate> repository,
11+
ICurrentUserRoleProvider userRoleProvider,
12+
IAuthorizationService authorizationService)
13+
: Endpoint<Request, Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>
14+
{
15+
public override void Configure()
16+
{
17+
Get("/api/form-templates/election-rounds/{electionRoundId}:available");
18+
Policies(PolicyNames.AdminsOnly);
19+
}
20+
21+
public override async Task<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>> ExecuteAsync(Request req, CancellationToken ct)
22+
{
23+
var isNgoAdmin = userRoleProvider.IsNgoAdmin();
24+
25+
if (isNgoAdmin)
26+
{
27+
var result = await authorizationService.AuthorizeAsync(User, new NgoAdminRequirement());
28+
29+
if (!result.Succeeded)
30+
{
31+
return TypedResults.NotFound();
32+
}
33+
}
34+
35+
var specification = new ListAssignedFormTemplateSpecification(req);
36+
var assignedFormTemplates = await repository.ListAsync(specification, ct);
37+
var assignedFormTemplatesCount = await repository.CountAsync(specification, ct);
38+
39+
return TypedResults.Ok(new PagedResponse<FormTemplateSlimModel>(assignedFormTemplates,
40+
assignedFormTemplatesCount, req.PageNumber, req.PageSize));
41+
}
42+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using Vote.Monitor.Core.Models;
2+
3+
namespace Feature.FormTemplates.ListAssignedTemplates;
4+
5+
public class Request : BaseSortPaginatedRequest
6+
{
7+
public Guid ElectionRoundId { get; set; }
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Feature.FormTemplates.ListAssignedTemplates;
2+
3+
public class Validator : Validator<Request>
4+
{
5+
public Validator()
6+
{
7+
RuleFor(x => x.ElectionRoundId).NotEmpty();
8+
RuleFor(x => x.PageNumber)
9+
.GreaterThanOrEqualTo(1);
10+
11+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
12+
}
13+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Xml;
2+
using Feature.FormTemplates.ListAssignedTemplates;
3+
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;
4+
using Vote.Monitor.Domain.Specifications;
5+
6+
namespace Feature.FormTemplates.Specifications;
7+
8+
public sealed class ListAssignedFormTemplateSpecification : Specification<ElectionRoundFormTemplate, FormTemplateSlimModel>
9+
{
10+
public ListAssignedFormTemplateSpecification(Request request)
11+
{
12+
Query
13+
.Where(x => x.ElectionRoundId == request.ElectionRoundId)
14+
.Include(x => x.FormTemplate)
15+
.ApplyDefaultOrdering(request)
16+
.Paginate(request);
17+
18+
Query
19+
.Select(x => new FormTemplateSlimModel
20+
{
21+
Id = x.FormTemplate.Id,
22+
Code = x.FormTemplate.Code,
23+
DefaultLanguage = x.FormTemplate.DefaultLanguage,
24+
Name = x.FormTemplate.Name,
25+
Description = x.FormTemplate.Description,
26+
FormType = x.FormTemplate.FormType,
27+
Status = x.FormTemplate.Status,
28+
LastModifiedOn = x.FormTemplate.LastModifiedOn ?? x.FormTemplate.CreatedOn,
29+
Languages = x.FormTemplate.Languages
30+
});
31+
}
32+
}

api/src/Vote.Monitor.Api.Feature.ElectionRound/List/Endpoint.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public override async Task<Results<Ok<PagedResponse<ElectionRoundModel>>, Proble
1515
{
1616
var specification = new ListElectionRoundsSpecification(req);
1717
var electionRounds = await repository.ListAsync(specification, ct);
18-
var electionRoundsCunt = await repository.CountAsync(specification, ct);
18+
var electionRoundsCount = await repository.CountAsync(specification, ct);
1919

20-
return TypedResults.Ok(new PagedResponse<ElectionRoundModel>(electionRounds, electionRoundsCunt, req.PageNumber, req.PageSize));
20+
return TypedResults.Ok(new PagedResponse<ElectionRoundModel>(electionRounds, electionRoundsCount, req.PageNumber, req.PageSize));
2121
}
2222
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System.Security.Claims;
2+
using Authorization.Policies.Requirements;
3+
using Feature.FormTemplates.ListAssignedTemplates;
4+
using Feature.FormTemplates.Specifications;
5+
using Microsoft.AspNetCore.Authorization;
6+
using Vote.Monitor.Core.Models;
7+
using Vote.Monitor.Core.Services.Security;
8+
using Vote.Monitor.Domain.Entities.ElectionRoundFormTemplateAggregate;
9+
10+
namespace Feature.FormTemplates.UnitTests.Endpoints;
11+
12+
public class ListAssignedEndpointTests
13+
{
14+
private readonly IReadRepository<ElectionRoundFormTemplate> _electionRoundFormTemplateRepository;
15+
private readonly ICurrentUserRoleProvider _userRoleProvider;
16+
private readonly IAuthorizationService _authorizationService;
17+
private readonly Endpoint _endpoint;
18+
19+
public ListAssignedEndpointTests()
20+
{
21+
_electionRoundFormTemplateRepository = Substitute.For<IReadRepository<ElectionRoundFormTemplate>>();
22+
_userRoleProvider = Substitute.For<ICurrentUserRoleProvider>();
23+
_authorizationService = Substitute.For<IAuthorizationService>();
24+
_endpoint = Factory.Create<Endpoint>
25+
(_electionRoundFormTemplateRepository,
26+
_userRoleProvider,
27+
_authorizationService);
28+
}
29+
30+
[Fact]
31+
public async Task Should_Return_NotFound_When_NgoAdmin_is_False()
32+
{
33+
// Arrange
34+
_userRoleProvider.IsNgoAdmin().Returns(false);
35+
36+
// Act
37+
var request = new Request();
38+
var result = await _endpoint.ExecuteAsync(request, default);
39+
40+
// Assert
41+
await _authorizationService
42+
.DidNotReceiveWithAnyArgs()
43+
.AuthorizeAsync(
44+
Arg.Any<ClaimsPrincipal>(),
45+
Arg.Any<object?>(),
46+
Arg.Any<IEnumerable<IAuthorizationRequirement>>());
47+
}
48+
49+
[Fact]
50+
public async Task Should_Return_NotFound_When_NgoAdmin_is_True_and_Authorization_Fails()
51+
{
52+
// Arrange
53+
_userRoleProvider.IsNgoAdmin().Returns(true);
54+
_authorizationService.AuthorizeAsync(
55+
Arg.Any<ClaimsPrincipal>(),
56+
Arg.Any<object?>(),
57+
Arg.Any<IEnumerable<IAuthorizationRequirement>>())
58+
.Returns(AuthorizationResult.Failed());
59+
60+
// Act
61+
var request = new Request();
62+
var result = await _endpoint.ExecuteAsync(request, default);
63+
64+
// Assert
65+
result.Should()
66+
.BeOfType<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>()
67+
.Which.Result.Should()
68+
.BeOfType<NotFound>();
69+
}
70+
71+
[Fact]
72+
public async Task Should_Return_Ok_With_Empty_List_When_No_Assigned_Templates()
73+
{
74+
// Arrange
75+
_userRoleProvider.IsNgoAdmin().Returns(true);
76+
77+
_authorizationService.AuthorizeAsync(
78+
Arg.Any<ClaimsPrincipal>(),
79+
Arg.Any<object?>(),
80+
Arg.Any<IEnumerable<IAuthorizationRequirement>>())
81+
.Returns(AuthorizationResult.Success());
82+
83+
_electionRoundFormTemplateRepository
84+
.ListAsync(Arg.Any<ListAssignedFormTemplateSpecification>())
85+
.Returns([]);
86+
87+
// Act
88+
var request = new Request();
89+
var result = await _endpoint.ExecuteAsync(request, default);
90+
91+
// Assert
92+
result.Should()
93+
.BeOfType<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>();
94+
}
95+
96+
[Fact]
97+
public async Task Should_Return_Ok_With_Paginated_List_When_Assigned_Templates_Exist()
98+
{
99+
// Arrange
100+
_userRoleProvider.IsNgoAdmin().Returns(true);
101+
102+
_authorizationService.AuthorizeAsync(
103+
Arg.Any<ClaimsPrincipal>(),
104+
Arg.Any<object?>(),
105+
Arg.Any<IEnumerable<IAuthorizationRequirement>>())
106+
.Returns(AuthorizationResult.Success());
107+
108+
var numberOfFormTemplates = 3;
109+
var totalCount = 154;
110+
var pageSize = 100;
111+
112+
var formTemplates = new FormTemplateSlimModelFaker().Generate(numberOfFormTemplates);
113+
114+
_electionRoundFormTemplateRepository
115+
.ListAsync(Arg.Any<ListAssignedFormTemplateSpecification>())
116+
.Returns(formTemplates);
117+
118+
_electionRoundFormTemplateRepository
119+
.CountAsync(Arg.Any<ListAssignedFormTemplateSpecification>())
120+
.Returns(totalCount);
121+
122+
// Act
123+
var request = new Request { PageSize = pageSize, PageNumber = numberOfFormTemplates };
124+
var result = await _endpoint.ExecuteAsync(request, default);
125+
126+
// Assert
127+
result
128+
.Should().BeOfType<Results<Ok<PagedResponse<FormTemplateSlimModel>>, NotFound>>()
129+
.Which
130+
.Result.Should().BeOfType<Ok<PagedResponse<FormTemplateSlimModel>>>()
131+
.Which.Value.Should().NotBeNull();
132+
133+
var pagedResult = (result.Result as Ok<PagedResponse<FormTemplateSlimModel>>)!.Value!;
134+
135+
pagedResult.PageSize.Should().Be(pageSize);
136+
pagedResult.CurrentPage.Should().Be(numberOfFormTemplates);
137+
pagedResult.TotalCount.Should().Be(totalCount);
138+
pagedResult.Items.Should().BeEquivalentTo(formTemplates);
139+
}
140+
}

0 commit comments

Comments
 (0)