Files
jobtrackingapp/JobTrackerApi/Controllers/RulesController.cs
T

117 lines
4.8 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
namespace JobTrackerApi.Controllers
{
[ApiController]
// Explicitly authorized. These endpoints are all tenant-scoped user data, so they must not
// depend on the Auth:Require fallback policy being switched on: a deployment that lost that flag
// would otherwise serve them anonymously. docs/security/security-notes.md.
[Route("api/rules")]
[Authorize(AuthenticationSchemes = "local")]
public class RulesController : ControllerBase
{
private readonly JobTrackerContext _db;
public RulesController(JobTrackerContext db)
{
_db = db;
}
[HttpGet]
public async Task<ActionResult<RuleSettings>> Get(CancellationToken cancellationToken)
{
// Per-user rule settings when authenticated.
if (!string.IsNullOrWhiteSpace(_db.CurrentUserId))
{
var u = await GetOrCreateUserSettingsAsync(_db.CurrentUserId, cancellationToken);
return Ok(new RuleSettings
{
Id = 1,
AppliedFollowUpDays = u.AppliedFollowUpDays,
AppliedGhostDays = u.AppliedGhostDays,
OfferFollowUpDays = u.OfferFollowUpDays,
OfferGhostDays = u.OfferGhostDays,
FeedbackFollowUpDays = u.FeedbackFollowUpDays,
FeedbackGhostDays = u.FeedbackGhostDays,
});
}
// Fallback global settings.
var s = await _db.RuleSettings.FirstOrDefaultAsync(x => x.Id == 1, cancellationToken);
if (s is null)
{
s = new RuleSettings { Id = 1 };
_db.RuleSettings.Add(s);
await _db.SaveChangesAsync(cancellationToken);
}
return Ok(s);
}
[HttpPut]
public async Task<IActionResult> Update([FromBody] RuleSettings incoming, CancellationToken cancellationToken)
{
// Per-user rule settings when authenticated.
if (!string.IsNullOrWhiteSpace(_db.CurrentUserId))
{
var s = await GetOrCreateUserSettingsAsync(_db.CurrentUserId, cancellationToken);
s.AppliedFollowUpDays = Clamp(incoming.AppliedFollowUpDays, 1, 365);
s.AppliedGhostDays = Clamp(incoming.AppliedGhostDays, 1, 365);
s.OfferFollowUpDays = Clamp(incoming.OfferFollowUpDays, 1, 365);
s.OfferGhostDays = Clamp(incoming.OfferGhostDays, 1, 365);
s.FeedbackFollowUpDays = Clamp(incoming.FeedbackFollowUpDays, 1, 365);
s.FeedbackGhostDays = Clamp(incoming.FeedbackGhostDays, 1, 365);
await _db.SaveChangesAsync(cancellationToken);
return NoContent();
}
// Fallback global settings.
var g = await _db.RuleSettings.FirstOrDefaultAsync(x => x.Id == 1, cancellationToken);
if (g is null)
{
g = new RuleSettings { Id = 1 };
_db.RuleSettings.Add(g);
}
g.AppliedFollowUpDays = Clamp(incoming.AppliedFollowUpDays, 1, 365);
g.AppliedGhostDays = Clamp(incoming.AppliedGhostDays, 1, 365);
g.OfferFollowUpDays = Clamp(incoming.OfferFollowUpDays, 1, 365);
g.OfferGhostDays = Clamp(incoming.OfferGhostDays, 1, 365);
g.FeedbackFollowUpDays = Clamp(incoming.FeedbackFollowUpDays, 1, 365);
g.FeedbackGhostDays = Clamp(incoming.FeedbackGhostDays, 1, 365);
await _db.SaveChangesAsync(cancellationToken);
return NoContent();
}
private async Task<UserRuleSettings> GetOrCreateUserSettingsAsync(string ownerUserId, CancellationToken cancellationToken)
{
var settings = await _db.UserRuleSettings.FirstOrDefaultAsync(x => x.OwnerUserId == ownerUserId, cancellationToken);
if (settings is not null) return settings;
settings = new UserRuleSettings { OwnerUserId = ownerUserId };
_db.UserRuleSettings.Add(settings);
try
{
await _db.SaveChangesAsync(cancellationToken);
return settings;
}
catch (DbUpdateException)
{
_db.Entry(settings).State = EntityState.Detached;
settings = await _db.UserRuleSettings.FirstOrDefaultAsync(x => x.OwnerUserId == ownerUserId, cancellationToken);
if (settings is null) throw;
return settings;
}
}
private static int Clamp(int v, int min, int max) => v < min ? min : v > max ? max : v;
}
}