< Summary

Information
Class: Chronicis.Api.Controllers.SysAdminTutorialsController
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Controllers/SysAdminTutorialsController.cs
Line coverage
100%
Covered lines: 9
Uncovered lines: 0
Coverable lines: 9
Total lines: 129
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
MapInvalidOperation(...)100%22100%

File(s)

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

#LineLine coverage
 1using Chronicis.Api.Services;
 2using Chronicis.Shared.DTOs;
 3using Microsoft.AspNetCore.Authorization;
 4using Microsoft.AspNetCore.Mvc;
 5
 6namespace Chronicis.Api.Controllers;
 7
 8/// <summary>
 9/// SysAdmin-only API endpoints for tutorial page mappings.
 10/// </summary>
 11[ApiController]
 12[Route("sysadmin/tutorials")]
 13[Authorize]
 14public class SysAdminTutorialsController : ControllerBase
 15{
 16    private readonly ITutorialService _tutorialService;
 17    private readonly ILogger<SysAdminTutorialsController> _logger;
 18
 219    public SysAdminTutorialsController(
 220        ITutorialService tutorialService,
 221        ILogger<SysAdminTutorialsController> logger)
 22    {
 223        _tutorialService = tutorialService;
 224        _logger = logger;
 225    }
 26
 27    /// <summary>
 28    /// GET /api/sysadmin/tutorials - Lists all tutorial mappings.
 29    /// </summary>
 30    [HttpGet]
 31    public async Task<ActionResult<List<TutorialMappingDto>>> GetMappings()
 32    {
 33        try
 34        {
 35            var mappings = await _tutorialService.GetMappingsAsync();
 36            return Ok(mappings);
 37        }
 38        catch (UnauthorizedAccessException)
 39        {
 40            _logger.LogWarningSanitized("Unauthorized attempt to list tutorial mappings");
 41            return Forbid();
 42        }
 43    }
 44
 45    /// <summary>
 46    /// POST /api/sysadmin/tutorials - Creates a tutorial mapping (and optionally a tutorial article).
 47    /// </summary>
 48    [HttpPost]
 49    public async Task<ActionResult<TutorialMappingDto>> CreateMapping([FromBody] TutorialMappingCreateDto dto)
 50    {
 51        try
 52        {
 53            var mapping = await _tutorialService.CreateMappingAsync(dto);
 54            return CreatedAtAction(nameof(GetMappings), new { id = mapping.Id }, mapping);
 55        }
 56        catch (UnauthorizedAccessException)
 57        {
 58            _logger.LogWarningSanitized("Unauthorized attempt to create tutorial mapping");
 59            return Forbid();
 60        }
 61        catch (ArgumentException ex)
 62        {
 63            return BadRequest(new { error = ex.Message });
 64        }
 65        catch (InvalidOperationException ex)
 66        {
 67            return MapInvalidOperation(ex);
 68        }
 69    }
 70
 71    /// <summary>
 72    /// PUT /api/sysadmin/tutorials/{id} - Updates a tutorial mapping.
 73    /// </summary>
 74    [HttpPut("{id:guid}")]
 75    public async Task<ActionResult<TutorialMappingDto>> UpdateMapping(Guid id, [FromBody] TutorialMappingUpdateDto dto)
 76    {
 77        try
 78        {
 79            var mapping = await _tutorialService.UpdateMappingAsync(id, dto);
 80            if (mapping == null)
 81            {
 82                return NotFound(new { error = "Tutorial mapping not found" });
 83            }
 84
 85            return Ok(mapping);
 86        }
 87        catch (UnauthorizedAccessException)
 88        {
 89            _logger.LogWarningSanitized("Unauthorized attempt to update tutorial mapping {MappingId}", id);
 90            return Forbid();
 91        }
 92        catch (ArgumentException ex)
 93        {
 94            return BadRequest(new { error = ex.Message });
 95        }
 96        catch (InvalidOperationException ex)
 97        {
 98            return MapInvalidOperation(ex);
 99        }
 100    }
 101
 102    /// <summary>
 103    /// DELETE /api/sysadmin/tutorials/{id} - Deletes a tutorial mapping.
 104    /// </summary>
 105    [HttpDelete("{id:guid}")]
 106    public async Task<IActionResult> DeleteMapping(Guid id)
 107    {
 108        try
 109        {
 110            var deleted = await _tutorialService.DeleteMappingAsync(id);
 111            return deleted ? NoContent() : NotFound(new { error = "Tutorial mapping not found" });
 112        }
 113        catch (UnauthorizedAccessException)
 114        {
 115            _logger.LogWarningSanitized("Unauthorized attempt to delete tutorial mapping {MappingId}", id);
 116            return Forbid();
 117        }
 118    }
 119
 120    private ObjectResult MapInvalidOperation(InvalidOperationException ex)
 121    {
 2122        if (ex.Message.Contains("already exists", StringComparison.OrdinalIgnoreCase))
 123        {
 1124            return Conflict(new { error = ex.Message });
 125        }
 126
 1127        return BadRequest(new { error = ex.Message });
 128    }
 129}