Skip to content
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
44 changes: 32 additions & 12 deletions DataImport.API/Controllers/CountryController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using DataImport.API.Controllers.BaseController;
using DataImport.Commands.Queries;
using DataImport.Data.Enums; // <-- add this
using DataImport.Data.Enums;
using DataImport.API.Filters;

namespace DataImport.API.Controllers;

[ValidateEnums]
[Route("api/v{version:apiVersion}/[controller]")]
public class CountryController : ApiControllerBasev1
{
Expand All @@ -14,33 +16,51 @@ public CountryController(IMediator mediator)
_mediator = mediator;
}

[HttpPost]
[Route("get-count-by-country")]
[HttpGet("get-count-by-country")]
public async Task<IActionResult> GetCountryCount(
[FromQuery] Country country,
CancellationToken ct)
{
if (!Enum.IsDefined(typeof(Country), country))
return BadRequest("Valid country is required");

var result = await _mediator.Send(new GetCountryCountQuery(country), ct);
return Ok(result);
}

[HttpPost]
[Route("get-sanctions-by-country")]
[HttpGet("get-delisted-count-by-country")]
public async Task<IActionResult> GetDelistedCountByCountry(
[FromQuery] Country country,
CancellationToken ct)
{
var result = await _mediator.Send(new GetCountryDelistedCountQuery(country), ct);
return Ok(result);
}

[HttpGet("get-sanctions-by-country")]
public async Task<IActionResult> GetCountrySanctionsPaged(
[FromQuery] Country country,
[FromQuery] int pageSize = 20,
[FromQuery] int page = 1,
CancellationToken ct = default)
{
if (!Enum.IsDefined(typeof(Country), country))
return BadRequest("Valid country is required");
pageSize = Math.Clamp(pageSize, 1, 100);
page = Math.Max(page, 1);

var result = await _mediator.Send(
new GetCountrySanctionsPagedQuery(country, pageSize, page), ct);
return Ok(result);
}

[HttpGet("get-delisted-sanctions-by-country")]
public async Task<IActionResult> GetDelistedCountrySanctionsPaged(
[FromQuery] Country country,
[FromQuery] int pageSize = 20,
[FromQuery] int page = 1,
CancellationToken ct = default)
{
pageSize = Math.Clamp(pageSize, 1, 100);
page = Math.Max(page, 1);

var result = await _mediator.Send(
new GetCountrySanctionsPagedQuery(country, pageSize, page),
ct);
new GetDelistedCountrySanctionsPagedQuery(country, pageSize, page), ct);

return Ok(result);
}
Expand Down
15 changes: 15 additions & 0 deletions DataImport.API/Controllers/SanctionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,20 @@ public async Task<IActionResult> GetPaged(
new GetSanctionsPagedQuery(page, pageSize, sdnType, lastNameContains), ct);
return Ok(result);
}

[HttpGet("Paged-delisted-Sanctions")]
public async Task<IActionResult> GetDelistedPaged(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] string? sdnType = null,
[FromQuery] string? lastNameContains = null,
CancellationToken ct = default)
{
var result = await _mediator.Send(
new GetDelistedSanctionsPagedQuery(page, pageSize, sdnType, lastNameContains), ct);
return Ok(result);
}


}
}
32 changes: 32 additions & 0 deletions DataImport.API/Filters/ValidateEnumsAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace DataImport.API.Filters;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class ValidateEnumsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(
new ValidationProblemDetails(context.ModelState));
return;
}

foreach (var (name, value) in context.ActionArguments)
{
if (value is null) continue;

var type = value.GetType();
if (type.IsEnum && !Enum.IsDefined(type, value))
{
context.ModelState.AddModelError(name, $"'{value}' is not a valid {type.Name}.");
context.Result = new BadRequestObjectResult(
new ValidationProblemDetails(context.ModelState));
return;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace DataImport.Commands.Commands;

public record CountryCountResult(bool Success, String Country, int Count);
public record CountryCountResult(bool Success, String Country, int Count, bool isDelisted = false);

public class GetCountryCountQueryCommand : IRequestHandler<GetCountryCountQuery, CountryCountResult>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using DataImport.Commands.Queries;
using DataImport.Data.Data;
using MediatR;
using Microsoft.EntityFrameworkCore;
using ZiggyCreatures.Caching.Fusion;

namespace DataImport.Commands.Commands;

public class GetCountryDelistedCountQueryCommand
: IRequestHandler<GetCountryDelistedCountQuery, CountryCountResult>
{
private readonly SanctionsDbContext _db;
private readonly IFusionCache _cache;

public GetCountryDelistedCountQueryCommand(SanctionsDbContext db, IFusionCache cache)
{
_db = db;
_cache = cache;
}

public async Task<CountryCountResult> Handle(
GetCountryDelistedCountQuery request,
CancellationToken ct)
{
var country = request.country.ToString();
var cacheKey = $"CountryDelistedCount_{country}";

return await _cache.GetOrSetAsync<CountryCountResult>(
cacheKey,
async _ =>
{
var count = await _db.SanctionDetails
.Where(x => x.Country == country && !x.IsActive)
.CountAsync(ct);

return new CountryCountResult(true, country, count, isDelisted: true);
},
options => options.SetDuration(TimeSpan.FromMinutes(5)),
ct
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using DataImport.Data.Data;
using DataImport.Presentation.GenericDTO;
using MediatR;
using Microsoft.EntityFrameworkCore;
using ZiggyCreatures.Caching.Fusion;
using Facet.Extensions;
using DataImport.Commands.Queries;

namespace DataImport.Commands.Commands;

public class GetDelistedCountrySanctionsPagedQueryHandler
: IRequestHandler<GetDelistedCountrySanctionsPagedQuery, PagedResult<SanctionListItemDto>>
{
private readonly SanctionsDbContext _db;
private readonly IFusionCache _cache;

public GetDelistedCountrySanctionsPagedQueryHandler(SanctionsDbContext db, IFusionCache cache)
{
_db = db;
_cache = cache;
}

public async Task<PagedResult<SanctionListItemDto>> Handle(
GetDelistedCountrySanctionsPagedQuery request, CancellationToken ct)
{
var countryName = request.country.ToString();

// "Delisted" prefix keeps this from colliding with the active-sanctions cache key
var cacheKey = $"DelistedCountrySanctionsPaged_{countryName}_{request.Page}_{request.PageSize}";

return await _cache.GetOrSetAsync<PagedResult<SanctionListItemDto>>(
cacheKey,
async _ =>
{
var query = _db.SanctionDetails
.AsNoTracking()
.Where(x => x.Country == countryName && !x.IsActive); // adjust to your delist field

var total = await query.CountAsync(ct);

var items = await query
.OrderBy(x => x.Id)
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.Select(x => x.ToFacet<SanctionListItemDto>())
.ToListAsync(ct);

return new PagedResult<SanctionListItemDto>(
items,
request.Page,
request.PageSize,
total,
(int)Math.Ceiling(total / (double)request.PageSize)
);
},
options => options.SetDuration(TimeSpan.FromMinutes(5)),
ct
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using DataImport.Data.Data;
using DataImport.Presentation.GenericDTO;
using Facet.Extensions;
using MediatR;
using Microsoft.EntityFrameworkCore;
using DataImport.Commands.Queries;
using ZiggyCreatures.Caching.Fusion;

namespace DataImport.Commands.Commands;

public class GetDelistedSanctionsPagedQueryCommand
: IRequestHandler<GetDelistedSanctionsPagedQuery, PagedResult<SanctionListItemDto>>
{
private readonly SanctionsDbContext _db;
private readonly IFusionCache _cache;


public GetDelistedSanctionsPagedQueryCommand(SanctionsDbContext db, IFusionCache cache)
{
_db = db;
_cache = cache;
}

public async Task<PagedResult<SanctionListItemDto>> Handle(
GetDelistedSanctionsPagedQuery request, CancellationToken ct)
{
var cacheKey =
$"DelistedSanctionsPaged_{request.SdnType}_{request.LastNameContains}_{request.Page}_{request.PageSize}";

return await _cache.GetOrSetAsync<PagedResult<SanctionListItemDto>>(
cacheKey,
async _ =>
{
var query = _db.SanctionDetails
.AsNoTracking()
.Where(s => !s.IsActive); // apply once, before count

if (!string.IsNullOrWhiteSpace(request.SdnType))
query = query.Where(s => s.SdnType == request.SdnType);

if (!string.IsNullOrWhiteSpace(request.LastNameContains))
query = query.Where(s => s.LastName.Contains(request.LastNameContains));

var total = await query.CountAsync(ct);

var items = await query
.OrderBy(s => s.LastName)
.ThenBy(s => s.Id) // tiebreaker, see below
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.Select(s => s.ToFacet<SanctionListItemDto>())
.ToListAsync(ct);

return new PagedResult<SanctionListItemDto>(
items,
request.Page,
request.PageSize,
total,
(int)Math.Ceiling(total / (double)request.PageSize));
},
options => options.SetDuration(TimeSpan.FromMinutes(5)),
ct);
}
}
9 changes: 9 additions & 0 deletions DataImport.Commands/Queries/CountryQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,14 @@ public record GetCountrySanctionsPagedQuery(
int Page = 1
) : IRequest<PagedResult<SanctionListItemDto>>;


public record GetDelistedCountrySanctionsPagedQuery(Country country,
int PageSize,
int Page)
: IRequest<PagedResult<SanctionListItemDto>>;


public record GetCountryCountQuery(Country country) : IRequest<DataImport.Commands.Commands.CountryCountResult>;

public record GetCountryDelistedCountQuery(Country country) : IRequest<DataImport.Commands.Commands.CountryCountResult>;

8 changes: 8 additions & 0 deletions DataImport.Commands/Queries/SanctionQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ public record GetSanctionsPagedQuery(
string? LastNameContains = null
) : IRequest<PagedResult<SanctionListItemDto>>;

public record GetDelistedSanctionsPagedQuery(
int Page = 1,
int PageSize = 20,
string? SdnType = null,
string? LastNameContains = null
) : IRequest<PagedResult<SanctionListItemDto>>;


public record SearchRequest(string Name);

// Added a normal Tolerance as it is not optimized yet.
Expand Down
Loading