Commit MWS backend source tree
The ASP.NET Core solution (mws.api/mws.application/mws.domain/ mws.infrastructure) existed only as untracked working files. Adding it to version control, plus a .gitignore for build output and local tooling directories, so the CQRS/mediator refactor plan has a real git history to branch and diff against.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mws.Application.Common.Repositories;
|
||||
using Mws.Domain.Documents;
|
||||
|
||||
namespace Mws.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public class DocumentRepository(AppDbContext db) : RepositoryBase<Document>(db), IDocumentRepository
|
||||
{
|
||||
public Task<Document?> GetByIdAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(d => d.Id == id, ct);
|
||||
|
||||
public Task<List<Document>> GetTreeForProjectAsync(Guid projectId, CancellationToken ct = default) =>
|
||||
Set.Where(d => d.ProjectId == projectId).OrderBy(d => d.Title).ToListAsync(ct);
|
||||
|
||||
public Task<Document?> GetInProjectAsync(Guid parentId, Guid projectId, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(d => d.Id == parentId && d.ProjectId == projectId, ct);
|
||||
|
||||
public Task<Guid?> GetParentIdAsync(Guid documentId, CancellationToken ct = default) =>
|
||||
Set.Where(d => d.Id == documentId).Select(d => d.ParentId).SingleAsync(ct);
|
||||
|
||||
public Task<List<Document>> GetChildrenAsync(Guid parentId, CancellationToken ct = default) =>
|
||||
Set.Where(d => d.ParentId == parentId).ToListAsync(ct);
|
||||
|
||||
public Task<List<Document>> SearchAsync(List<Guid> projectIds, string? term, int take, CancellationToken ct = default)
|
||||
{
|
||||
var query = Set.Where(d => projectIds.Contains(d.ProjectId));
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
var lower = term.Trim().ToLower();
|
||||
query = query.Where(d => d.Title.ToLower().Contains(lower));
|
||||
}
|
||||
|
||||
return query.OrderByDescending(d => d.UpdatedAt).Take(take).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public Task<int> CountByTypeForProjectAsync(Guid projectId, DocumentType type, CancellationToken ct = default) =>
|
||||
Set.CountAsync(d => d.ProjectId == projectId && d.Type == type, ct);
|
||||
|
||||
public Task<List<Document>> GetRecentForProjectAsync(Guid projectId, DocumentType type, int take, CancellationToken ct = default) =>
|
||||
Set.Where(d => d.ProjectId == projectId && d.Type == type).OrderByDescending(d => d.UpdatedAt).Take(take).ToListAsync(ct);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mws.Application.Common.Repositories;
|
||||
using Mws.Domain.Projects;
|
||||
|
||||
namespace Mws.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public class ProjectRepository(AppDbContext db) : RepositoryBase<Project>(db), IProjectRepository
|
||||
{
|
||||
public Task<Project?> GetByIdAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(p => p.Id == id, ct);
|
||||
|
||||
public Task<Project?> GetForUserAsync(Guid userId, Guid projectId, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(p => p.Id == projectId && p.Members.Any(m => m.UserId == userId), ct);
|
||||
|
||||
public Task<List<Project>> GetForUserAsync(Guid userId, CancellationToken ct = default) =>
|
||||
Set.Where(p => p.Members.Any(m => m.UserId == userId))
|
||||
.OrderByDescending(p => p.UpdatedAt)
|
||||
.ToListAsync(ct);
|
||||
|
||||
public Task<List<Project>> SearchForUserAsync(Guid userId, string? term, CancellationToken ct = default)
|
||||
{
|
||||
var query = Set.Where(p => p.Members.Any(m => m.UserId == userId));
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
var lower = term.Trim().ToLower();
|
||||
query = query.Where(p => p.Name.ToLower().Contains(lower));
|
||||
}
|
||||
|
||||
return query.OrderByDescending(p => p.UpdatedAt).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public Task<int> CountMembersAsync(Guid projectId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers.CountAsync(m => m.ProjectId == projectId, ct);
|
||||
|
||||
public Task<bool> IsMemberAsync(Guid projectId, Guid userId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers.AnyAsync(m => m.ProjectId == projectId && m.UserId == userId, ct);
|
||||
|
||||
public Task<MemberRole?> GetMemberRoleAsync(Guid projectId, Guid userId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers
|
||||
.Where(m => m.ProjectId == projectId && m.UserId == userId)
|
||||
.Select(m => (MemberRole?)m.Role)
|
||||
.SingleOrDefaultAsync(ct);
|
||||
|
||||
public Task<ProjectMember?> GetMemberAsync(Guid projectId, Guid userId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers.FirstOrDefaultAsync(m => m.ProjectId == projectId && m.UserId == userId, ct);
|
||||
|
||||
public Task<ProjectMember?> GetMemberWithUserAsync(Guid projectId, Guid userId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers.Include(m => m.User).FirstOrDefaultAsync(m => m.ProjectId == projectId && m.UserId == userId, ct);
|
||||
|
||||
public Task<List<ProjectMember>> GetMembersWithUserAsync(Guid projectId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers
|
||||
.Where(m => m.ProjectId == projectId)
|
||||
.Include(m => m.User)
|
||||
.OrderByDescending(m => m.Role)
|
||||
.ThenBy(m => m.User.DisplayName)
|
||||
.ToListAsync(ct);
|
||||
|
||||
public Task<int> CountOwnersAsync(Guid projectId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers.CountAsync(m => m.ProjectId == projectId && m.Role == MemberRole.Owner, ct);
|
||||
|
||||
public Task<List<Guid>> GetProjectIdsForUserAsync(Guid userId, CancellationToken ct = default) =>
|
||||
Set.Where(p => p.Members.Any(m => m.UserId == userId)).Select(p => p.Id).ToListAsync(ct);
|
||||
|
||||
public Task<List<Guid>> GetOwnedProjectIdsAsync(Guid userId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers.Where(m => m.UserId == userId && m.Role == MemberRole.Owner).Select(m => m.ProjectId).ToListAsync(ct);
|
||||
|
||||
public Task<List<Guid>> GetDocumentViewableProjectIdsAsync(Guid userId, CancellationToken ct = default) =>
|
||||
Db.ProjectMembers
|
||||
.Where(m => m.UserId == userId &&
|
||||
(m.Role == MemberRole.Owner ||
|
||||
Db.ProjectMemberPermissions.Any(p => p.ProjectId == m.ProjectId && p.UserId == userId && p.Screen == ProjectPermissionScreens.Documents && p.CanView)))
|
||||
.Select(m => m.ProjectId)
|
||||
.ToListAsync(ct);
|
||||
|
||||
public void AddMember(ProjectMember member) => Db.ProjectMembers.Add(member);
|
||||
|
||||
public void RemoveMember(ProjectMember member) => Db.ProjectMembers.Remove(member);
|
||||
|
||||
public Task<ProjectMemberPermission?> GetMemberPermissionAsync(Guid projectId, Guid userId, string screen, CancellationToken ct = default) =>
|
||||
Db.ProjectMemberPermissions.FirstOrDefaultAsync(p => p.ProjectId == projectId && p.UserId == userId && p.Screen == screen, ct);
|
||||
|
||||
public Task<Dictionary<Guid, ProjectMemberPermission>> GetMemberPermissionsAsync(Guid projectId, string screen, CancellationToken ct = default) =>
|
||||
Db.ProjectMemberPermissions
|
||||
.Where(p => p.ProjectId == projectId && p.Screen == screen)
|
||||
.ToDictionaryAsync(p => p.UserId, ct);
|
||||
|
||||
public void AddMemberPermission(ProjectMemberPermission permission) => Db.ProjectMemberPermissions.Add(permission);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mws.Application.Common.Repositories;
|
||||
|
||||
namespace Mws.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public abstract class RepositoryBase<T>(AppDbContext db) : IRepository<T> where T : class
|
||||
{
|
||||
protected readonly AppDbContext Db = db;
|
||||
protected DbSet<T> Set => Db.Set<T>();
|
||||
|
||||
public void Add(T entity) => Set.Add(entity);
|
||||
public void Remove(T entity) => Set.Remove(entity);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mws.Application.Common.Repositories;
|
||||
using Mws.Domain.Roles;
|
||||
|
||||
namespace Mws.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public class RoleRepository(AppDbContext db) : RepositoryBase<Role>(db), IRoleRepository
|
||||
{
|
||||
public Task<List<Role>> GetAllWithPermissionsAsync(CancellationToken ct = default) =>
|
||||
Set.Include(r => r.Permissions).OrderBy(r => r.Name).ToListAsync(ct);
|
||||
|
||||
public Task<Role?> GetByIdWithPermissionsAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.Include(r => r.Permissions).FirstOrDefaultAsync(r => r.Id == id, ct);
|
||||
|
||||
public Task<Role?> GetByIdAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(r => r.Id == id, ct);
|
||||
|
||||
public Task<bool> ExistsByNameAsync(string name, Guid? excludeId, CancellationToken ct = default) =>
|
||||
Set.AnyAsync(r => r.Name == name && (excludeId == null || r.Id != excludeId), ct);
|
||||
|
||||
public Task<RolePermission?> GetPermissionAsync(Guid roleId, string screen, CancellationToken ct = default) =>
|
||||
Db.RolePermissions.FirstOrDefaultAsync(p => p.RoleId == roleId && p.Screen == screen, ct);
|
||||
|
||||
public Task<Dictionary<string, RolePermission>> GetPermissionsAsync(Guid roleId, CancellationToken ct = default) =>
|
||||
Db.RolePermissions.Where(p => p.RoleId == roleId).ToDictionaryAsync(p => p.Screen, ct);
|
||||
|
||||
public void RemovePermissions(IEnumerable<RolePermission> permissions) => Db.RolePermissions.RemoveRange(permissions);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mws.Application.Common.Repositories;
|
||||
using Mws.Domain.Tasks;
|
||||
using TaskPriority = Mws.Domain.Tasks.TaskPriority;
|
||||
using TaskStatus = Mws.Domain.Tasks.TaskStatus;
|
||||
|
||||
namespace Mws.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public class TaskRepository(AppDbContext db) : RepositoryBase<TaskItem>(db), ITaskRepository
|
||||
{
|
||||
public Task<TaskItem?> GetByIdAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(t => t.Id == id, ct);
|
||||
|
||||
public Task<TaskItem?> GetWithAssigneeAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.Include(t => t.Assignee).FirstOrDefaultAsync(t => t.Id == id, ct);
|
||||
|
||||
public Task<List<TaskItem>> GetForProjectAsync(
|
||||
Guid projectId, TaskStatus? status, TaskPriority? priority, Guid? assigneeId, CancellationToken ct = default)
|
||||
{
|
||||
var query = Set.Where(t => t.ProjectId == projectId);
|
||||
|
||||
if (status.HasValue)
|
||||
{
|
||||
query = query.Where(t => t.Status == status);
|
||||
}
|
||||
if (priority.HasValue)
|
||||
{
|
||||
query = query.Where(t => t.Priority == priority);
|
||||
}
|
||||
if (assigneeId.HasValue)
|
||||
{
|
||||
query = query.Where(t => t.AssigneeId == assigneeId);
|
||||
}
|
||||
|
||||
return query.Include(t => t.Assignee).OrderByDescending(t => t.UpdatedAt).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public Task<List<TaskItem>> SearchWithAssigneeAsync(List<Guid> projectIds, string? term, int take, CancellationToken ct = default)
|
||||
{
|
||||
var query = Set.Where(t => projectIds.Contains(t.ProjectId));
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
var lower = term.Trim().ToLower();
|
||||
query = query.Where(t => t.Title.ToLower().Contains(lower));
|
||||
}
|
||||
|
||||
return query.Include(t => t.Assignee).OrderByDescending(t => t.UpdatedAt).Take(take).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<TaskStatus, int>> CountByStatusForProjectAsync(Guid projectId, CancellationToken ct = default)
|
||||
{
|
||||
return await Set.Where(t => t.ProjectId == projectId)
|
||||
.GroupBy(t => t.Status)
|
||||
.Select(g => new { Status = g.Key, Count = g.Count() })
|
||||
.ToDictionaryAsync(a => a.Status, a => a.Count, ct);
|
||||
}
|
||||
|
||||
public Task<List<TaskItem>> GetRecentForProjectAsync(Guid projectId, int take, CancellationToken ct = default) =>
|
||||
Set.Where(t => t.ProjectId == projectId).OrderByDescending(t => t.UpdatedAt).Take(take).ToListAsync(ct);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Mws.Application.Common.Repositories;
|
||||
using Mws.Domain.Users;
|
||||
|
||||
namespace Mws.Infrastructure.Persistence.Repositories;
|
||||
|
||||
public class UserRepository(AppDbContext db) : RepositoryBase<User>(db), IUserRepository
|
||||
{
|
||||
public Task<User?> GetByIdAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.FirstOrDefaultAsync(u => u.Id == id, ct);
|
||||
|
||||
public Task<User?> GetByIdWithRoleAsync(Guid id, CancellationToken ct = default) =>
|
||||
Set.Include(u => u.Role).FirstOrDefaultAsync(u => u.Id == id, ct);
|
||||
|
||||
public Task<User?> GetByUsernameWithRoleAsync(string username, CancellationToken ct = default) =>
|
||||
Set.Include(u => u.Role).SingleOrDefaultAsync(u => u.Username == username, ct);
|
||||
|
||||
public Task<bool> ExistsByUsernameAsync(string username, CancellationToken ct = default) =>
|
||||
Set.AnyAsync(u => u.Username == username, ct);
|
||||
|
||||
public Task<bool> ExistsByRoleIdAsync(Guid roleId, CancellationToken ct = default) =>
|
||||
Set.AnyAsync(u => u.RoleId == roleId, ct);
|
||||
|
||||
public Task<Guid> GetRoleIdAsync(Guid userId, CancellationToken ct = default) =>
|
||||
Set.Where(u => u.Id == userId).Select(u => u.RoleId).SingleOrDefaultAsync(ct);
|
||||
|
||||
public Task<List<User>> SearchWithRoleAsync(string? term, int? take, CancellationToken ct = default)
|
||||
{
|
||||
var query = Set.Include(u => u.Role).AsQueryable();
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
var lower = term.Trim().ToLower();
|
||||
query = query.Where(u => u.Username.ToLower().Contains(lower) || u.DisplayName.ToLower().Contains(lower));
|
||||
}
|
||||
|
||||
query = query.OrderBy(u => u.DisplayName);
|
||||
if (take is { } n)
|
||||
{
|
||||
query = query.Take(n);
|
||||
}
|
||||
|
||||
return query.ToListAsync(ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user