From 953e66faf0b065ef67bf897d63d4379d9a23c2cf Mon Sep 17 00:00:00 2001 From: Nickolas Gupton Date: Fri, 26 Jul 2024 00:50:11 -0500 Subject: [PATCH] Add more DB handlers --- .../Features/Identity/ChangeUserEmail.cs | 2 +- Bones.Api/Models/ApiQueryResponse.cs | 4 +- Bones.Database/BonesDbContext.cs | 26 ++-- Bones.Database/DbSets/Identity/BonesRole.cs | 4 +- .../DbSets/Identity/BonesRoleClaim.cs | 4 +- .../DbSets/Identity/BonesUserClaim.cs | 4 +- .../DbSets/Identity/BonesUserLogin.cs | 5 +- .../DbSets/Identity/BonesUserRole.cs | 4 +- .../DbSets/Identity/BonesUserToken.cs | 4 +- .../DbSets/ProjectManagement/Items/Item.cs | 5 + .../ProjectManagement/Items/ItemValue.cs | 5 + .../ProjectManagement/Items/ItemVersion.cs | 5 + .../ProjectManagement/Layouts/Layout.cs | 8 +- .../Layouts/LayoutVersion.cs | 6 + .../ProjectManagement/Projects/Project.cs | 6 + .../DbSets/ProjectManagement/Queues/Queue.cs | 5 + .../Operations/Identity/ChangeUserEmailDb.cs | 2 +- .../ClearEmailVerificationsForUserDb.cs | 2 +- .../ClearExpiredEmailVerificationsDb.cs | 2 +- .../Identity/CreateEmailVerificationDb.cs | 10 +- .../Operations/Identity/CreateUserDb.cs | 2 +- .../Identity/EmailAvailableForUseDb.cs | 2 +- .../Operations/Identity/GetUserByIdDb.cs | 2 +- .../Operations/Identity/VerifyUserEmailDb.cs | 10 +- .../Initiatives/CreateInitiativeDb.cs | 4 +- .../Initiatives/DeleteInitiativeDb.cs | 43 ++++++- .../Initiatives/UpdateInitiativeDb.cs | 49 +++++++- .../ProjectManagement/Items/CreateItemDb.cs | 10 +- .../Items/CreateItemVersionDb.cs | 115 ++++++++++++++++++ .../ProjectManagement/Items/CreateTagDb.cs | 2 +- .../ProjectManagement/Items/DeleteItemDb.cs | 52 +++++++- .../Items/DeleteItemVersionDb.cs | 55 +++++++++ .../ProjectManagement/Items/DeleteTagDb.cs | 42 ++++++- .../ProjectManagement/Items/UpdateItemDb.cs | 88 +++++++++++++- .../ProjectManagement/Items/UpdateTagDb.cs | 49 +++++++- .../Layouts/CreateLayoutDb.cs | 3 +- .../Layouts/DeleteLayoutDb.cs | 49 +++++++- .../Layouts/UpdateLayoutDb.cs | 49 +++++++- .../Projects/CreateProjectDb.cs | 2 +- .../Projects/DeleteProjectDb.cs | 50 +++++++- .../Projects/UpdateProjectDb.cs | 48 +++++++- .../ProjectManagement/Queues/CreateQueueDb.cs | 2 +- .../ProjectManagement/Queues/DeleteQueueDb.cs | 53 +++++++- .../ProjectManagement/Queues/UpdateQueueDb.cs | 49 +++++++- Bones.Shared/Bones.Shared.csproj | 2 +- Bones.Shared/Exceptions/BonesException.cs | 7 ++ Bones.Shared/Exceptions/BonesExceptionBase.cs | 7 -- .../Exceptions/RecoverableException.cs | 7 -- .../Exceptions/UnrecoverableException.cs | 7 -- Bones.Shared/Models/CommandResponse.cs | 6 +- Bones.sln.DotSettings | 2 + 51 files changed, 881 insertions(+), 100 deletions(-) create mode 100644 Bones.Database/Operations/ProjectManagement/Items/CreateItemVersionDb.cs create mode 100644 Bones.Database/Operations/ProjectManagement/Items/DeleteItemVersionDb.cs create mode 100644 Bones.Shared/Exceptions/BonesException.cs delete mode 100644 Bones.Shared/Exceptions/BonesExceptionBase.cs delete mode 100644 Bones.Shared/Exceptions/RecoverableException.cs delete mode 100644 Bones.Shared/Exceptions/UnrecoverableException.cs diff --git a/Bones.Api/Features/Identity/ChangeUserEmail.cs b/Bones.Api/Features/Identity/ChangeUserEmail.cs index fcd71f2..98bffcc 100644 --- a/Bones.Api/Features/Identity/ChangeUserEmail.cs +++ b/Bones.Api/Features/Identity/ChangeUserEmail.cs @@ -41,7 +41,7 @@ public async Task Handle(Command request, CancellationToken can }; } - CommandResponse emailChanged = await sender.Send(new ChangeUserEmailDb.Command(request.UserId, request.Email)); + CommandResponse emailChanged = await sender.Send(new ChangeUserEmailDb.Command(request.UserId, request.Email), cancellationToken); if (!emailChanged.Success) { diff --git a/Bones.Api/Models/ApiQueryResponse.cs b/Bones.Api/Models/ApiQueryResponse.cs index 8e81083..8017644 100644 --- a/Bones.Api/Models/ApiQueryResponse.cs +++ b/Bones.Api/Models/ApiQueryResponse.cs @@ -32,7 +32,7 @@ public sealed record ApiQueryResponse /// The newly translated TResult? public static implicit operator TResult?(ApiQueryResponse response) { - if (response.Success == false || response.Result == null) + if (!response.Success || Equals(response.Result, default(TResult))) { return default; } @@ -47,7 +47,7 @@ public sealed record ApiQueryResponse /// The newly translated QueryResponse<TResult> public static implicit operator ApiQueryResponse(TResult? result) { - if (result == null) + if (Equals(result, default(TResult))) { return new() { diff --git a/Bones.Database/BonesDbContext.cs b/Bones.Database/BonesDbContext.cs index 8fb33cf..da054b0 100644 --- a/Bones.Database/BonesDbContext.cs +++ b/Bones.Database/BonesDbContext.cs @@ -77,7 +77,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) if (string.IsNullOrWhiteSpace(connectionString)) { - throw new UnrecoverableException("Missing appsettings configuration for connection string: BonesDb"); + throw new BonesException("Missing appsettings configuration for connection string: BonesDb"); } optionsBuilder.UseNpgsql(connectionString); @@ -87,18 +87,18 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) /// /// Create models /// - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) + /// + protected override void OnModelCreating(ModelBuilder builder) { - base.OnModelCreating(modelBuilder); - - // Identity - modelBuilder.Entity().ToTable("BonesUsers", "Identity"); - modelBuilder.Entity().ToTable("BonesUserRoles", "Identity"); - modelBuilder.Entity().ToTable("BonesUserLogins", "Identity"); - modelBuilder.Entity().ToTable("BonesUserClaims", "Identity"); - modelBuilder.Entity().ToTable("BonesUserTokens", "Identity"); - modelBuilder.Entity().ToTable("BonesRoles", "Identity"); - modelBuilder.Entity().ToTable("BonesRoleClaims", "Identity"); + base.OnModelCreating(builder); + + const string identitySchema = "Identity"; + builder.Entity().ToTable("BonesUsers", identitySchema); + builder.Entity().ToTable("BonesUserRoles", identitySchema); + builder.Entity().ToTable("BonesUserLogins", identitySchema); + builder.Entity().ToTable("BonesUserClaims", identitySchema); + builder.Entity().ToTable("BonesUserTokens", identitySchema); + builder.Entity().ToTable("BonesRoles", identitySchema); + builder.Entity().ToTable("BonesRoleClaims", identitySchema); } } \ No newline at end of file diff --git a/Bones.Database/DbSets/Identity/BonesRole.cs b/Bones.Database/DbSets/Identity/BonesRole.cs index df45274..e9c8166 100644 --- a/Bones.Database/DbSets/Identity/BonesRole.cs +++ b/Bones.Database/DbSets/Identity/BonesRole.cs @@ -6,6 +6,4 @@ namespace Bones.Database.DbSets.Identity; /// Model for the Identity.BonesRoles table. /// [Table("BonesRoles", Schema = "Identity")] -public class BonesRole : IdentityRole -{ -} \ No newline at end of file +public class BonesRole : IdentityRole; \ No newline at end of file diff --git a/Bones.Database/DbSets/Identity/BonesRoleClaim.cs b/Bones.Database/DbSets/Identity/BonesRoleClaim.cs index 1166c69..6c940a8 100644 --- a/Bones.Database/DbSets/Identity/BonesRoleClaim.cs +++ b/Bones.Database/DbSets/Identity/BonesRoleClaim.cs @@ -6,6 +6,4 @@ namespace Bones.Database.DbSets.Identity; /// Model for the Identity.BonesRoleClaims table. /// [Table("BonesRoleClaims", Schema = "Identity")] -public class BonesRoleClaim : IdentityRoleClaim -{ -} \ No newline at end of file +public class BonesRoleClaim : IdentityRoleClaim; \ No newline at end of file diff --git a/Bones.Database/DbSets/Identity/BonesUserClaim.cs b/Bones.Database/DbSets/Identity/BonesUserClaim.cs index 9b41ddb..cba990f 100644 --- a/Bones.Database/DbSets/Identity/BonesUserClaim.cs +++ b/Bones.Database/DbSets/Identity/BonesUserClaim.cs @@ -6,6 +6,4 @@ namespace Bones.Database.DbSets.Identity; /// Model for the Identity.BonesUserClaims table. /// [Table("BonesUserClaims", Schema = "Identity")] -public class BonesUserClaim : IdentityUserClaim -{ -} \ No newline at end of file +public class BonesUserClaim : IdentityUserClaim; \ No newline at end of file diff --git a/Bones.Database/DbSets/Identity/BonesUserLogin.cs b/Bones.Database/DbSets/Identity/BonesUserLogin.cs index 648a909..83b5845 100644 --- a/Bones.Database/DbSets/Identity/BonesUserLogin.cs +++ b/Bones.Database/DbSets/Identity/BonesUserLogin.cs @@ -6,7 +6,4 @@ namespace Bones.Database.DbSets.Identity; /// Model for the Identity.BonesUserLogins table. /// [Table("BonesUserLogins", Schema = "Identity")] -public class BonesUserLogin : IdentityUserLogin -{ - -} \ No newline at end of file +public class BonesUserLogin : IdentityUserLogin; \ No newline at end of file diff --git a/Bones.Database/DbSets/Identity/BonesUserRole.cs b/Bones.Database/DbSets/Identity/BonesUserRole.cs index a1bc60e..0d9a922 100644 --- a/Bones.Database/DbSets/Identity/BonesUserRole.cs +++ b/Bones.Database/DbSets/Identity/BonesUserRole.cs @@ -6,6 +6,4 @@ namespace Bones.Database.DbSets.Identity; /// Model for the Identity.BonesUserRoles table. /// [Table("BonesUserRoles", Schema = "Identity")] -public class BonesUserRole : IdentityUserRole -{ -} \ No newline at end of file +public class BonesUserRole : IdentityUserRole; \ No newline at end of file diff --git a/Bones.Database/DbSets/Identity/BonesUserToken.cs b/Bones.Database/DbSets/Identity/BonesUserToken.cs index 0d20f5f..45a3d60 100644 --- a/Bones.Database/DbSets/Identity/BonesUserToken.cs +++ b/Bones.Database/DbSets/Identity/BonesUserToken.cs @@ -6,6 +6,4 @@ namespace Bones.Database.DbSets.Identity; /// Model for the Identity.BonesUserTokens table. /// [Table("BonesUserTokens", Schema = "Identity")] -public class BonesUserToken : IdentityUserToken -{ -} \ No newline at end of file +public class BonesUserToken : IdentityUserToken; \ No newline at end of file diff --git a/Bones.Database/DbSets/ProjectManagement/Items/Item.cs b/Bones.Database/DbSets/ProjectManagement/Items/Item.cs index bd5b1d0..2a90976 100644 --- a/Bones.Database/DbSets/ProjectManagement/Items/Item.cs +++ b/Bones.Database/DbSets/ProjectManagement/Items/Item.cs @@ -25,4 +25,9 @@ public class Item public List Tags { get; set; } = []; public List Versions { get; set; } = []; + + /// + /// Disables viewing this item, and when safe to do so it will be removed. + /// + public bool DeleteFlag { get; set; } = false; } \ No newline at end of file diff --git a/Bones.Database/DbSets/ProjectManagement/Items/ItemValue.cs b/Bones.Database/DbSets/ProjectManagement/Items/ItemValue.cs index 142e6f1..3e1ab20 100644 --- a/Bones.Database/DbSets/ProjectManagement/Items/ItemValue.cs +++ b/Bones.Database/DbSets/ProjectManagement/Items/ItemValue.cs @@ -19,6 +19,11 @@ public class ItemValue public string? Value { get; private set; } + /// + /// Disables viewing this item value, and when safe to do so it will be removed. + /// + public bool DeleteFlag { get; set; } = false; + public bool TrySetValue(T value) { string? valueStr = value?.ToString(); diff --git a/Bones.Database/DbSets/ProjectManagement/Items/ItemVersion.cs b/Bones.Database/DbSets/ProjectManagement/Items/ItemVersion.cs index 892f2ac..d4c397a 100644 --- a/Bones.Database/DbSets/ProjectManagement/Items/ItemVersion.cs +++ b/Bones.Database/DbSets/ProjectManagement/Items/ItemVersion.cs @@ -22,4 +22,9 @@ public class ItemVersion public required Item Item { get; set; } public required List Values { get; set; } + + /// + /// Disables viewing this item version, and when safe to do so it will be removed. + /// + public bool DeleteFlag { get; set; } = false; } \ No newline at end of file diff --git a/Bones.Database/DbSets/ProjectManagement/Layouts/Layout.cs b/Bones.Database/DbSets/ProjectManagement/Layouts/Layout.cs index 3150246..3c37b53 100644 --- a/Bones.Database/DbSets/ProjectManagement/Layouts/Layout.cs +++ b/Bones.Database/DbSets/ProjectManagement/Layouts/Layout.cs @@ -14,10 +14,16 @@ public class Layout public Guid Id { get; init; } [MaxLength(512)] - public required string Name { get; init; } + public required string Name { get; set; } /// /// The versions for this layout /// public List Versions { get; set; } = []; + + /// + /// Disables creating of new items using this layout, + /// and when all items using it are deleted it will be removed. + /// + public bool DeleteFlag { get; set; } = false; } \ No newline at end of file diff --git a/Bones.Database/DbSets/ProjectManagement/Layouts/LayoutVersion.cs b/Bones.Database/DbSets/ProjectManagement/Layouts/LayoutVersion.cs index 0ab1811..45d3c20 100644 --- a/Bones.Database/DbSets/ProjectManagement/Layouts/LayoutVersion.cs +++ b/Bones.Database/DbSets/ProjectManagement/Layouts/LayoutVersion.cs @@ -26,4 +26,10 @@ public class LayoutVersion public required long Version { get; init; } public List Fields { get; init; } = []; + + /// + /// Disables creating of new items using this layout version, + /// and when all items using it are deleted it will be removed. + /// + public bool DeleteFlag { get; set; } = false; } \ No newline at end of file diff --git a/Bones.Database/DbSets/ProjectManagement/Projects/Project.cs b/Bones.Database/DbSets/ProjectManagement/Projects/Project.cs index ef7289d..d4c2fac 100644 --- a/Bones.Database/DbSets/ProjectManagement/Projects/Project.cs +++ b/Bones.Database/DbSets/ProjectManagement/Projects/Project.cs @@ -19,4 +19,10 @@ public class Project public required string Name { get; set; } public List Initiatives { get; set; } = []; + + /// + /// Disables access to this Project and schedules deletes for everything within, + /// when all items using it are deleted it will be removed. + /// + public bool DeleteFlag { get; set; } = false; } \ No newline at end of file diff --git a/Bones.Database/DbSets/ProjectManagement/Queues/Queue.cs b/Bones.Database/DbSets/ProjectManagement/Queues/Queue.cs index 5e4bc98..356e075 100644 --- a/Bones.Database/DbSets/ProjectManagement/Queues/Queue.cs +++ b/Bones.Database/DbSets/ProjectManagement/Queues/Queue.cs @@ -22,4 +22,9 @@ public class Queue public required string Name { get; set; } public List Items { get; set; } = []; + + /// + /// Disables viewing this queue, and when safe to do so it will be removed. + /// + public bool DeleteFlag { get; set; } = false; } \ No newline at end of file diff --git a/Bones.Database/Operations/Identity/ChangeUserEmailDb.cs b/Bones.Database/Operations/Identity/ChangeUserEmailDb.cs index a44dc08..d55f655 100644 --- a/Bones.Database/Operations/Identity/ChangeUserEmailDb.cs +++ b/Bones.Database/Operations/Identity/ChangeUserEmailDb.cs @@ -2,7 +2,7 @@ namespace Bones.Database.Operations.Identity; -public class ChangeUserEmailDb(BonesDbContext dbContext, ISender sender) : IRequestHandler +public sealed class ChangeUserEmailDb(BonesDbContext dbContext, ISender sender) : IRequestHandler { /// /// DB Command for updating the email address on a user. diff --git a/Bones.Database/Operations/Identity/ClearEmailVerificationsForUserDb.cs b/Bones.Database/Operations/Identity/ClearEmailVerificationsForUserDb.cs index 9577c99..41791d2 100644 --- a/Bones.Database/Operations/Identity/ClearEmailVerificationsForUserDb.cs +++ b/Bones.Database/Operations/Identity/ClearEmailVerificationsForUserDb.cs @@ -2,7 +2,7 @@ namespace Bones.Database.Operations.Identity; -public class ClearEmailVerificationsForUserDb(BonesDbContext dbContext) : IRequestHandler +public sealed class ClearEmailVerificationsForUserDb(BonesDbContext dbContext) : IRequestHandler { /// /// diff --git a/Bones.Database/Operations/Identity/ClearExpiredEmailVerificationsDb.cs b/Bones.Database/Operations/Identity/ClearExpiredEmailVerificationsDb.cs index 253e6bc..cf9acea 100644 --- a/Bones.Database/Operations/Identity/ClearExpiredEmailVerificationsDb.cs +++ b/Bones.Database/Operations/Identity/ClearExpiredEmailVerificationsDb.cs @@ -2,7 +2,7 @@ namespace Bones.Database.Operations.Identity; -public class ClearExpiredEmailVerificationsDb(BonesDbContext dbContext) : IRequestHandler> +public sealed class ClearExpiredEmailVerificationsDb(BonesDbContext dbContext) : IRequestHandler> { /// /// DB Query for clearing the expired email verifications, returns the number removed. diff --git a/Bones.Database/Operations/Identity/CreateEmailVerificationDb.cs b/Bones.Database/Operations/Identity/CreateEmailVerificationDb.cs index ebdb9c6..f85df11 100644 --- a/Bones.Database/Operations/Identity/CreateEmailVerificationDb.cs +++ b/Bones.Database/Operations/Identity/CreateEmailVerificationDb.cs @@ -4,7 +4,7 @@ namespace Bones.Database.Operations.Identity; -public class CreateEmailVerificationDb(BonesDbContext dbContext) : IRequestHandler +public sealed class CreateEmailVerificationDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for generating an email verification token and queuing an email to be sent. @@ -26,11 +26,15 @@ public bool IsRequestValid() public async Task Handle(Command request, CancellationToken cancellationToken) { - BonesUser? user = dbContext.Users.FirstOrDefault(u => u.Id == request.UserId); + BonesUser? user = await dbContext.Users.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); if (user == null) { - throw new UnrecoverableException($"Unable to find user by ID: {request.UserId}"); + return new() + { + Success = false, + FailureReason = $"Unable to find user by ID: {request.UserId}" + }; } EntityEntry created = await dbContext.UserEmailVerifications.AddAsync(new() diff --git a/Bones.Database/Operations/Identity/CreateUserDb.cs b/Bones.Database/Operations/Identity/CreateUserDb.cs index 646f4cf..6c5047a 100644 --- a/Bones.Database/Operations/Identity/CreateUserDb.cs +++ b/Bones.Database/Operations/Identity/CreateUserDb.cs @@ -3,7 +3,7 @@ namespace Bones.Database.Operations.Identity; -public class CreateUserDb(BonesDbContext dbContext, ISender sender) : IRequestHandler +public sealed class CreateUserDb(BonesDbContext dbContext, ISender sender) : IRequestHandler { /// /// DB Command for creating a user. diff --git a/Bones.Database/Operations/Identity/EmailAvailableForUseDb.cs b/Bones.Database/Operations/Identity/EmailAvailableForUseDb.cs index d9510b4..b567ec1 100644 --- a/Bones.Database/Operations/Identity/EmailAvailableForUseDb.cs +++ b/Bones.Database/Operations/Identity/EmailAvailableForUseDb.cs @@ -2,7 +2,7 @@ namespace Bones.Database.Operations.Identity; -public class EmailAvailableForUseDb(BonesDbContext dbContext) : IRequestHandler +public sealed class EmailAvailableForUseDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for checking if an email is available to use. diff --git a/Bones.Database/Operations/Identity/GetUserByIdDb.cs b/Bones.Database/Operations/Identity/GetUserByIdDb.cs index e636041..8c60f0a 100644 --- a/Bones.Database/Operations/Identity/GetUserByIdDb.cs +++ b/Bones.Database/Operations/Identity/GetUserByIdDb.cs @@ -2,7 +2,7 @@ namespace Bones.Database.Operations.Identity; -public class GetUserByIdDb(BonesDbContext dbContext) : IRequestHandler> +public sealed class GetUserByIdDb(BonesDbContext dbContext) : IRequestHandler> { /// /// DB Query to get a user by UserId diff --git a/Bones.Database/Operations/Identity/VerifyUserEmailDb.cs b/Bones.Database/Operations/Identity/VerifyUserEmailDb.cs index 61d6459..04bb1b8 100644 --- a/Bones.Database/Operations/Identity/VerifyUserEmailDb.cs +++ b/Bones.Database/Operations/Identity/VerifyUserEmailDb.cs @@ -2,7 +2,7 @@ namespace Bones.Database.Operations.Identity; -public class VerifyUserEmailDb(BonesDbContext dbContext) : IRequestHandler +public sealed class VerifyUserEmailDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for verifying an email address for a user. @@ -35,20 +35,18 @@ public async Task Handle(Command request, CancellationToken can && verification.Token == request.Token && verification.ValidUntilDateTime > DateTimeOffset.UtcNow); - if (!validMatches.Any()) + if (!await validMatches.AnyAsync(cancellationToken)) { return new() { Success = false, - FailureReason = "Supplied information is invalid or the verification time has expired." + FailureReason = "Supplied information is invalid or the token has expired." }; } await validMatches.ExecuteDeleteAsync(cancellationToken); - BonesUser? acct = await dbContext.Users.FirstOrDefaultAsync(user => user.Id == request.UserId, - cancellationToken); - + BonesUser? acct = await dbContext.Users.FirstOrDefaultAsync(user => user.Id == request.UserId, cancellationToken); if (acct == null) { return new() diff --git a/Bones.Database/Operations/ProjectManagement/Initiatives/CreateInitiativeDb.cs b/Bones.Database/Operations/ProjectManagement/Initiatives/CreateInitiativeDb.cs index baa1a64..9fdba66 100644 --- a/Bones.Database/Operations/ProjectManagement/Initiatives/CreateInitiativeDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Initiatives/CreateInitiativeDb.cs @@ -4,7 +4,7 @@ namespace Bones.Database.Operations.ProjectManagement.Initiatives; -public class CreateInitiativeDb(BonesDbContext dbContext) : IRequestHandler +public sealed class CreateInitiativeDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for creating an Initiative. @@ -30,8 +30,6 @@ public bool IsRequestValid() } } - - /// public async Task Handle(Command request, CancellationToken cancellationToken) { diff --git a/Bones.Database/Operations/ProjectManagement/Initiatives/DeleteInitiativeDb.cs b/Bones.Database/Operations/ProjectManagement/Initiatives/DeleteInitiativeDb.cs index 6fac6b9..71e9f8c 100644 --- a/Bones.Database/Operations/ProjectManagement/Initiatives/DeleteInitiativeDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Initiatives/DeleteInitiativeDb.cs @@ -1,6 +1,47 @@ +using Bones.Database.DbSets.ProjectManagement.Initiatives; + namespace Bones.Database.Operations.ProjectManagement.Initiatives; -public class DeleteInitiativeDb +public sealed class DeleteInitiativeDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for deleting an Initiative. + /// + /// Internal ID of the initiative + public record Command(Guid InitiativeId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (InitiativeId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + // TODO: fix this + IQueryable initiative = dbContext.Initiatives.Where(i => i.Id == request.InitiativeId); + if (await initiative.AnyAsync(cancellationToken)) + { + return new() + { + Success = false, + FailureReason = "Invalid InitiativeId." + }; + } + + await initiative.ExecuteDeleteAsync(cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Initiatives/UpdateInitiativeDb.cs b/Bones.Database/Operations/ProjectManagement/Initiatives/UpdateInitiativeDb.cs index 189cfcb..03b7eac 100644 --- a/Bones.Database/Operations/ProjectManagement/Initiatives/UpdateInitiativeDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Initiatives/UpdateInitiativeDb.cs @@ -1,6 +1,53 @@ +using Bones.Database.DbSets.ProjectManagement.Initiatives; + namespace Bones.Database.Operations.ProjectManagement.Initiatives; -public class UpdateInitiativeDb +public sealed class UpdateInitiativeDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for updating an Initiative. + /// + /// Internal ID of the initiative + /// The new name of the initiative + public record Command(Guid InitiativeId, string Name) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (string.IsNullOrWhiteSpace(Name)) + { + return false; + } + + if (InitiativeId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Initiative? initiative = await dbContext.Initiatives.FirstOrDefaultAsync(i => i.Id == request.InitiativeId, cancellationToken); + if (initiative == null) + { + return new() + { + Success = false, + FailureReason = "Invalid InitiativeId." + }; + } + + initiative.Name = request.Name; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Items/CreateItemDb.cs b/Bones.Database/Operations/ProjectManagement/Items/CreateItemDb.cs index 2365621..1322fec 100644 --- a/Bones.Database/Operations/ProjectManagement/Items/CreateItemDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Items/CreateItemDb.cs @@ -5,7 +5,7 @@ namespace Bones.Database.Operations.ProjectManagement.Items; -public class CreateItemDb(BonesDbContext dbContext) : IRequestHandler +public sealed class CreateItemDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for creating an Item. @@ -42,7 +42,7 @@ public bool IsRequestValid() /// public async Task Handle(Command request, CancellationToken cancellationToken) { - Queue? queue = await dbContext.Queues.FirstOrDefaultAsync(i => i.Id == request.QueueId, cancellationToken); + Queue? queue = await dbContext.Queues.FirstOrDefaultAsync(q => q.Id == request.QueueId, cancellationToken); if (queue == null) { return new() @@ -52,7 +52,7 @@ public async Task Handle(Command request, CancellationToken can }; } - LayoutVersion? layoutVersion = await dbContext.LayoutVersions.Include(layoutVersion => layoutVersion.Fields).FirstOrDefaultAsync(i => i.Id == request.LayoutVersionId, cancellationToken); + LayoutVersion? layoutVersion = await dbContext.LayoutVersions.Include(layoutVersion => layoutVersion.Fields).FirstOrDefaultAsync(lv => lv.Id == request.LayoutVersionId, cancellationToken); if (layoutVersion == null) { return new() @@ -75,7 +75,7 @@ public async Task Handle(Command request, CancellationToken can foreach ((string? key, object? value) in request.Values) { - ItemField? field = layoutVersion.Fields.FirstOrDefault(f => f.Name == key); + ItemField? field = layoutVersion.Fields.Find(f => f.Name == key); if (field == null) { return new() @@ -102,7 +102,7 @@ public async Task Handle(Command request, CancellationToken can ItemVersion initialVersion = new() { - Version = 1, + Version = 0, Item = new() { Name = request.Name, diff --git a/Bones.Database/Operations/ProjectManagement/Items/CreateItemVersionDb.cs b/Bones.Database/Operations/ProjectManagement/Items/CreateItemVersionDb.cs new file mode 100644 index 0000000..301d175 --- /dev/null +++ b/Bones.Database/Operations/ProjectManagement/Items/CreateItemVersionDb.cs @@ -0,0 +1,115 @@ +using Bones.Database.DbSets.ProjectManagement.Items; +using Bones.Database.DbSets.ProjectManagement.Layouts; +using Bones.Database.DbSets.ProjectManagement.Queues; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace Bones.Database.Operations.ProjectManagement.Items; + +public sealed class CreateItemVersionDb(BonesDbContext dbContext) : IRequestHandler +{ + /// + /// DB Command for creating an ItemVersion. + /// + /// Internal ID of the item + /// Internal ID of the layout version this item is using + /// Internal ID of the queue + public record Command(Guid ItemID, Guid LayoutVersionId, Dictionary Values) + : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (ItemID == Guid.Empty) + { + return false; + } + + if (LayoutVersionId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Item? item = await dbContext.Items.Include(item => item.Versions).FirstOrDefaultAsync(i => i.Id == request.ItemID, cancellationToken); + if (item == null) + { + return new() + { + Success = false, + FailureReason = "Invalid ItemID." + }; + } + + LayoutVersion? layoutVersion = await dbContext.LayoutVersions.Include(layoutVersion => layoutVersion.Fields).FirstOrDefaultAsync(lv => lv.Id == request.LayoutVersionId, cancellationToken); + if (layoutVersion == null) + { + return new() + { + Success = false, + FailureReason = "Invalid LayoutVersionId." + }; + } + + if (request.Values.Count > layoutVersion.Fields.Count) + { + return new() + { + Success = false, + FailureReason = "Invalid values provided." + }; + } + + List values = []; + + foreach ((string? key, object? value) in request.Values) + { + ItemField? field = layoutVersion.Fields.Find(f => f.Name == key); + if (field == null) + { + return new() + { + Success = false, + FailureReason = $"Invalid field name provided: {key}" + }; + } + + ItemValue itemValue = new() { Field = field }; + bool valid = itemValue.TrySetValue(value); + if (!valid) + { + return new() + { + Success = false, + FailureReason = $"Invalid value provided for '{Enum.GetName(field.Type)}' field '{key}': {value}" + }; + } + + values.Add(itemValue); + } + + + ItemVersion newVersion = new() + { + Version = item.Versions.Count, + Item = item, + Layout = layoutVersion, + Values = values + }; + + EntityEntry created = await dbContext.ItemVersions.AddAsync(newVersion, cancellationToken); + + await dbContext.SaveChangesAsync(cancellationToken); + + return new() + { + Success = true, + Id = created.Entity.Id + }; + } +} \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Items/CreateTagDb.cs b/Bones.Database/Operations/ProjectManagement/Items/CreateTagDb.cs index af8d4c7..ca0be6a 100644 --- a/Bones.Database/Operations/ProjectManagement/Items/CreateTagDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Items/CreateTagDb.cs @@ -3,7 +3,7 @@ namespace Bones.Database.Operations.ProjectManagement.Items; -public class CreateTagDb(BonesDbContext dbContext) : IRequestHandler +public sealed class CreateTagDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for creating a Project. diff --git a/Bones.Database/Operations/ProjectManagement/Items/DeleteItemDb.cs b/Bones.Database/Operations/ProjectManagement/Items/DeleteItemDb.cs index e15b870..f6e14e9 100644 --- a/Bones.Database/Operations/ProjectManagement/Items/DeleteItemDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Items/DeleteItemDb.cs @@ -1,6 +1,56 @@ +using Bones.Database.DbSets.ProjectManagement.Items; + namespace Bones.Database.Operations.ProjectManagement.Items; -public class DeleteItemDb +public sealed class DeleteItemDb(BonesDbContext dbContext, ISender sender) : IRequestHandler { + /// + /// DB Command for deleting an Item. + /// + /// Internal ID of the item + public record Command(Guid ItemId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (ItemId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Item? item = await dbContext.Items + .Include(item => item.Versions) + .ThenInclude(itemVersion => itemVersion.Values) + .FirstOrDefaultAsync(p => p.Id == request.ItemId, cancellationToken); + + if (item == null) + { + return new() + { + Success = false, + FailureReason = "Invalid ItemId." + }; + } + + foreach (ItemVersion version in item.Versions) + { + await sender.Send(new DeleteItemVersionDb.Command(version.Id), cancellationToken); + } + + item.DeleteFlag = true; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Items/DeleteItemVersionDb.cs b/Bones.Database/Operations/ProjectManagement/Items/DeleteItemVersionDb.cs new file mode 100644 index 0000000..f839164 --- /dev/null +++ b/Bones.Database/Operations/ProjectManagement/Items/DeleteItemVersionDb.cs @@ -0,0 +1,55 @@ +using Bones.Database.DbSets.ProjectManagement.Items; + +namespace Bones.Database.Operations.ProjectManagement.Items; + +public sealed class DeleteItemVersionDb(BonesDbContext dbContext) : IRequestHandler +{ + /// + /// DB Command for deleting an ItemVersion. + /// + /// Internal ID of the item version + public record Command(Guid ItemVersionId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (ItemVersionId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + ItemVersion? itemVersion = await dbContext.ItemVersions + .Include(itemVersion => itemVersion.Values) + .FirstOrDefaultAsync(p => p.Id == request.ItemVersionId, cancellationToken); + + if (itemVersion == null) + { + return new() + { + Success = false, + FailureReason = "Invalid ItemVersionId." + }; + } + + foreach (ItemValue value in itemVersion.Values) + { + value.DeleteFlag = true; + } + + itemVersion.DeleteFlag = true; + + await dbContext.SaveChangesAsync(cancellationToken); + + return new() + { + Success = true + }; + } +} \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Items/DeleteTagDb.cs b/Bones.Database/Operations/ProjectManagement/Items/DeleteTagDb.cs index b246dee..02fc32c 100644 --- a/Bones.Database/Operations/ProjectManagement/Items/DeleteTagDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Items/DeleteTagDb.cs @@ -1,6 +1,46 @@ +using Bones.Database.DbSets.ProjectManagement.Items; + namespace Bones.Database.Operations.ProjectManagement.Items; -public class DeleteTagDb +public sealed class DeleteTagDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for deleting a Tag. + /// + /// Internal ID of the tag + public record Command(Guid TagId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (TagId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + IQueryable tag = dbContext.Tags.Where(p => p.Id == request.TagId); + if (await tag.AnyAsync(cancellationToken)) + { + return new() + { + Success = false, + FailureReason = "Invalid TagId." + }; + } + + await tag.ExecuteDeleteAsync(cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Items/UpdateItemDb.cs b/Bones.Database/Operations/ProjectManagement/Items/UpdateItemDb.cs index 9ab8c58..d2fd251 100644 --- a/Bones.Database/Operations/ProjectManagement/Items/UpdateItemDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Items/UpdateItemDb.cs @@ -1,6 +1,92 @@ +using Bones.Database.DbSets.ProjectManagement.Items; +using Bones.Database.DbSets.ProjectManagement.Queues; + namespace Bones.Database.Operations.ProjectManagement.Items; -public class UpdateItemDb +public sealed class UpdateItemDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for updating an Item. + /// + /// Internal ID of the item + /// The new name of the item + public record Command(Guid ItemId, string Name, Guid QueueId, List TagIds) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (string.IsNullOrWhiteSpace(Name)) + { + return false; + } + + if (ItemId == Guid.Empty) + { + return false; + } + + if (QueueId == Guid.Empty) + { + return false; + } + + if (TagIds.Exists(tagId => tagId == Guid.Empty)) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Item? item = await dbContext.Items.FirstOrDefaultAsync(p => p.Id == request.ItemId, cancellationToken); + if (item == null) + { + return new() + { + Success = false, + FailureReason = "Invalid ItemId." + }; + } + + Queue? queue = await dbContext.Queues.FirstOrDefaultAsync(q => q.Id == request.QueueId, cancellationToken); + if (queue == null) + { + return new() + { + Success = false, + FailureReason = "Invalid QueueId." + }; + } + + List tags = []; + foreach (Guid tagId in request.TagIds) + { + Tag? tag = await dbContext.Tags.FirstOrDefaultAsync(t => t.Id == tagId, cancellationToken); + if (tag == null) + { + return new() + { + Success = false, + FailureReason = $"Invalid TagId: {tagId}" + }; + } + tags.Add(tag); + } + + item.Name = request.Name; + item.Queue = queue; + item.AddedToQueueDateTime = DateTimeOffset.Now; + item.Tags = tags; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Items/UpdateTagDb.cs b/Bones.Database/Operations/ProjectManagement/Items/UpdateTagDb.cs index e5fbdb8..be83895 100644 --- a/Bones.Database/Operations/ProjectManagement/Items/UpdateTagDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Items/UpdateTagDb.cs @@ -1,6 +1,53 @@ +using Bones.Database.DbSets.ProjectManagement.Items; + namespace Bones.Database.Operations.ProjectManagement.Items; -public class UpdateTagDb +public sealed class UpdateTagDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for updating a Tag. + /// + /// Internal ID of the tag + /// The new name of the tag + public record Command(Guid TagId, string Name) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (string.IsNullOrWhiteSpace(Name)) + { + return false; + } + + if (TagId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Tag? tag = await dbContext.Tags.FirstOrDefaultAsync(t => t.Id == request.TagId, cancellationToken); + if (tag == null) + { + return new() + { + Success = false, + FailureReason = "Invalid TagId." + }; + } + + tag.Name = request.Name; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Layouts/CreateLayoutDb.cs b/Bones.Database/Operations/ProjectManagement/Layouts/CreateLayoutDb.cs index 1e5e152..445b2c5 100644 --- a/Bones.Database/Operations/ProjectManagement/Layouts/CreateLayoutDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Layouts/CreateLayoutDb.cs @@ -4,7 +4,8 @@ namespace Bones.Database.Operations.ProjectManagement.Layouts; -public class CreateLayoutDb(BonesDbContext dbContext) : IRequestHandler +// TODO: LayoutVersions +public sealed class CreateLayoutDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for creating a layout. diff --git a/Bones.Database/Operations/ProjectManagement/Layouts/DeleteLayoutDb.cs b/Bones.Database/Operations/ProjectManagement/Layouts/DeleteLayoutDb.cs index ee62693..f389528 100644 --- a/Bones.Database/Operations/ProjectManagement/Layouts/DeleteLayoutDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Layouts/DeleteLayoutDb.cs @@ -1,6 +1,53 @@ +using Bones.Database.DbSets.ProjectManagement.Layouts; + namespace Bones.Database.Operations.ProjectManagement.Layouts; -public class DeleteLayoutDb +public sealed class DeleteLayoutDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for deleting a Layout. + /// + /// Internal ID of the layout + public record Command(Guid LayoutId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (LayoutId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Layout? layout = await dbContext.Layouts.Include(layout => layout.Versions).FirstOrDefaultAsync(p => p.Id == request.LayoutId, cancellationToken); + if (layout == null) + { + return new() + { + Success = false, + FailureReason = "Invalid InitiativeId." + }; + } + + foreach (LayoutVersion version in layout.Versions) + { + // TODO: send it + version.DeleteFlag = true; + } + + layout.DeleteFlag = true; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Layouts/UpdateLayoutDb.cs b/Bones.Database/Operations/ProjectManagement/Layouts/UpdateLayoutDb.cs index 3ae7287..d11cebe 100644 --- a/Bones.Database/Operations/ProjectManagement/Layouts/UpdateLayoutDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Layouts/UpdateLayoutDb.cs @@ -1,6 +1,53 @@ +using Bones.Database.DbSets.ProjectManagement.Layouts; + namespace Bones.Database.Operations.ProjectManagement.Layouts; -public class UpdateLayoutDb +public sealed class UpdateLayoutDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for updating a Layout. + /// + /// Internal ID of the layout + /// The new name of the layout + public record Command(Guid LayoutId, string Name) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (string.IsNullOrWhiteSpace(Name)) + { + return false; + } + + if (LayoutId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Layout? layout = await dbContext.Layouts.FirstOrDefaultAsync(p => p.Id == request.LayoutId, cancellationToken); + if (layout == null) + { + return new() + { + Success = false, + FailureReason = "Invalid LayoutId." + }; + } + + layout.Name = request.Name; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Projects/CreateProjectDb.cs b/Bones.Database/Operations/ProjectManagement/Projects/CreateProjectDb.cs index 756872d..89d3bdf 100644 --- a/Bones.Database/Operations/ProjectManagement/Projects/CreateProjectDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Projects/CreateProjectDb.cs @@ -3,7 +3,7 @@ namespace Bones.Database.Operations.ProjectManagement.Projects; -public class CreateProjectDb(BonesDbContext dbContext) : IRequestHandler +public sealed class CreateProjectDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for creating a Project. diff --git a/Bones.Database/Operations/ProjectManagement/Projects/DeleteProjectDb.cs b/Bones.Database/Operations/ProjectManagement/Projects/DeleteProjectDb.cs index b692b5f..bc089c7 100644 --- a/Bones.Database/Operations/ProjectManagement/Projects/DeleteProjectDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Projects/DeleteProjectDb.cs @@ -1,6 +1,54 @@ +using Bones.Database.DbSets.ProjectManagement.Initiatives; +using Bones.Database.DbSets.ProjectManagement.Projects; +using Bones.Database.Operations.ProjectManagement.Initiatives; + namespace Bones.Database.Operations.ProjectManagement.Projects; -public class DeleteProjectDb +public sealed class DeleteProjectDb(BonesDbContext dbContext, ISender sender) : IRequestHandler { + /// + /// DB Command for deleting a Project. + /// + /// Internal ID of the project + public record Command(Guid ProjectId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (ProjectId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Project? project = await dbContext.Projects.Include(project => project.Initiatives).FirstOrDefaultAsync(p => p.Id == request.ProjectId, cancellationToken); + if (project == null) + { + return new() + { + Success = false, + FailureReason = "Invalid ProjectId." + }; + } + + foreach (Initiative initiative in project.Initiatives) + { + await sender.Send(new DeleteInitiativeDb.Command(initiative.Id), cancellationToken); + } + + project.DeleteFlag = true; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Projects/UpdateProjectDb.cs b/Bones.Database/Operations/ProjectManagement/Projects/UpdateProjectDb.cs index dbc2ecd..40bbb43 100644 --- a/Bones.Database/Operations/ProjectManagement/Projects/UpdateProjectDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Projects/UpdateProjectDb.cs @@ -1,6 +1,52 @@ +using Bones.Database.DbSets.ProjectManagement.Projects; + namespace Bones.Database.Operations.ProjectManagement.Projects; -public class UpdateProjectDb +public sealed class UpdateProjectDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for updating a Project. + /// + /// Internal ID of the project + /// The new name of the project + public record Command(Guid ProjectId, string Name) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (string.IsNullOrWhiteSpace(Name)) + { + return false; + } + + if (ProjectId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Project? project = await dbContext.Projects.FirstOrDefaultAsync(p => p.Id == request.ProjectId, cancellationToken); + if (project == null) + { + return new() + { + Success = false, + FailureReason = "Invalid ProjectId." + }; + } + + project.Name = request.Name; + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Queues/CreateQueueDb.cs b/Bones.Database/Operations/ProjectManagement/Queues/CreateQueueDb.cs index 5ff6eed..a743375 100644 --- a/Bones.Database/Operations/ProjectManagement/Queues/CreateQueueDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Queues/CreateQueueDb.cs @@ -4,7 +4,7 @@ namespace Bones.Database.Operations.ProjectManagement.Queues; -public class CreateQueueDb(BonesDbContext dbContext) : IRequestHandler +public sealed class CreateQueueDb(BonesDbContext dbContext) : IRequestHandler { /// /// DB Command for creating a Queue. diff --git a/Bones.Database/Operations/ProjectManagement/Queues/DeleteQueueDb.cs b/Bones.Database/Operations/ProjectManagement/Queues/DeleteQueueDb.cs index df4b2df..864e083 100644 --- a/Bones.Database/Operations/ProjectManagement/Queues/DeleteQueueDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Queues/DeleteQueueDb.cs @@ -1,6 +1,57 @@ +using Bones.Database.DbSets.ProjectManagement.Items; +using Bones.Database.DbSets.ProjectManagement.Queues; +using Bones.Database.Operations.ProjectManagement.Items; + namespace Bones.Database.Operations.ProjectManagement.Queues; -public class DeleteQueueDb +public sealed class DeleteQueueDb(BonesDbContext dbContext, ISender sender) : IRequestHandler { + /// + /// DB Command for deleting a Queue. + /// + /// Internal ID of the queue + public record Command(Guid QueueId) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (QueueId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Queue? queue = await dbContext.Queues + .Include(queue => queue.Items) + .FirstOrDefaultAsync(p => p.Id == request.QueueId, cancellationToken); + + if (queue == null) + { + return new() + { + Success = false, + FailureReason = "Invalid QueueId." + }; + } + + foreach (Item item in queue.Items) + { + // TODO: Might want to eventually add the ability to move these to a different queue instead + await sender.Send(new DeleteItemDb.Command(item.Id), cancellationToken); + } + + queue.DeleteFlag = true; + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Database/Operations/ProjectManagement/Queues/UpdateQueueDb.cs b/Bones.Database/Operations/ProjectManagement/Queues/UpdateQueueDb.cs index b09f770..a1296f6 100644 --- a/Bones.Database/Operations/ProjectManagement/Queues/UpdateQueueDb.cs +++ b/Bones.Database/Operations/ProjectManagement/Queues/UpdateQueueDb.cs @@ -1,6 +1,53 @@ +using Bones.Database.DbSets.ProjectManagement.Queues; + namespace Bones.Database.Operations.ProjectManagement.Queues; -public class UpdateQueueDb +public sealed class UpdateQueueDb(BonesDbContext dbContext) : IRequestHandler { + /// + /// DB Command for updating a Queue. + /// + /// Internal ID of the queue + /// The new name of the queue + public record Command(Guid QueueId, string Name) : IValidatableRequest + { + /// + public bool IsRequestValid() + { + if (string.IsNullOrWhiteSpace(Name)) + { + return false; + } + + if (QueueId == Guid.Empty) + { + return false; + } + + return true; + } + } + + /// + public async Task Handle(Command request, CancellationToken cancellationToken) + { + Queue? queue = await dbContext.Queues.FirstOrDefaultAsync(p => p.Id == request.QueueId, cancellationToken); + if (queue == null) + { + return new() + { + Success = false, + FailureReason = "Invalid QueueId." + }; + } + + queue.Name = request.Name; + + await dbContext.SaveChangesAsync(cancellationToken); + return new() + { + Success = true + }; + } } \ No newline at end of file diff --git a/Bones.Shared/Bones.Shared.csproj b/Bones.Shared/Bones.Shared.csproj index 6840c7a..5e55830 100644 --- a/Bones.Shared/Bones.Shared.csproj +++ b/Bones.Shared/Bones.Shared.csproj @@ -15,7 +15,7 @@ - + diff --git a/Bones.Shared/Exceptions/BonesException.cs b/Bones.Shared/Exceptions/BonesException.cs new file mode 100644 index 0000000..b1384ad --- /dev/null +++ b/Bones.Shared/Exceptions/BonesException.cs @@ -0,0 +1,7 @@ +namespace Bones.Shared.Exceptions; + +/// +/// Exceptions from the Bones application. +/// +/// What went wrong. +public class BonesException(string message) : ApplicationException(message); \ No newline at end of file diff --git a/Bones.Shared/Exceptions/BonesExceptionBase.cs b/Bones.Shared/Exceptions/BonesExceptionBase.cs deleted file mode 100644 index 18006a9..0000000 --- a/Bones.Shared/Exceptions/BonesExceptionBase.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Bones.Shared.Exceptions; - -/// -/// Exceptions from the Bones application. -/// -/// What went wrong. -public abstract class BonesExceptionBase(string reason) : ApplicationException(reason); \ No newline at end of file diff --git a/Bones.Shared/Exceptions/RecoverableException.cs b/Bones.Shared/Exceptions/RecoverableException.cs deleted file mode 100644 index e7e8c9f..0000000 --- a/Bones.Shared/Exceptions/RecoverableException.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Bones.Shared.Exceptions; - -/// -/// Some shit went sideways, but we might still be able to flush. -/// -/// -public class RecoverableException(string message) : BonesExceptionBase(message); diff --git a/Bones.Shared/Exceptions/UnrecoverableException.cs b/Bones.Shared/Exceptions/UnrecoverableException.cs deleted file mode 100644 index 97605d8..0000000 --- a/Bones.Shared/Exceptions/UnrecoverableException.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Bones.Shared.Exceptions; - -/// -/// An exception that cannot be recovered from, just stop whatever you're doing and have good logs about what happened. -/// -/// -public class UnrecoverableException(string message) : BonesExceptionBase(message); \ No newline at end of file diff --git a/Bones.Shared/Models/CommandResponse.cs b/Bones.Shared/Models/CommandResponse.cs index 9ef0451..e408226 100644 --- a/Bones.Shared/Models/CommandResponse.cs +++ b/Bones.Shared/Models/CommandResponse.cs @@ -12,15 +12,15 @@ public sealed record CommandResponse /// /// Was the command successful? /// - public required bool Success { get; init; } = false; + public required bool Success { get; init; } /// /// If an ID was generated for something by the command, it can optionally be returned here. /// - public Guid? Id { get; init; } = null; + public Guid? Id { get; init; } /// /// If the command failed, why? /// - public string? FailureReason { get; init; } = null; + public string? FailureReason { get; init; } } \ No newline at end of file diff --git a/Bones.sln.DotSettings b/Bones.sln.DotSettings index b2ca88c..3396800 100644 --- a/Bones.sln.DotSettings +++ b/Bones.sln.DotSettings @@ -1,6 +1,8 @@  Required True + <Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> True True True