feat(profile): ProfileController + DTOs + integration tests
Add /profile/index endpoint that returns user_rank_match_total_win (stubbed 0) and user_class_list built from viewer Classes + owned LeaderSkins. Six NUnit integration tests cover zero wins, all classes present, level/exp/default skin, leader_skin_id_list population, is_random_leader_skin round-trip, and 401 on unauthenticated access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
51
SVSim.EmulatedEntrypoint/Controllers/ProfileController.cs
Normal file
51
SVSim.EmulatedEntrypoint/Controllers/ProfileController.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SVSim.Database.Repositories.Viewer;
|
||||
using SVSim.EmulatedEntrypoint.Constants;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Profile;
|
||||
|
||||
namespace SVSim.EmulatedEntrypoint.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// /profile/* — viewer-scoped profile read endpoint. Surfaces total rank-match wins
|
||||
/// and the per-class roster (level, exp, leader-skin selection).
|
||||
/// </summary>
|
||||
[Route("profile")]
|
||||
public sealed class ProfileController : SVSimController
|
||||
{
|
||||
private readonly IViewerRepository _viewerRepository;
|
||||
|
||||
public ProfileController(IViewerRepository viewerRepository) =>
|
||||
_viewerRepository = viewerRepository;
|
||||
|
||||
[HttpPost("index")]
|
||||
public async Task<ActionResult<ProfileIndexResponse>> Index(
|
||||
[FromBody] ProfileIndexRequest _,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var shortUdidClaim = User.Claims.FirstOrDefault(c => c.Type == ShadowverseClaimTypes.ShortUdidClaim)?.Value;
|
||||
if (shortUdidClaim is null || !long.TryParse(shortUdidClaim, out long shortUdid))
|
||||
return Unauthorized();
|
||||
|
||||
var viewer = await _viewerRepository.GetViewerByShortUdid(shortUdid);
|
||||
if (viewer is null) return NotFound();
|
||||
|
||||
var skinsByClass = viewer.LeaderSkins
|
||||
.Where(s => s.ClassId.HasValue)
|
||||
.GroupBy(s => s.ClassId!.Value)
|
||||
.ToDictionary(g => g.Key, g => (IReadOnlyCollection<int>)g.Select(s => s.Id).ToList());
|
||||
|
||||
var classes = viewer.Classes
|
||||
.Select(vc => new UserClass(
|
||||
vc,
|
||||
skinsByClass.GetValueOrDefault(vc.Class.Id, Array.Empty<int>())))
|
||||
.ToList();
|
||||
|
||||
return new ProfileIndexResponse
|
||||
{
|
||||
// TODO: when rank-match results are tracked, compute from viewer's rank history.
|
||||
UserRankMatchTotalWin = 0,
|
||||
UserClassList = classes,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user