Skip to content

Commit cde2b97

Browse files
authored
Add available ngos for monitoring (#911)
* Add available ngos for monitoring * Add validators for paged requests
1 parent af75e7c commit cde2b97

File tree

12 files changed

+190
-3
lines changed

12 files changed

+190
-3
lines changed

api/src/Feature.CitizenReports/ListEntries/Validator.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ public Validator()
66
{
77
RuleFor(x => x.ElectionRoundId).NotEmpty();
88
RuleFor(x => x.NgoId).NotEmpty();
9+
10+
RuleFor(x => x.PageNumber)
11+
.GreaterThanOrEqualTo(1);
12+
13+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
914
}
10-
}
15+
}

api/src/Feature.Form.Submissions/ListByObserver/Validator.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ public Validator()
77
RuleFor(x => x.ElectionRoundId).NotEmpty();
88
RuleFor(x => x.NgoId).NotEmpty();
99
RuleFor(x => x.DataSource).NotEmpty();
10+
11+
RuleFor(x => x.PageNumber)
12+
.GreaterThanOrEqualTo(1);
13+
14+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
1015
}
1116
}

api/src/Feature.Form.Submissions/ListEntries/Validator.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ public Validator()
77
RuleFor(x => x.ElectionRoundId).NotEmpty();
88
RuleFor(x => x.NgoId).NotEmpty();
99
RuleFor(x => x.DataSource).NotEmpty();
10+
11+
RuleFor(x => x.PageNumber)
12+
.GreaterThanOrEqualTo(1);
13+
14+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
1015
}
1116
}

api/src/Feature.IncidentReports/ListByObserver/Validator.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ public Validator()
66
{
77
RuleFor(x => x.ElectionRoundId).NotEmpty();
88
RuleFor(x => x.NgoId).NotEmpty();
9+
10+
RuleFor(x => x.PageNumber)
11+
.GreaterThanOrEqualTo(1);
12+
13+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
914
}
10-
}
15+
}

api/src/Feature.IncidentReports/ListEntries/Validator.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ public Validator()
66
{
77
RuleFor(x => x.ElectionRoundId).NotEmpty();
88
RuleFor(x => x.NgoId).NotEmpty();
9+
10+
RuleFor(x => x.PageNumber)
11+
.GreaterThanOrEqualTo(1);
12+
13+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
914
}
10-
}
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Feature.Monitoring.ListAvailableNgos;
2+
3+
public class AvailableNgoModel
4+
{
5+
public Guid Id { get; init; }
6+
public string Name { get; init; }
7+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using Authorization.Policies;
2+
using Dapper;
3+
using Vote.Monitor.Core.Models;
4+
using Vote.Monitor.Domain.ConnectionFactory;
5+
using Vote.Monitor.Domain.Specifications;
6+
7+
namespace Feature.Monitoring.ListAvailableNgos;
8+
9+
public class Endpoint(INpgsqlConnectionFactory dbConnectionFactory)
10+
: Endpoint<Request, Results<Ok<PagedResponse<AvailableNgoModel>>, NotFound>>
11+
{
12+
public override void Configure()
13+
{
14+
Get("/api/election-rounds/{electionRoundId}/monitoring-ngos:available");
15+
DontAutoTag();
16+
Options(x => x.WithTags("monitoring"));
17+
Summary(s =>
18+
{
19+
s.Summary = "Lists ngos that have status Activated and currently are not monitoring specified election round";
20+
});
21+
Policies(PolicyNames.PlatformAdminsOnly);
22+
}
23+
24+
public override async Task<Results<Ok<PagedResponse<AvailableNgoModel>>, NotFound>> ExecuteAsync(Request request,
25+
CancellationToken ct)
26+
{
27+
var sql = """
28+
SELECT
29+
COUNT(*)
30+
FROM
31+
"Ngos" N
32+
WHERE
33+
"Status" = 'Activated'
34+
AND "Id" NOT IN (
35+
SELECT
36+
"NgoId"
37+
FROM
38+
"MonitoringNgos"
39+
WHERE
40+
"ElectionRoundId" = @electionRoundId
41+
)
42+
AND (
43+
@searchText IS NULL
44+
OR N."Name" ILIKE @searchText
45+
OR N."Id"::TEXT ILIKE @searchText
46+
);
47+
48+
SELECT
49+
N."Id",
50+
N."Name"
51+
FROM
52+
"Ngos" N
53+
WHERE
54+
"Status" = 'Activated'
55+
AND "Id" NOT IN (
56+
SELECT
57+
"NgoId"
58+
FROM
59+
"MonitoringNgos"
60+
WHERE
61+
"ElectionRoundId" = @electionRoundId
62+
)
63+
AND (
64+
@searchText IS NULL
65+
OR N."Name" ILIKE @searchText
66+
OR N."Id"::TEXT ILIKE @searchText
67+
)
68+
ORDER BY
69+
CASE
70+
WHEN @sortExpression = 'Name ASC' THEN "Name"
71+
END ASC,
72+
CASE
73+
WHEN @sortExpression = 'Name DESC' THEN "Name"
74+
END DESC
75+
OFFSET
76+
@offset ROWS
77+
FETCH NEXT
78+
@pageSize ROWS ONLY;
79+
""";
80+
81+
var queryArgs = new
82+
{
83+
electionRoundId = request.ElectionRoundId,
84+
searchText = request.SearchText,
85+
offset = PaginationHelper.CalculateSkip(request.PageSize, request.PageNumber),
86+
pageSize = request.PageSize,
87+
sortExpression = GetSortExpression(request.SortColumnName, request.IsAscendingSorting)
88+
};
89+
90+
int totalRowCount;
91+
List<AvailableNgoModel> entries;
92+
using (var dbConnection = await dbConnectionFactory.GetOpenConnectionAsync(ct))
93+
{
94+
using var multi = await dbConnection.QueryMultipleAsync(sql, queryArgs);
95+
96+
totalRowCount = multi.Read<int>().Single();
97+
entries = multi.Read<AvailableNgoModel>().ToList();
98+
}
99+
100+
return TypedResults.Ok(
101+
new PagedResponse<AvailableNgoModel>(entries, totalRowCount, request.PageNumber, request.PageSize));
102+
}
103+
104+
private static string GetSortExpression(string? sortColumnName, bool isAscendingSorting)
105+
{
106+
if (string.IsNullOrWhiteSpace(sortColumnName))
107+
{
108+
return "Name ASC";
109+
}
110+
111+
var sortOrder = isAscendingSorting ? "ASC" : "DESC";
112+
113+
if (string.Equals(sortColumnName, nameof(AvailableNgoModel.Name), StringComparison.InvariantCultureIgnoreCase))
114+
{
115+
return $"{nameof(AvailableNgoModel.Name)} {sortOrder}";
116+
}
117+
118+
return "Name ASC";
119+
}
120+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Vote.Monitor.Core.Models;
2+
3+
namespace Feature.Monitoring.ListAvailableNgos;
4+
5+
public class Request : BaseSortPaginatedRequest
6+
{
7+
public Guid ElectionRoundId { get; set; }
8+
[QueryParam] public string? SearchText { get; set; }
9+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Feature.Monitoring.ListAvailableNgos;
2+
3+
public class Validator : Validator<Request>
4+
{
5+
public Validator()
6+
{
7+
RuleFor(x => x.ElectionRoundId).NotEmpty();
8+
9+
RuleFor(x => x.PageNumber)
10+
.GreaterThanOrEqualTo(1);
11+
12+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
13+
}
14+
}

api/src/Feature.MonitoringObservers/List/Validator.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ public Validator()
77
RuleFor(x => x.ElectionRoundId).NotEmpty();
88
RuleFor(x => x.NgoId).NotEmpty();
99
RuleForEach(x => x.Tags).NotEmpty();
10+
11+
RuleFor(x => x.PageNumber)
12+
.GreaterThanOrEqualTo(1);
13+
14+
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
1015
}
1116
}

0 commit comments

Comments
 (0)