< Summary

Information
Class: Chronicis.Api.Controllers.CampaignArcsController
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/ArcsController.cs
Line coverage
100%
Covered lines: 8
Uncovered lines: 0
Coverable lines: 8
Total lines: 201
Line coverage: 100%
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
.ctor(...)100%11100%

File(s)

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

#LineLine coverage
 1using Chronicis.Api.Infrastructure;
 2using Chronicis.Api.Models;
 3using Chronicis.Api.Services;
 4using Chronicis.Shared.DTOs;
 5using Microsoft.AspNetCore.Authorization;
 6using Microsoft.AspNetCore.Mvc;
 7
 8namespace Chronicis.Api.Controllers;
 9
 10/// <summary>
 11/// API endpoints for Arc management.
 12/// </summary>
 13[ApiController]
 14[Route("arcs")]
 15[Authorize]
 16public class ArcsController : ControllerBase
 17{
 18    private readonly IArcService _arcService;
 19    private readonly ICurrentUserService _currentUserService;
 20    private readonly ILogger<ArcsController> _logger;
 21
 22    public ArcsController(
 23        IArcService arcService,
 24        ICurrentUserService currentUserService,
 25        ILogger<ArcsController> logger)
 26    {
 27        _arcService = arcService;
 28        _currentUserService = currentUserService;
 29        _logger = logger;
 30    }
 31
 32    /// <summary>
 33    /// GET /api/arcs/{id} - Get a specific arc.
 34    /// </summary>
 35    [HttpGet("{id:guid}")]
 36    public async Task<ActionResult<ArcDto>> GetArc(Guid id)
 37    {
 38        var user = await _currentUserService.GetRequiredUserAsync();
 39        _logger.LogTraceSanitized("Getting arc {ArcId} for user {UserId}", id, user.Id);
 40
 41        var arc = await _arcService.GetArcAsync(id, user.Id);
 42
 43        if (arc == null)
 44        {
 45            return NotFound(new { error = "Arc not found or access denied" });
 46        }
 47
 48        return Ok(arc);
 49    }
 50
 51    /// <summary>
 52    /// POST /api/arcs - Create a new arc.
 53    /// </summary>
 54    [HttpPost]
 55    public async Task<ActionResult<ArcDto>> CreateArc([FromBody] ArcCreateDto dto)
 56    {
 57        var user = await _currentUserService.GetRequiredUserAsync();
 58
 59        if (dto == null || string.IsNullOrWhiteSpace(dto.Name))
 60        {
 61            return BadRequest(new { error = "Name is required" });
 62        }
 63
 64        if (dto.CampaignId == Guid.Empty)
 65        {
 66            return BadRequest(new { error = "CampaignId is required" });
 67        }
 68
 69        _logger.LogTraceSanitized("Creating arc '{Name}' in campaign {CampaignId} for user {UserId}",
 70            dto.Name, dto.CampaignId, user.Id);
 71
 72        var arc = await _arcService.CreateArcAsync(dto, user.Id);
 73
 74        if (arc == null)
 75        {
 76            return StatusCode(403, new { error = "Access denied or failed to create arc" });
 77        }
 78
 79        return CreatedAtAction(nameof(GetArc), new { id = arc.Id }, arc);
 80    }
 81
 82    /// <summary>
 83    /// PUT /api/arcs/{id} - Update an arc.
 84    /// </summary>
 85    [HttpPut("{id:guid}")]
 86    public async Task<ActionResult<ArcDto>> UpdateArc(Guid id, [FromBody] ArcUpdateDto dto)
 87    {
 88        var user = await _currentUserService.GetRequiredUserAsync();
 89
 90        if (dto == null || string.IsNullOrWhiteSpace(dto.Name))
 91        {
 92            return BadRequest(new { error = "Name is required" });
 93        }
 94
 95        _logger.LogTraceSanitized("Updating arc {ArcId} for user {UserId}", id, user.Id);
 96
 97        var arc = await _arcService.UpdateArcAsync(id, dto, user.Id);
 98
 99        if (arc == null)
 100        {
 101            return NotFound(new { error = "Arc not found or access denied" });
 102        }
 103
 104        return Ok(arc);
 105    }
 106
 107    /// <summary>
 108    /// DELETE /api/arcs/{id} - Delete an arc (only if empty).
 109    /// </summary>
 110    [HttpDelete("{id:guid}")]
 111    public async Task<IActionResult> DeleteArc(Guid id)
 112    {
 113        var user = await _currentUserService.GetRequiredUserAsync();
 114        _logger.LogTraceSanitized("Deleting arc {ArcId} for user {UserId}", id, user.Id);
 115
 116        var success = await _arcService.DeleteArcAsync(id, user.Id);
 117
 118        if (!success)
 119        {
 120            return BadRequest(new { error = "Arc not found, access denied, or arc is not empty" });
 121        }
 122
 123        return NoContent();
 124    }
 125
 126    /// <summary>
 127    /// POST /api/arcs/{id}/activate - Activate an arc for quick session creation.
 128    /// </summary>
 129    [HttpPost("{id:guid}/activate")]
 130    public async Task<IActionResult> ActivateArc(Guid id)
 131    {
 132        var user = await _currentUserService.GetRequiredUserAsync();
 133        _logger.LogTraceSanitized("Activating arc {ArcId} for user {UserId}", id, user.Id);
 134
 135        var success = await _arcService.ActivateArcAsync(id, user.Id);
 136
 137        if (!success)
 138        {
 139            return BadRequest(new { error = "Unable to activate arc. Arc not found or you don't have permission." });
 140        }
 141
 142        return NoContent();
 143    }
 144
 145    /// <summary>
 146    /// PUT /api/arcs/{id}/slug - Update an arc's slug.
 147    /// </summary>
 148    [HttpPut("{id:guid}/slug")]
 149    public async Task<ActionResult<SlugUpdateResponseDto>> UpdateArcSlug(Guid id, [FromBody] SlugUpdateRequestDto dto)
 150    {
 151        var user = await _currentUserService.GetRequiredUserAsync();
 152
 153        var result = await _arcService.UpdateSlugAsync(id, dto.Slug, user.Id);
 154
 155        return result.Status switch
 156        {
 157            ServiceStatus.Success => Ok(new SlugUpdateResponseDto { Slug = result.Value! }),
 158            ServiceStatus.NotFound => NotFound(new { error = "Arc not found" }),
 159            ServiceStatus.Forbidden => StatusCode(403, new { error = result.ErrorMessage }),
 160            ServiceStatus.ValidationError when result.ErrorMessage == "SLUG_RESERVED" =>
 161                BadRequest(new { error = "SLUG_RESERVED" }),
 162            _ => BadRequest(new { error = result.ErrorMessage })
 163        };
 164    }
 165}
 166
 167/// <summary>
 168/// Campaign-scoped arc endpoints.
 169/// </summary>
 170[ApiController]
 171[Route("campaigns/{campaignId:guid}/arcs")]
 172[Authorize]
 173public class CampaignArcsController : ControllerBase
 174{
 175    private readonly IArcService _arcService;
 176    private readonly ICurrentUserService _currentUserService;
 177    private readonly ILogger<CampaignArcsController> _logger;
 178
 1179    public CampaignArcsController(
 1180        IArcService arcService,
 1181        ICurrentUserService currentUserService,
 1182        ILogger<CampaignArcsController> logger)
 183    {
 1184        _arcService = arcService;
 1185        _currentUserService = currentUserService;
 1186        _logger = logger;
 1187    }
 188
 189    /// <summary>
 190    /// GET /api/campaigns/{campaignId}/arcs - Get all arcs for a campaign.
 191    /// </summary>
 192    [HttpGet]
 193    public async Task<ActionResult<List<ArcDto>>> GetArcsByCampaign(Guid campaignId)
 194    {
 195        var user = await _currentUserService.GetRequiredUserAsync();
 196        _logger.LogTraceSanitized("Getting arcs for campaign {CampaignId} for user {UserId}", campaignId, user.Id);
 197
 198        var arcs = await _arcService.GetArcsByCampaignAsync(campaignId, user.Id);
 199        return Ok(arcs);
 200    }
 201}