From c3384d76d92c6cdd89382bfa9631f8af8ef47927 Mon Sep 17 00:00:00 2001 From: namdh861 Date: Mon, 17 Aug 2026 21:02:25 +0700 Subject: [PATCH] Convert Documents module to MediatR commands/queries Replaces IDocumentService/DocumentService. The permission-check and descendant-deletion logic moves to a shared DocumentAccess static helper used by all seven handlers. Co-Authored-By: Claude Sonnet 5 --- mws.api/Controllers/DocumentsController.cs | 21 +- .../Documents/Commands/CreateDocument.cs | 57 +++++ .../Documents/Commands/DeleteDocument.cs | 20 ++ .../Documents/Commands/MoveDocument.cs | 51 ++++ .../Documents/Commands/UpdateDocument.cs | 40 +++ mws.application/Documents/DocumentAccess.cs | 71 ++++++ mws.application/Documents/DocumentService.cs | 229 ------------------ mws.application/Documents/IDocumentService.cs | 14 -- .../Documents/Queries/GetDocument.cs | 16 ++ .../Documents/Queries/GetDocumentTree.cs | 35 +++ .../Documents/Queries/SearchDocuments.cs | 17 ++ mws.infrastructure/DependencyInjection.cs | 2 - 12 files changed, 318 insertions(+), 255 deletions(-) create mode 100644 mws.application/Documents/Commands/CreateDocument.cs create mode 100644 mws.application/Documents/Commands/DeleteDocument.cs create mode 100644 mws.application/Documents/Commands/MoveDocument.cs create mode 100644 mws.application/Documents/Commands/UpdateDocument.cs create mode 100644 mws.application/Documents/DocumentAccess.cs delete mode 100644 mws.application/Documents/DocumentService.cs delete mode 100644 mws.application/Documents/IDocumentService.cs create mode 100644 mws.application/Documents/Queries/GetDocument.cs create mode 100644 mws.application/Documents/Queries/GetDocumentTree.cs create mode 100644 mws.application/Documents/Queries/SearchDocuments.cs diff --git a/mws.api/Controllers/DocumentsController.cs b/mws.api/Controllers/DocumentsController.cs index 7b4d24f..83237be 100644 --- a/mws.api/Controllers/DocumentsController.cs +++ b/mws.api/Controllers/DocumentsController.cs @@ -1,3 +1,4 @@ +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Mws.Application.Documents; @@ -7,50 +8,50 @@ namespace Mws.Api.Controllers; [ApiController] [Route("api")] [Authorize] -public class DocumentsController(IDocumentService documentService) : ControllerBase +public class DocumentsController(ISender sender) : ControllerBase { [HttpGet("documents/search")] public async Task>> Search([FromQuery] string? q, CancellationToken ct) { - return Ok(await documentService.SearchAsync(User.GetUserId(), q ?? string.Empty, ct)); + return Ok(await sender.Send(new SearchDocumentsQuery(User.GetUserId(), q ?? string.Empty), ct)); } [HttpGet("projects/{projectId:guid}/documents")] public async Task>> GetTree(Guid projectId, CancellationToken ct) { - return Ok(await documentService.GetTreeAsync(User.GetUserId(), projectId, ct)); + return Ok(await sender.Send(new GetDocumentTreeQuery(User.GetUserId(), projectId), ct)); } [HttpPost("projects/{projectId:guid}/documents")] public async Task> Create(Guid projectId, [FromBody] CreateDocumentRequest request, CancellationToken ct) { - var result = await documentService.CreateAsync(User.GetUserId(), projectId, request, ct); + var result = await sender.Send(new CreateDocumentCommand(User.GetUserId(), projectId, request), ct); return CreatedAtAction(nameof(Get), new { id = result.Id }, result); } [HttpGet("documents/{id:guid}")] public async Task> Get(Guid id, CancellationToken ct) { - return Ok(await documentService.GetAsync(User.GetUserId(), id, ct)); + return Ok(await sender.Send(new GetDocumentQuery(User.GetUserId(), id), ct)); } [HttpPut("documents/{id:guid}")] public async Task> Update(Guid id, [FromBody] UpdateDocumentRequest request, CancellationToken ct) { - return Ok(await documentService.UpdateAsync(User.GetUserId(), id, request, ct)); + return Ok(await sender.Send(new UpdateDocumentCommand(User.GetUserId(), id, request), ct)); } [HttpPut("documents/{id:guid}/move")] public async Task> Move(Guid id, [FromBody] MoveDocumentRequest request, CancellationToken ct) { - await documentService.MoveAsync(User.GetUserId(), id, request.NewParentId, ct); - return Ok(await documentService.GetAsync(User.GetUserId(), id, ct)); + await sender.Send(new MoveDocumentCommand(User.GetUserId(), id, request.NewParentId), ct); + return Ok(await sender.Send(new GetDocumentQuery(User.GetUserId(), id), ct)); } [HttpDelete("documents/{id:guid}")] public async Task Delete(Guid id, CancellationToken ct) { - await documentService.DeleteAsync(User.GetUserId(), id, ct); + await sender.Send(new DeleteDocumentCommand(User.GetUserId(), id), ct); return NoContent(); } -} \ No newline at end of file +} diff --git a/mws.application/Documents/Commands/CreateDocument.cs b/mws.application/Documents/Commands/CreateDocument.cs new file mode 100644 index 0000000..01e8571 --- /dev/null +++ b/mws.application/Documents/Commands/CreateDocument.cs @@ -0,0 +1,57 @@ +using AutoMapper; +using MediatR; +using Mws.Application.Common; +using Mws.Application.Permissions; +using Mws.Domain.Documents; + +namespace Mws.Application.Documents; + +public record CreateDocumentCommand(Guid UserId, Guid ProjectId, CreateDocumentRequest Request) : IRequest; + +public class CreateDocumentHandler(IUnitOfWork uow, IMapper mapper) : IRequestHandler +{ + public async Task Handle(CreateDocumentCommand command, CancellationToken ct) + { + await DocumentAccess.EnsureDocumentPermissionAsync(uow, command.UserId, command.ProjectId, PermissionAction.Create, ct); + + var request = command.Request; + if (string.IsNullOrWhiteSpace(request.Title)) + { + throw new BadRequestException("Title is required"); + } + + if (request.ParentId is { } parentId) + { + var parent = await uow.Documents.GetInProjectAsync(parentId, command.ProjectId, ct) + ?? throw new BadRequestException("Parent document not found"); + if (parent.Type != DocumentType.Folder) + { + throw new BadRequestException("Parent must be a folder"); + } + } + + if (request.Type == DocumentType.Folder && !string.IsNullOrWhiteSpace(request.Content)) + { + throw new BadRequestException("Folders cannot have content"); + } + + var now = DateTime.UtcNow; + var doc = new Document + { + Id = Guid.NewGuid(), + ProjectId = command.ProjectId, + ParentId = request.ParentId, + Title = request.Title.Trim(), + Type = request.Type, + Content = request.Type == DocumentType.Document ? request.Content : null, + CreatedBy = command.UserId, + CreatedAt = now, + UpdatedBy = command.UserId, + UpdatedAt = now, + }; + + uow.Documents.Add(doc); + await uow.SaveChangesAsync(ct); + return mapper.Map(doc); + } +} diff --git a/mws.application/Documents/Commands/DeleteDocument.cs b/mws.application/Documents/Commands/DeleteDocument.cs new file mode 100644 index 0000000..f0caf08 --- /dev/null +++ b/mws.application/Documents/Commands/DeleteDocument.cs @@ -0,0 +1,20 @@ +using MediatR; +using Mws.Application.Common; +using Mws.Application.Permissions; + +namespace Mws.Application.Documents; + +public record DeleteDocumentCommand(Guid UserId, Guid DocumentId) : IRequest; + +public class DeleteDocumentHandler(IUnitOfWork uow) : IRequestHandler +{ + public async Task Handle(DeleteDocumentCommand command, CancellationToken ct) + { + var doc = await DocumentAccess.GetDocumentForUserAsync(uow, command.UserId, command.DocumentId, ct); + await DocumentAccess.EnsureDocumentPermissionAsync(uow, command.UserId, doc.ProjectId, PermissionAction.Delete, ct); + + await DocumentAccess.DeleteDescendantsAsync(uow, command.DocumentId, ct); + uow.Documents.Remove(doc); + await uow.SaveChangesAsync(ct); + } +} diff --git a/mws.application/Documents/Commands/MoveDocument.cs b/mws.application/Documents/Commands/MoveDocument.cs new file mode 100644 index 0000000..79ef864 --- /dev/null +++ b/mws.application/Documents/Commands/MoveDocument.cs @@ -0,0 +1,51 @@ +using MediatR; +using Mws.Application.Common; +using Mws.Application.Permissions; +using Mws.Domain.Documents; + +namespace Mws.Application.Documents; + +public record MoveDocumentCommand(Guid UserId, Guid DocumentId, Guid? NewParentId) : IRequest; + +public class MoveDocumentHandler(IUnitOfWork uow) : IRequestHandler +{ + public async Task Handle(MoveDocumentCommand command, CancellationToken ct) + { + var doc = await DocumentAccess.GetDocumentForUserAsync(uow, command.UserId, command.DocumentId, ct); + await DocumentAccess.EnsureDocumentPermissionAsync(uow, command.UserId, doc.ProjectId, PermissionAction.Edit, ct); + + if (command.NewParentId == command.DocumentId) + { + throw new BadRequestException("A document cannot be moved into itself"); + } + + if (command.NewParentId is { } parentId) + { + var parent = await uow.Documents.GetByIdAsync(parentId, ct) + ?? throw new NotFoundException("Parent folder not found"); + if (parent.ProjectId != doc.ProjectId) + { + throw new BadRequestException("Parent must belong to the same project"); + } + if (parent.Type != DocumentType.Folder) + { + throw new BadRequestException("Parent must be a folder"); + } + + var cursor = parent.ParentId; + while (cursor is not null) + { + if (cursor == command.DocumentId) + { + throw new BadRequestException("A folder cannot be moved into its own descendant"); + } + cursor = await uow.Documents.GetParentIdAsync(cursor.Value, ct); + } + } + + doc.ParentId = command.NewParentId; + doc.UpdatedAt = DateTime.UtcNow; + doc.UpdatedBy = command.UserId; + await uow.SaveChangesAsync(ct); + } +} diff --git a/mws.application/Documents/Commands/UpdateDocument.cs b/mws.application/Documents/Commands/UpdateDocument.cs new file mode 100644 index 0000000..57eed4c --- /dev/null +++ b/mws.application/Documents/Commands/UpdateDocument.cs @@ -0,0 +1,40 @@ +using AutoMapper; +using MediatR; +using Mws.Application.Common; +using Mws.Application.Permissions; +using Mws.Domain.Documents; + +namespace Mws.Application.Documents; + +public record UpdateDocumentCommand(Guid UserId, Guid DocumentId, UpdateDocumentRequest Request) : IRequest; + +public class UpdateDocumentHandler(IUnitOfWork uow, IMapper mapper) : IRequestHandler +{ + public async Task Handle(UpdateDocumentCommand command, CancellationToken ct) + { + var doc = await DocumentAccess.GetDocumentForUserAsync(uow, command.UserId, command.DocumentId, ct); + await DocumentAccess.EnsureDocumentPermissionAsync(uow, command.UserId, doc.ProjectId, PermissionAction.Edit, ct); + + var request = command.Request; + if (string.IsNullOrWhiteSpace(request.Title)) + { + throw new BadRequestException("Title is required"); + } + + doc.Title = request.Title.Trim(); + if (doc.Type == DocumentType.Document) + { + doc.Content = request.Content; + doc.UpdatedAt = DateTime.UtcNow; + doc.UpdatedBy = command.UserId; + } + else + { + doc.UpdatedAt = DateTime.UtcNow; + doc.UpdatedBy = command.UserId; + } + + await uow.SaveChangesAsync(ct); + return mapper.Map(doc); + } +} diff --git a/mws.application/Documents/DocumentAccess.cs b/mws.application/Documents/DocumentAccess.cs new file mode 100644 index 0000000..cf14bdb --- /dev/null +++ b/mws.application/Documents/DocumentAccess.cs @@ -0,0 +1,71 @@ +using Mws.Application.Common; +using Mws.Application.Permissions; +using Mws.Domain.Documents; +using Mws.Domain.Projects; + +namespace Mws.Application.Documents; + +internal static class DocumentAccess +{ + public static async Task EnsureDocumentPermissionAsync( + IUnitOfWork uow, Guid userId, Guid projectId, PermissionAction action, CancellationToken ct) + { + var member = await uow.Projects.GetMemberAsync(projectId, userId, ct) + ?? throw new NotFoundException("Project not found"); + + if (!await HasDocumentPermissionAsync(uow, member, action, ct)) + { + throw new ForbiddenException("You do not have permission to access documents in this project"); + } + } + + public static async Task HasDocumentPermissionAsync( + IUnitOfWork uow, ProjectMember member, PermissionAction action, CancellationToken ct) + { + if (member.Role == MemberRole.Owner) + { + return true; + } + + var permission = await uow.Projects.GetMemberPermissionAsync(member.ProjectId, member.UserId, ProjectPermissionScreens.Documents, ct); + + return action switch + { + PermissionAction.View => permission?.CanView ?? false, + PermissionAction.Create => permission?.CanCreate ?? false, + PermissionAction.Edit => permission?.CanEdit ?? false, + PermissionAction.Delete => permission?.CanDelete ?? false, + _ => false, + }; + } + + public static async Task GetDocumentForUserAsync(IUnitOfWork uow, Guid userId, Guid documentId, CancellationToken ct) + { + var doc = await uow.Documents.GetByIdAsync(documentId, ct) + ?? throw new NotFoundException("Document not found"); + + var member = await uow.Projects.GetMemberAsync(doc.ProjectId, userId, ct); + + if (member is null) + { + throw new NotFoundException("Document not found"); + } + + if (!await HasDocumentPermissionAsync(uow, member, PermissionAction.View, ct)) + { + throw new ForbiddenException("You do not have permission to view this document"); + } + + return doc; + } + + public static async Task DeleteDescendantsAsync(IUnitOfWork uow, Guid parentId, CancellationToken ct) + { + var children = await uow.Documents.GetChildrenAsync(parentId, ct); + foreach (var child in children) + { + await DeleteDescendantsAsync(uow, child.Id, ct); + uow.Documents.Remove(child); + } + } +} diff --git a/mws.application/Documents/DocumentService.cs b/mws.application/Documents/DocumentService.cs deleted file mode 100644 index 75bce90..0000000 --- a/mws.application/Documents/DocumentService.cs +++ /dev/null @@ -1,229 +0,0 @@ -using AutoMapper; -using Mws.Application.Common; -using Mws.Application.Permissions; -using Mws.Domain.Documents; -using Mws.Domain.Projects; - -namespace Mws.Application.Documents; - -public class DocumentService(IUnitOfWork uow, IMapper mapper) : IDocumentService -{ - public async Task> GetTreeAsync(Guid userId, Guid projectId, CancellationToken ct = default) - { - await EnsureDocumentPermissionAsync(userId, projectId, PermissionAction.View, ct); - - var docs = await uow.Documents.GetTreeForProjectAsync(projectId, ct); - - var nodes = docs.ToDictionary(d => d.Id, d => mapper.Map(d)); - - var roots = new List(); - foreach (var node in nodes.Values) - { - if (node.ParentId is { } parentId && nodes.TryGetValue(parentId, out var parent)) - { - parent.Children.Add(node); - } - else - { - roots.Add(node); - } - } - - return roots; - } - - public async Task GetAsync(Guid userId, Guid documentId, CancellationToken ct = default) - { - var doc = await GetDocumentForUserAsync(userId, documentId, ct); - return mapper.Map(doc); - } - - public async Task CreateAsync(Guid userId, Guid projectId, CreateDocumentRequest request, CancellationToken ct = default) - { - await EnsureDocumentPermissionAsync(userId, projectId, PermissionAction.Create, ct); - - if (string.IsNullOrWhiteSpace(request.Title)) - { - throw new BadRequestException("Title is required"); - } - - if (request.ParentId is { } parentId) - { - var parent = await uow.Documents.GetInProjectAsync(parentId, projectId, ct) - ?? throw new BadRequestException("Parent document not found"); - if (parent.Type != DocumentType.Folder) - { - throw new BadRequestException("Parent must be a folder"); - } - } - - if (request.Type == DocumentType.Folder && !string.IsNullOrWhiteSpace(request.Content)) - { - throw new BadRequestException("Folders cannot have content"); - } - - var now = DateTime.UtcNow; - var doc = new Document - { - Id = Guid.NewGuid(), - ProjectId = projectId, - ParentId = request.ParentId, - Title = request.Title.Trim(), - Type = request.Type, - Content = request.Type == DocumentType.Document ? request.Content : null, - CreatedBy = userId, - CreatedAt = now, - UpdatedBy = userId, - UpdatedAt = now, - }; - - uow.Documents.Add(doc); - await uow.SaveChangesAsync(ct); - return mapper.Map(doc); - } - - public async Task UpdateAsync(Guid userId, Guid documentId, UpdateDocumentRequest request, CancellationToken ct = default) - { - var doc = await GetDocumentForUserAsync(userId, documentId, ct); - await EnsureDocumentPermissionAsync(userId, doc.ProjectId, PermissionAction.Edit, ct); - - if (string.IsNullOrWhiteSpace(request.Title)) - { - throw new BadRequestException("Title is required"); - } - - doc.Title = request.Title.Trim(); - if (doc.Type == DocumentType.Document) - { - doc.Content = request.Content; - doc.UpdatedAt = DateTime.UtcNow; - doc.UpdatedBy = userId; - } - else - { - doc.UpdatedAt = DateTime.UtcNow; - doc.UpdatedBy = userId; - } - - await uow.SaveChangesAsync(ct); - return mapper.Map(doc); - } - - public async Task MoveAsync(Guid userId, Guid documentId, Guid? newParentId, CancellationToken ct = default) - { - var doc = await GetDocumentForUserAsync(userId, documentId, ct); - await EnsureDocumentPermissionAsync(userId, doc.ProjectId, PermissionAction.Edit, ct); - - if (newParentId == documentId) - { - throw new BadRequestException("A document cannot be moved into itself"); - } - - if (newParentId is { } parentId) - { - var parent = await uow.Documents.GetByIdAsync(parentId, ct) - ?? throw new NotFoundException("Parent folder not found"); - if (parent.ProjectId != doc.ProjectId) - { - throw new BadRequestException("Parent must belong to the same project"); - } - if (parent.Type != DocumentType.Folder) - { - throw new BadRequestException("Parent must be a folder"); - } - - var cursor = parent.ParentId; - while (cursor is not null) - { - if (cursor == documentId) - { - throw new BadRequestException("A folder cannot be moved into its own descendant"); - } - cursor = await uow.Documents.GetParentIdAsync(cursor.Value, ct); - } - } - - doc.ParentId = newParentId; - doc.UpdatedAt = DateTime.UtcNow; - doc.UpdatedBy = userId; - await uow.SaveChangesAsync(ct); - } - - public async Task DeleteAsync(Guid userId, Guid documentId, CancellationToken ct = default) - { - var doc = await GetDocumentForUserAsync(userId, documentId, ct); - await EnsureDocumentPermissionAsync(userId, doc.ProjectId, PermissionAction.Delete, ct); - - await DeleteDescendantsAsync(documentId, ct); - uow.Documents.Remove(doc); - await uow.SaveChangesAsync(ct); - } - - public async Task> SearchAsync(Guid userId, string term, CancellationToken ct = default) - { - var projectIds = await uow.Projects.GetDocumentViewableProjectIdsAsync(userId, ct); - var results = await uow.Documents.SearchAsync(projectIds, term, 20, ct); - return mapper.Map>(results); - } - - private async Task DeleteDescendantsAsync(Guid parentId, CancellationToken ct) - { - var children = await uow.Documents.GetChildrenAsync(parentId, ct); - foreach (var child in children) - { - await DeleteDescendantsAsync(child.Id, ct); - uow.Documents.Remove(child); - } - } - - private async Task EnsureDocumentPermissionAsync( - Guid userId, Guid projectId, PermissionAction action, CancellationToken ct = default) - { - var member = await uow.Projects.GetMemberAsync(projectId, userId, ct) - ?? throw new NotFoundException("Project not found"); - - if (!await HasDocumentPermissionAsync(member, action, ct)) - { - throw new ForbiddenException("You do not have permission to access documents in this project"); - } - } - - private async Task HasDocumentPermissionAsync(ProjectMember member, PermissionAction action, CancellationToken ct) - { - if (member.Role == MemberRole.Owner) - { - return true; - } - - var permission = await uow.Projects.GetMemberPermissionAsync(member.ProjectId, member.UserId, ProjectPermissionScreens.Documents, ct); - - return action switch - { - PermissionAction.View => permission?.CanView ?? false, - PermissionAction.Create => permission?.CanCreate ?? false, - PermissionAction.Edit => permission?.CanEdit ?? false, - PermissionAction.Delete => permission?.CanDelete ?? false, - _ => false, - }; - } - - private async Task GetDocumentForUserAsync(Guid userId, Guid documentId, CancellationToken ct = default) - { - var doc = await uow.Documents.GetByIdAsync(documentId, ct) - ?? throw new NotFoundException("Document not found"); - - var member = await uow.Projects.GetMemberAsync(doc.ProjectId, userId, ct); - - if (member is null) - { - throw new NotFoundException("Document not found"); - } - - if (!await HasDocumentPermissionAsync(member, PermissionAction.View, ct)) - { - throw new ForbiddenException("You do not have permission to view this document"); - } - - return doc; - } -} diff --git a/mws.application/Documents/IDocumentService.cs b/mws.application/Documents/IDocumentService.cs deleted file mode 100644 index 79a3418..0000000 --- a/mws.application/Documents/IDocumentService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Mws.Application.Common; - -namespace Mws.Application.Documents; - -public interface IDocumentService -{ - Task> GetTreeAsync(Guid userId, Guid projectId, CancellationToken ct = default); - Task GetAsync(Guid userId, Guid documentId, CancellationToken ct = default); - Task CreateAsync(Guid userId, Guid projectId, CreateDocumentRequest request, CancellationToken ct = default); - Task UpdateAsync(Guid userId, Guid documentId, UpdateDocumentRequest request, CancellationToken ct = default); - Task MoveAsync(Guid userId, Guid documentId, Guid? newParentId, CancellationToken ct = default); - Task DeleteAsync(Guid userId, Guid documentId, CancellationToken ct = default); - Task> SearchAsync(Guid userId, string term, CancellationToken ct = default); -} \ No newline at end of file diff --git a/mws.application/Documents/Queries/GetDocument.cs b/mws.application/Documents/Queries/GetDocument.cs new file mode 100644 index 0000000..77c9d0c --- /dev/null +++ b/mws.application/Documents/Queries/GetDocument.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using MediatR; +using Mws.Application.Common; + +namespace Mws.Application.Documents; + +public record GetDocumentQuery(Guid UserId, Guid DocumentId) : IRequest; + +public class GetDocumentHandler(IUnitOfWork uow, IMapper mapper) : IRequestHandler +{ + public async Task Handle(GetDocumentQuery query, CancellationToken ct) + { + var doc = await DocumentAccess.GetDocumentForUserAsync(uow, query.UserId, query.DocumentId, ct); + return mapper.Map(doc); + } +} diff --git a/mws.application/Documents/Queries/GetDocumentTree.cs b/mws.application/Documents/Queries/GetDocumentTree.cs new file mode 100644 index 0000000..86e1aed --- /dev/null +++ b/mws.application/Documents/Queries/GetDocumentTree.cs @@ -0,0 +1,35 @@ +using AutoMapper; +using MediatR; +using Mws.Application.Common; +using Mws.Application.Permissions; + +namespace Mws.Application.Documents; + +public record GetDocumentTreeQuery(Guid UserId, Guid ProjectId) : IRequest>; + +public class GetDocumentTreeHandler(IUnitOfWork uow, IMapper mapper) : IRequestHandler> +{ + public async Task> Handle(GetDocumentTreeQuery query, CancellationToken ct) + { + await DocumentAccess.EnsureDocumentPermissionAsync(uow, query.UserId, query.ProjectId, PermissionAction.View, ct); + + var docs = await uow.Documents.GetTreeForProjectAsync(query.ProjectId, ct); + + var nodes = docs.ToDictionary(d => d.Id, d => mapper.Map(d)); + + var roots = new List(); + foreach (var node in nodes.Values) + { + if (node.ParentId is { } parentId && nodes.TryGetValue(parentId, out var parent)) + { + parent.Children.Add(node); + } + else + { + roots.Add(node); + } + } + + return roots; + } +} diff --git a/mws.application/Documents/Queries/SearchDocuments.cs b/mws.application/Documents/Queries/SearchDocuments.cs new file mode 100644 index 0000000..dcfcb8b --- /dev/null +++ b/mws.application/Documents/Queries/SearchDocuments.cs @@ -0,0 +1,17 @@ +using AutoMapper; +using MediatR; +using Mws.Application.Common; + +namespace Mws.Application.Documents; + +public record SearchDocumentsQuery(Guid UserId, string Term) : IRequest>; + +public class SearchDocumentsHandler(IUnitOfWork uow, IMapper mapper) : IRequestHandler> +{ + public async Task> Handle(SearchDocumentsQuery query, CancellationToken ct) + { + var projectIds = await uow.Projects.GetDocumentViewableProjectIdsAsync(query.UserId, ct); + var results = await uow.Documents.SearchAsync(projectIds, query.Term, 20, ct); + return mapper.Map>(results); + } +} diff --git a/mws.infrastructure/DependencyInjection.cs b/mws.infrastructure/DependencyInjection.cs index ea4e72c..3f15e3e 100644 --- a/mws.infrastructure/DependencyInjection.cs +++ b/mws.infrastructure/DependencyInjection.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Mws.Application.Auth; using Mws.Application.Common; -using Mws.Application.Documents; using Mws.Application.Permissions; using Mws.Application.Projects; using Mws.Application.Tasks; @@ -39,7 +38,6 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); return services;