1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 05:00:19 -05:00

Example of how a partial success/failure command result would look.

This commit is contained in:
jrmccannon 2025-03-10 10:56:30 -05:00
parent 6e7c5b172c
commit a9054b26f9
No known key found for this signature in database
GPG Key ID: CF03F3DB01CE96A6
3 changed files with 83 additions and 9 deletions

View File

@ -12,7 +12,7 @@ public static class CommandResultExtensions
NoRecordFoundFailure<T> failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status404NotFound },
BadRequestFailure<T> failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status400BadRequest },
Failure<T> failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status400BadRequest },
Success<T> success => new ObjectResult(success.Data) { StatusCode = StatusCodes.Status200OK },
Success<T> success => new ObjectResult(success.Value) { StatusCode = StatusCodes.Status200OK },
_ => throw new InvalidOperationException($"Unhandled commandResult type: {commandResult.GetType().Name}")
};
}

View File

@ -9,7 +9,6 @@ public class CommandResult(IEnumerable<string> errors)
public bool Success => ErrorMessages.Count == 0;
public bool HasErrors => ErrorMessages.Count > 0;
public List<string> ErrorMessages { get; } = errors.ToList();
public CommandResult() : this(Array.Empty<string>()) { }
}
@ -29,22 +28,51 @@ public class Success : CommandResult
{
}
public abstract class CommandResult<T>
{
public abstract class CommandResult<T>;
public class Success<T>(T value) : CommandResult<T>
{
public T Value { get; } = value;
}
public class Success<T>(T data) : CommandResult<T>
public class Failure<T>(IEnumerable<string> errorMessages) : CommandResult<T>
{
public T? Data { get; init; } = data;
public List<string> ErrorMessages { get; } = errorMessages.ToList();
public string ErrorMessage => string.Join(" ", ErrorMessages);
public Failure(string error) : this([error]) { }
}
public class Failure<T>(IEnumerable<string> errorMessage) : CommandResult<T>
public class Partial<T> : CommandResult<T>
{
public IEnumerable<string> ErrorMessages { get; init; } = errorMessage;
public T[] Successes { get; set; } = [];
public Error<T>[] Failures { get; set; } = [];
public Failure(string errorMessage) : this(new[] { errorMessage })
public Partial(IEnumerable<T> successfulItems, IEnumerable<Error<T>> failedItems)
{
}
}
public record Error<T>(string Message, T ErroredValue);
public record RecordNotFoundError<T>(string Message, T ErroredValue) : Error<T>(Message, ErroredValue)
{
public const string Code = "Record Not Found";
public RecordNotFoundError(T ErroredValue) : this(Code, ErroredValue)
{
}
}
public record InsufficientPermissionsError<T>(string Message, T ErroredValue) : Error<T>(Message, ErroredValue)
{
public const string Code = "Insufficient Permissions";
public InsufficientPermissionsError(T ErroredValue) : this(Code, ErroredValue)
{
}
}

View File

@ -0,0 +1,46 @@
using Bit.Core.Models.Commands;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Models.Commands;
public class CommandResultTests
{
public class TestItem
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public CommandResult<TestItem> BulkAction(IEnumerable<TestItem> items)
{
var itemList = items.ToList();
var successfulItems = items.Where(x => x.Value == "SuccessfulRequest").ToArray();
var failedItems = itemList.Except(successfulItems).ToArray();
var notFound = failedItems.First(x => x.Value == "Failed due to not found");
var invalidPermissions = failedItems.First(x => x.Value == "Failed due to invalid permissions");
var notFoundError = new RecordNotFoundError<TestItem>(notFound);
var insufficientPermissionsError = new InsufficientPermissionsError<TestItem>(invalidPermissions);
return new Partial<TestItem>(successfulItems.ToArray(), [notFoundError, insufficientPermissionsError]);
}
[Theory]
[BitAutoData]
public void Partial_CommandResult_BulkRequestWithSuccessAndFailures(Guid successId1, Guid failureId1, Guid failureId2)
{
var listOfRecords = new List<TestItem>
{
new TestItem() { Id = successId1, Value = "SuccessfulRequest" },
new TestItem() { Id = failureId1, Value = "Failed due to not found" },
new TestItem() { Id = failureId2, Value = "Failed due to invalid permissions" }
};
var result = BulkAction(listOfRecords);
Assert.IsType<Partial<TestItem>>(listOfRecords);
}
}