85 lines
4.2 KiB
C#
85 lines
4.2 KiB
C#
using System.Text.Json;
|
|
using JobTrackerApi.Data;
|
|
using JobTrackerApi.Models;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace JobTrackerApi.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/career/evidence")]
|
|
[Authorize(AuthenticationSchemes = "local")]
|
|
public sealed class CareerEvidenceController(JobTrackerContext db, UserManager<ApplicationUser> users) : ControllerBase
|
|
{
|
|
public sealed record EvidenceRequest(string? Category, string? Title, string? Statement, IReadOnlyList<string>? Tags, string? SourceType, string? SourceReference, bool? IsVerified);
|
|
public sealed record EvidenceDto(int Id, string Category, string Title, string Statement, IReadOnlyList<string> Tags, string SourceType, string? SourceReference, bool IsVerified, DateTimeOffset UpdatedAtUtc);
|
|
|
|
[HttpGet]
|
|
public async Task<ActionResult<IReadOnlyList<EvidenceDto>>> List(CancellationToken ct)
|
|
{
|
|
var owner = await OwnerAsync();
|
|
if (owner is null) return Unauthorized();
|
|
var rows = await db.Set<CareerEvidence>().AsNoTracking().Where(x => x.OwnerUserId == owner).OrderByDescending(x => x.UpdatedAtUtc).ToListAsync(ct);
|
|
return Ok(rows.Select(ToDto));
|
|
}
|
|
|
|
[HttpPost]
|
|
public async Task<ActionResult<EvidenceDto>> Create(EvidenceRequest request, CancellationToken ct)
|
|
{
|
|
var owner = await OwnerAsync();
|
|
if (owner is null) return Unauthorized();
|
|
var error = Validate(request);
|
|
if (error is not null) return BadRequest(error);
|
|
var row = new CareerEvidence { OwnerUserId = owner };
|
|
Apply(row, request);
|
|
db.Add(row);
|
|
await db.SaveChangesAsync(ct);
|
|
return Ok(ToDto(row));
|
|
}
|
|
|
|
[HttpPut("{id:int}")]
|
|
public async Task<ActionResult<EvidenceDto>> Update(int id, EvidenceRequest request, CancellationToken ct)
|
|
{
|
|
var owner = await OwnerAsync();
|
|
if (owner is null) return Unauthorized();
|
|
var row = await db.Set<CareerEvidence>().FirstOrDefaultAsync(x => x.Id == id && x.OwnerUserId == owner, ct);
|
|
if (row is null) return NotFound();
|
|
var error = Validate(request);
|
|
if (error is not null) return BadRequest(error);
|
|
Apply(row, request);
|
|
await db.SaveChangesAsync(ct);
|
|
return Ok(ToDto(row));
|
|
}
|
|
|
|
[HttpDelete("{id:int}")]
|
|
public async Task<IActionResult> Delete(int id, CancellationToken ct)
|
|
{
|
|
var owner = await OwnerAsync();
|
|
if (owner is null) return Unauthorized();
|
|
var row = await db.Set<CareerEvidence>().FirstOrDefaultAsync(x => x.Id == id && x.OwnerUserId == owner, ct);
|
|
if (row is null) return NotFound();
|
|
db.Remove(row);
|
|
await db.SaveChangesAsync(ct);
|
|
return NoContent();
|
|
}
|
|
|
|
private async Task<string?> OwnerAsync() => (await users.GetUserAsync(User))?.Id;
|
|
private static string? Validate(EvidenceRequest value) =>
|
|
string.IsNullOrWhiteSpace(value.Title) ? "Title is required." : string.IsNullOrWhiteSpace(value.Statement) ? "Evidence is required." : null;
|
|
private static void Apply(CareerEvidence row, EvidenceRequest value)
|
|
{
|
|
row.Category = Clean(value.Category, "achievement", 32);
|
|
row.Title = Clean(value.Title, string.Empty, 255);
|
|
row.Statement = value.Statement!.Trim();
|
|
row.TagsJson = JsonSerializer.Serialize((value.Tags ?? []).Select(x => x.Trim()).Where(x => x.Length > 0).Distinct(StringComparer.OrdinalIgnoreCase).Take(30));
|
|
row.SourceType = Clean(value.SourceType, "manual", 32);
|
|
row.SourceReference = string.IsNullOrWhiteSpace(value.SourceReference) ? null : value.SourceReference.Trim()[..Math.Min(value.SourceReference.Trim().Length, 500)];
|
|
row.IsVerified = value.IsVerified ?? true;
|
|
row.UpdatedAtUtc = DateTimeOffset.UtcNow;
|
|
}
|
|
private static string Clean(string? value, string fallback, int max) { var text = string.IsNullOrWhiteSpace(value) ? fallback : value.Trim(); return text[..Math.Min(text.Length, max)]; }
|
|
private static EvidenceDto ToDto(CareerEvidence row) => new(row.Id, row.Category, row.Title, row.Statement, JsonSerializer.Deserialize<List<string>>(row.TagsJson) ?? [], row.SourceType, row.SourceReference, row.IsVerified, row.UpdatedAtUtc);
|
|
}
|