< Summary

Information
Class: Chronicis.Api.Controllers.CharacterClaimStatusDto
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/CharactersController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 4
Coverable lines: 4
Total lines: 169
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_CharacterId()100%210%
get_IsClaimed()100%210%
get_IsClaimedByMe()100%210%
get_ClaimedByName()100%210%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/CharactersController.cs

#LineLine coverage
 1using Chronicis.Api.Data;
 2using Chronicis.Api.Infrastructure;
 3using Chronicis.Shared.DTOs;
 4using Chronicis.Shared.Enums;
 5using Microsoft.AspNetCore.Authorization;
 6using Microsoft.AspNetCore.Mvc;
 7using Microsoft.EntityFrameworkCore;
 8
 9namespace Chronicis.Api.Controllers;
 10
 11/// <summary>
 12/// API endpoints for Character claiming operations.
 13/// </summary>
 14[ApiController]
 15[Route("characters")]
 16[Authorize]
 17public class CharactersController : ControllerBase
 18{
 19    private readonly ChronicisDbContext _context;
 20    private readonly ICurrentUserService _currentUserService;
 21    private readonly ILogger<CharactersController> _logger;
 22
 23    public CharactersController(
 24        ChronicisDbContext context,
 25        ICurrentUserService currentUserService,
 26        ILogger<CharactersController> logger)
 27    {
 28        _context = context;
 29        _currentUserService = currentUserService;
 30        _logger = logger;
 31    }
 32
 33    /// <summary>
 34    /// GET /api/characters/claimed - Get all characters claimed by the current user.
 35    /// </summary>
 36    [HttpGet("claimed")]
 37    public async Task<ActionResult<List<ClaimedCharacterDto>>> GetClaimedCharacters()
 38    {
 39        var user = await _currentUserService.GetRequiredUserAsync();
 40        _logger.LogDebug("Getting claimed characters for user {UserId}", user.Id);
 41
 42        var claimedCharacters = await _context.Articles
 43            .Where(a => a.PlayerId == user.Id && a.Type == ArticleType.Character)
 44            .Where(a => a.WorldId.HasValue)
 45            .Select(a => new ClaimedCharacterDto
 46            {
 47                Id = a.Id,
 48                Title = a.Title ?? "Unnamed Character",
 49                IconEmoji = a.IconEmoji,
 50                WorldId = a.WorldId!.Value,
 51                WorldName = a.World != null ? a.World.Name : "Unknown World",
 52                ModifiedAt = a.ModifiedAt,
 53                CreatedAt = a.CreatedAt
 54            })
 55            .ToListAsync();
 56
 57        return Ok(claimedCharacters);
 58    }
 59
 60    /// <summary>
 61    /// GET /api/characters/{id}/claim - Get the claim status of a character.
 62    /// </summary>
 63    [HttpGet("{id:guid}/claim")]
 64    public async Task<ActionResult<CharacterClaimStatusDto>> GetClaimStatus(Guid id)
 65    {
 66        var user = await _currentUserService.GetRequiredUserAsync();
 67        _logger.LogDebug("Getting claim status for character {CharacterId}", id);
 68
 69        var article = await _context.Articles
 70            .Where(a => a.Id == id && a.Type == ArticleType.Character)
 71            .Select(a => new
 72            {
 73                a.Id,
 74                a.PlayerId,
 75                PlayerName = a.Player != null ? a.Player.DisplayName : null
 76            })
 77            .FirstOrDefaultAsync();
 78
 79        if (article == null)
 80        {
 81            return NotFound(new { error = "Character not found" });
 82        }
 83
 84        var status = new CharacterClaimStatusDto
 85        {
 86            CharacterId = article.Id,
 87            IsClaimed = article.PlayerId.HasValue,
 88            IsClaimedByMe = article.PlayerId == user.Id,
 89            ClaimedByName = article.PlayerName
 90        };
 91
 92        return Ok(status);
 93    }
 94
 95    /// <summary>
 96    /// POST /api/characters/{id}/claim - Claim a character for the current user.
 97    /// </summary>
 98    [HttpPost("{id:guid}/claim")]
 99    public async Task<IActionResult> ClaimCharacter(Guid id)
 100    {
 101        var user = await _currentUserService.GetRequiredUserAsync();
 102        _logger.LogDebug("User {UserId} claiming character {CharacterId}", user.Id, id);
 103
 104        // Get the character article and verify user has access to its world
 105        var article = await _context.Articles
 106            .Where(a => a.Id == id && a.Type == ArticleType.Character)
 107            .Where(a => a.WorldId.HasValue && a.World!.Members.Any(m => m.UserId == user.Id))
 108            .FirstOrDefaultAsync();
 109
 110        if (article == null)
 111        {
 112            return NotFound(new { error = "Character not found or access denied" });
 113        }
 114
 115        // Check if already claimed by someone else
 116        if (article.PlayerId.HasValue && article.PlayerId != user.Id)
 117        {
 118            return Conflict(new { error = "Character is already claimed by another player" });
 119        }
 120
 121        // Claim the character
 122        article.PlayerId = user.Id;
 123        article.ModifiedAt = DateTime.UtcNow;
 124        await _context.SaveChangesAsync();
 125
 126        _logger.LogDebug("Character {CharacterId} claimed by user {UserId}", id, user.Id);
 127        return NoContent();
 128    }
 129
 130    /// <summary>
 131    /// DELETE /api/characters/{id}/claim - Unclaim a character (remove current user's claim).
 132    /// </summary>
 133    [HttpDelete("{id:guid}/claim")]
 134    public async Task<IActionResult> UnclaimCharacter(Guid id)
 135    {
 136        var user = await _currentUserService.GetRequiredUserAsync();
 137        _logger.LogDebug("User {UserId} unclaiming character {CharacterId}", user.Id, id);
 138
 139        // Get the character article
 140        var article = await _context.Articles
 141            .Where(a => a.Id == id && a.Type == ArticleType.Character)
 142            .Where(a => a.PlayerId == user.Id) // Only allow unclaiming own characters
 143            .FirstOrDefaultAsync();
 144
 145        if (article == null)
 146        {
 147            return NotFound(new { error = "Character not found or not claimed by you" });
 148        }
 149
 150        // Unclaim the character
 151        article.PlayerId = null;
 152        article.ModifiedAt = DateTime.UtcNow;
 153        await _context.SaveChangesAsync();
 154
 155        _logger.LogDebug("Character {CharacterId} unclaimed by user {UserId}", id, user.Id);
 156        return NoContent();
 157    }
 158}
 159
 160/// <summary>
 161/// Status of a character's claim.
 162/// </summary>
 163public class CharacterClaimStatusDto
 164{
 0165    public Guid CharacterId { get; set; }
 0166    public bool IsClaimed { get; set; }
 0167    public bool IsClaimedByMe { get; set; }
 0168    public string? ClaimedByName { get; set; }
 169}