< Summary

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

#LineLine coverage
 1using Chronicis.Api.Infrastructure;
 2using Chronicis.Api.Services;
 3using Chronicis.Shared.DTOs;
 4using Microsoft.AspNetCore.Authorization;
 5using Microsoft.AspNetCore.Mvc;
 6
 7namespace Chronicis.Api.Controllers;
 8
 9/// <summary>
 10/// API endpoints for World Document management (file uploads to blob storage).
 11/// </summary>
 12[Route("worlds/{worldId:guid}/documents")]
 13[Authorize]
 14public class WorldDocumentsController : ControllerBase
 15{
 16    private readonly IWorldDocumentService _documentService;
 17    private readonly ICurrentUserService _currentUserService;
 18    private readonly ILogger<WorldDocumentsController> _logger;
 19
 120    public WorldDocumentsController(
 121        IWorldDocumentService documentService,
 122        ICurrentUserService currentUserService,
 123        ILogger<WorldDocumentsController> logger)
 24    {
 125        _documentService = documentService;
 126        _currentUserService = currentUserService;
 127        _logger = logger;
 128    }
 29
 30    /// <summary>
 31    /// GET /worlds/{worldId}/documents - Get all documents for a world.
 32    /// </summary>
 33    [HttpGet]
 34    public async Task<ActionResult<IEnumerable<WorldDocumentDto>>> GetWorldDocuments(Guid worldId)
 35    {
 36        var user = await _currentUserService.GetRequiredUserAsync();
 37        _logger.LogTraceSanitized("User {UserId} getting documents for world {WorldId}", user.Id, worldId);
 38
 39        try
 40        {
 41            var documents = await _documentService.GetWorldDocumentsAsync(worldId, user.Id);
 42            return Ok(documents);
 43        }
 44        catch (UnauthorizedAccessException ex)
 45        {
 46            _logger.LogWarningSanitized(ex, "Unauthorized access to world documents");
 47            return StatusCode(403, new { error = ex.Message });
 48        }
 49    }
 50
 51    /// <summary>
 52    /// Get a download URL for a document.
 53    /// </summary>
 54    [HttpGet("documents/{documentId:guid}/content")]
 55    [HttpGet("/documents/{documentId:guid}/content")]
 56    public async Task<ActionResult<WorldDocumentDownloadDto>> GetDocumentContentAsync(Guid documentId)
 57    {
 58        var user = await _currentUserService.GetRequiredUserAsync();
 59        _logger.LogTraceSanitized("User {UserId} requesting download URL for document {DocumentId}", user.Id, documentId
 60
 61        try
 62        {
 63            var result = await _documentService.GetDocumentContentAsync(documentId, user.Id);
 64
 65            return Ok(new WorldDocumentDownloadDto
 66            {
 67                DownloadUrl = result.DownloadUrl,
 68                FileName = result.FileName,
 69                ContentType = result.ContentType,
 70                FileSizeBytes = result.FileSizeBytes
 71            });
 72        }
 73        catch (UnauthorizedAccessException ex)
 74        {
 75            _logger.LogWarningSanitized(ex, "Unauthorized document content request");
 76            return StatusCode(403, new { error = ex.Message });
 77        }
 78        catch (InvalidOperationException ex)
 79        {
 80            _logger.LogWarningSanitized(ex, "Document not found for content request");
 81            return NotFound(new { error = ex.Message });
 82        }
 83        catch (FileNotFoundException ex)
 84        {
 85            _logger.LogTraceSanitized(ex, "File not found in storage");
 86            return NotFound(new { error = "File not found in storage" });
 87        }
 88        catch (Exception ex)
 89        {
 90            _logger.LogErrorSanitized(ex, "Error generating download URL");
 91            return StatusCode(500, new { error = "Failed to generate download URL" });
 92        }
 93    }
 94
 95    /// <summary>
 96    /// POST /worlds/{worldId}/documents/request-upload - Request a document upload (generates SAS URL).
 97    /// </summary>
 98    [HttpPost("request-upload")]
 99    public async Task<ActionResult<WorldDocumentUploadResponseDto>> RequestDocumentUpload(
 100        Guid worldId,
 101        [FromBody] WorldDocumentUploadRequestDto request)
 102    {
 103        var user = await _currentUserService.GetRequiredUserAsync();
 104        _logger.LogTraceSanitized("User {UserId} requesting document upload for world {WorldId}", user.Id, worldId);
 105
 106        if (request == null)
 107        {
 108            return BadRequest(new { error = "Invalid request body" });
 109        }
 110
 111        try
 112        {
 113            var result = await _documentService.RequestUploadAsync(worldId, user.Id, request);
 114            return Ok(result);
 115        }
 116        catch (UnauthorizedAccessException ex)
 117        {
 118            _logger.LogWarningSanitized(ex, "Unauthorized upload request");
 119            return StatusCode(403, new { error = ex.Message });
 120        }
 121        catch (ArgumentException ex)
 122        {
 123            _logger.LogWarningSanitized(ex, "Invalid upload request");
 124            return BadRequest(new { error = ex.Message });
 125        }
 126    }
 127
 128    /// <summary>
 129    /// POST /worlds/{worldId}/documents/{documentId}/confirm - Confirm a document upload completed.
 130    /// </summary>
 131    [HttpPost("{documentId:guid}/confirm")]
 132    public async Task<ActionResult<WorldDocumentDto>> ConfirmDocumentUpload(Guid worldId, Guid documentId)
 133    {
 134        var user = await _currentUserService.GetRequiredUserAsync();
 135        _logger.LogTraceSanitized("User {UserId} confirming document upload {DocumentId}", user.Id, documentId);
 136
 137        try
 138        {
 139            var result = await _documentService.ConfirmUploadAsync(worldId, documentId, user.Id);
 140            return Ok(result);
 141        }
 142        catch (UnauthorizedAccessException ex)
 143        {
 144            _logger.LogWarningSanitized(ex, "Unauthorized confirm upload");
 145            return StatusCode(403, new { error = ex.Message });
 146        }
 147        catch (InvalidOperationException ex)
 148        {
 149            _logger.LogWarningSanitized(ex, "Invalid confirm upload request");
 150            return NotFound(new { error = ex.Message });
 151        }
 152    }
 153
 154    /// <summary>
 155    /// PUT /worlds/{worldId}/documents/{documentId} - Update document metadata.
 156    /// </summary>
 157    [HttpPut("{documentId:guid}")]
 158    public async Task<ActionResult<WorldDocumentDto>> UpdateWorldDocument(
 159        Guid worldId,
 160        Guid documentId,
 161        [FromBody] WorldDocumentUpdateDto update)
 162    {
 163        var user = await _currentUserService.GetRequiredUserAsync();
 164        _logger.LogTraceSanitized("User {UserId} updating document {DocumentId}", user.Id, documentId);
 165
 166        if (update == null)
 167        {
 168            return BadRequest(new { error = "Invalid request body" });
 169        }
 170
 171        try
 172        {
 173            var result = await _documentService.UpdateDocumentAsync(worldId, documentId, user.Id, update);
 174            return Ok(result);
 175        }
 176        catch (UnauthorizedAccessException ex)
 177        {
 178            _logger.LogWarningSanitized(ex, "Unauthorized update request");
 179            return StatusCode(403, new { error = ex.Message });
 180        }
 181        catch (InvalidOperationException ex)
 182        {
 183            _logger.LogWarningSanitized(ex, "Document not found for update");
 184            return NotFound(new { error = ex.Message });
 185        }
 186    }
 187
 188    /// <summary>
 189    /// DELETE /worlds/{worldId}/documents/{documentId} - Delete a document.
 190    /// </summary>
 191    [HttpDelete("{documentId:guid}")]
 192    public async Task<IActionResult> DeleteWorldDocument(Guid worldId, Guid documentId)
 193    {
 194        var user = await _currentUserService.GetRequiredUserAsync();
 195        _logger.LogTraceSanitized("User {UserId} deleting document {DocumentId}", user.Id, documentId);
 196
 197        try
 198        {
 199            await _documentService.DeleteDocumentAsync(worldId, documentId, user.Id);
 200            return NoContent();
 201        }
 202        catch (UnauthorizedAccessException ex)
 203        {
 204            _logger.LogWarningSanitized(ex, "Unauthorized delete request");
 205            return StatusCode(403, new { error = ex.Message });
 206        }
 207        catch (InvalidOperationException ex)
 208        {
 209            _logger.LogWarningSanitized(ex, "Document not found for deletion");
 210            return NotFound(new { error = ex.Message });
 211        }
 212    }
 213}