diff --git a/LibraryManagement.Api.Tests.Unit/DeleteMe.cs b/LibraryManagement.Api.Tests.Unit/DeleteMe.cs deleted file mode 100644 index 8f56efd..0000000 --- a/LibraryManagement.Api.Tests.Unit/DeleteMe.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace LibraryManagement.Api.Tests.Unit -{ - public class DeleteMe - { - [Fact] - public void ShouldBeTrue() => Assert.True(true); - } -} diff --git a/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj b/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj index 9c5b30a..dc41089 100644 --- a/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj +++ b/LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj @@ -10,10 +10,24 @@ - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Exceptions.Add.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Exceptions.Add.cs new file mode 100644 index 0000000..54e1e5c --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Exceptions.Add.cs @@ -0,0 +1,133 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using EFxceptions.Models.Exceptions; +using LibraryManagement.Api.Models.Foundations.Books; +using LibraryManagement.Api.Models.Foundations.Books.Exceptions; +using Microsoft.Data.SqlClient; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books +{ + public partial class BookServiceTests + { + [Fact] + public async Task ShouldThrowCriticalDependencyExceptionOnAddIfSqlErrorOccursAndLogItAsync() + { + // given + Book someBook = CreateRandomBook(); + SqlException sqlException = GetSqlError(); + + var failedBookStorageException = + new FailedBookStorageException(sqlException); + + var expectedBookDependencyException = + new BookDependencyException(failedBookStorageException); + + this.storageBrokerMock.Setup(broker => + broker.InsertBookAsync(someBook)) + .ThrowsAsync(sqlException); + + // when + ValueTask addBookTask = + this.bookService.AddBookAsync(someBook); + + // then + await Assert.ThrowsAsync(() => + addBookTask.AsTask()); + + this.storageBrokerMock.Verify(broker => + broker.InsertBookAsync(someBook), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogCritical(It.Is(SameExceptionAs( + expectedBookDependencyException))), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + + [Fact] + public async Task ShouldThrowDependencyValidationOnAddIfDuplicateKeyErrorOccursAndLogItAsync() + { + // given + Book someBook = CreateRandomBook(); + string someMessage = GetRandomString(); + + var duplicateKeyException = + new DuplicateKeyException(someMessage); + + var alreadyExistsBookException = + new AlreadyExistsBookException(duplicateKeyException); + + var expectedBookDependencyValidationException = + new BookDependencyValidationException(alreadyExistsBookException); + + this.storageBrokerMock.Setup(broker => + broker.InsertBookAsync(someBook)) + .ThrowsAsync(duplicateKeyException); + + // when + ValueTask addBookTask = + this.bookService.AddBookAsync(someBook); + + // then + await Assert.ThrowsAsync(() => + addBookTask.AsTask()); + + this.storageBrokerMock.Verify(broker => + broker.InsertBookAsync(someBook), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedBookDependencyValidationException))), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + + [Fact] + public async Task ShouldThrowServiceExceptionOnAddIfServiceErrorOccursAndLogItAsync() + { + // given + Book someBook = CreateRandomBook(); + var serviceException = new Exception(); + + var failedBookServiceException = + new FailedBookServiceException(serviceException); + + var expectedBookServiceException = + new BookServiceException(failedBookServiceException); + + this.storageBrokerMock.Setup(broker => + broker.InsertBookAsync(someBook)) + .ThrowsAsync(serviceException); + + // when + ValueTask addBookTask = + this.bookService.AddBookAsync(someBook); + + // then + await Assert.ThrowsAsync(() => + addBookTask.AsTask()); + + this.storageBrokerMock.Verify(broker => + broker.InsertBookAsync(someBook), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedBookServiceException))), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Logic.Add.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Logic.Add.cs new file mode 100644 index 0000000..000a25b --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Logic.Add.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using FluentAssertions; +using Force.DeepCloner; +using LibraryManagement.Api.Models.Foundations.Books; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books +{ + public partial class BookServiceTests + { + [Fact] + public async Task ShouldAddBookAsync() + { + // given + Book randomBook = CreateRandomBook(); + Book inputBook = randomBook; + Book storageBook = inputBook; + Book expectedBook = storageBook.DeepClone(); + + this.storageBrokerMock.Setup(broker => + broker.InsertBookAsync(inputBook)) + .ReturnsAsync(storageBook); + + // when + Book actualBook = + await this.bookService.AddBookAsync(inputBook); + + // then + actualBook.Should().BeEquivalentTo(expectedBook); + + this.storageBrokerMock.Verify(broker => + broker.InsertBookAsync(inputBook), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Validations.Add.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Validations.Add.cs new file mode 100644 index 0000000..0844067 --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.Validations.Add.cs @@ -0,0 +1,104 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using LibraryManagement.Api.Models.Foundations.Books; +using LibraryManagement.Api.Models.Foundations.Books.Exceptions; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books +{ + public partial class BookServiceTests + { + [Fact] + public async Task ShouldThrowValidationExceptionOnAddIfBookIsNullAndLogItAsync() + { + // given + Book nullBook = null; + var nullBookException = new NullBookException(); + + var expectedBookValidationException = + new BookValidationException(nullBookException); + + // when + ValueTask addBookTask = + this.bookService.AddBookAsync(nullBook); + + // then + await Assert.ThrowsAsync(() => + addBookTask.AsTask()); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedBookValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.InsertBookAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task ShouldThrowValidationExceptionOnAddIfBookIsInvalidAndLogItAsync( + string invalidText) + { + var invalidBook = new Book + { + BookTitle = invalidText + }; + + var invalidBookException = new InvalidBookException(); + + invalidBookException.AddData( + key: nameof(Book.BookId), + values: "Id is required"); + + invalidBookException.AddData( + key: nameof(Book.ReaderId), + values: "Id is required"); + + invalidBookException.AddData( + key: nameof(Book.BookTitle), + values: "Text is required"); + + invalidBookException.AddData( + key: nameof(Book.Author), + values: "Text is required"); + + invalidBookException.AddData( + key: nameof(Book.Genre), + values: "Text is required"); + + + var expectedBookValidationException = + new BookValidationException(invalidBookException); + + // when + ValueTask addBookTask = + this.bookService.AddBookAsync(invalidBook); + + // then + await Assert.ThrowsAsync(() => + addBookTask.AsTask()); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedBookValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.InsertBookAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.cs new file mode 100644 index 0000000..ea1bcb9 --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Books/BookServiceTests.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using LibraryManagement.Api.Brokers.Loggings; +using LibraryManagement.Api.Brokers.Storages; +using LibraryManagement.Api.Models.Foundations.Books; +using LibraryManagement.Api.Services.Foundations.Books; +using Microsoft.Data.SqlClient; +using Moq; +using Tynamix.ObjectFiller; +using Xeptions; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books +{ + public partial class BookServiceTests + { + private readonly Mock storageBrokerMock; + private readonly Mock loggingBrokerMock; + private readonly IBookService bookService; + + public BookServiceTests() + { + this.storageBrokerMock = new Mock(); + this.loggingBrokerMock = new Mock(); + this.bookService = new BookService( + storageBroker: this.storageBrokerMock.Object, + loggingBroker: this.loggingBrokerMock.Object); + } + + private static Book CreateRandomBook() => + CreateBookFiller(date: GetRandomDateTimeOffset()).Create(); + + private static DateTimeOffset GetRandomDateTimeOffset() => + new DateTimeRange(earliestDate: new DateTime()).GetValue(); + + private static SqlException GetSqlError() => + (SqlException)RuntimeHelpers.GetUninitializedObject(typeof(SqlException)); + + private static string GetRandomString() => + new MnemonicString().GetValue(); + + private Expression> SameExceptionAs(Xeption expectedException) => + actualException => actualException.SameExceptionAs(expectedException); + + private static Filler CreateBookFiller(DateTimeOffset date) + { + var filler = new Filler(); + + filler.Setup() + .OnType().Use(date); + + return filler; + } + } +} diff --git a/LibraryManagement.Api/LibraryManagement.Api.csproj b/LibraryManagement.Api/LibraryManagement.Api.csproj index fcfae9d..08bc71b 100644 --- a/LibraryManagement.Api/LibraryManagement.Api.csproj +++ b/LibraryManagement.Api/LibraryManagement.Api.csproj @@ -19,6 +19,7 @@ + diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/AlreadyExistsBookException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/AlreadyExistsBookException.cs new file mode 100644 index 0000000..606917a --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/AlreadyExistsBookException.cs @@ -0,0 +1,16 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class AlreadyExistsBookException : Xeption + { + public AlreadyExistsBookException(Exception innerException) + : base(message: "Book already exists.", innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyException.cs new file mode 100644 index 0000000..57ad8b2 --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class BookDependencyException : Xeption + { + public BookDependencyException(Xeption innerException) + : base(message: "Book dependency error occurred, contact support.", + innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyValidationException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyValidationException.cs new file mode 100644 index 0000000..ab12175 --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookDependencyValidationException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class BookDependencyValidationException : Xeption + { + public BookDependencyValidationException(Xeption innerException) + : base(message: "Book dependency validation errors occurred, fix the errors and try again.", + innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookServiceException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookServiceException.cs new file mode 100644 index 0000000..1e7ed2c --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookServiceException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class BookServiceException : Xeption + { + public BookServiceException(Xeption innerException) + : base(message: "Book service error occurred, contact support.", + innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookValidationException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookValidationException.cs new file mode 100644 index 0000000..bdc8ed1 --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/BookValidationException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class BookValidationException : Xeption + { + public BookValidationException(Xeption innerException) + : base(message: "Book validation errors occurred, fix the errors and try again.", + innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookServiceException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookServiceException.cs new file mode 100644 index 0000000..fac8434 --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookServiceException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class FailedBookServiceException : Xeption + { + public FailedBookServiceException(Exception innerException) + : base(message: "Failed book service error occurred, contact support.", + innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookStorageException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookStorageException.cs new file mode 100644 index 0000000..1463b8f --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/FailedBookStorageException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class FailedBookStorageException : Xeption + { + public FailedBookStorageException(Exception innerException) + : base(message: "Failed book storage error occurred, contact support.", + innerException) + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/InvalidBookException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/InvalidBookException.cs new file mode 100644 index 0000000..8ef0135 --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/InvalidBookException.cs @@ -0,0 +1,16 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class InvalidBookException : Xeption + { + public InvalidBookException() + : base(message: "Book is invalid.") + { } + } +} diff --git a/LibraryManagement.Api/Models/Foundations/Books/Exceptions/NullBookException.cs b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/NullBookException.cs new file mode 100644 index 0000000..f6d8b4f --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Books/Exceptions/NullBookException.cs @@ -0,0 +1,16 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Books.Exceptions +{ + public class NullBookException : Xeption + { + public NullBookException() + : base(message: "Book is null.") + { } + } +} diff --git a/LibraryManagement.Api/Services/Foundations/Books/BookService.Exceptions.cs b/LibraryManagement.Api/Services/Foundations/Books/BookService.Exceptions.cs new file mode 100644 index 0000000..ffa4d07 --- /dev/null +++ b/LibraryManagement.Api/Services/Foundations/Books/BookService.Exceptions.cs @@ -0,0 +1,95 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using EFxceptions.Models.Exceptions; +using LibraryManagement.Api.Models.Foundations.Books; +using LibraryManagement.Api.Models.Foundations.Books.Exceptions; +using Microsoft.Data.SqlClient; +using Xeptions; + +namespace LibraryManagement.Api.Services.Foundations.Books +{ + public partial class BookService + { + private delegate ValueTask ReturningBookFunction(); + + private async ValueTask TryCatch(ReturningBookFunction returningBookFunction) + { + try + { + return await returningBookFunction(); + } + catch (NullBookException nullBookException) + { + throw CreateAndLogValidationException(nullBookException); + } + catch (InvalidBookException invalidBookException) + { + throw CreateAndLogValidationException(invalidBookException); + } + catch (SqlException sqlException) + { + var failedBookStorageException = + new FailedBookStorageException(sqlException); + + throw CreateAndLogCriticalDependencyException(failedBookStorageException); + } + catch (DuplicateKeyException duplicateKeyException) + { + var alreadyExistsBookException = + new AlreadyExistsBookException(duplicateKeyException); + + throw CreateAndLogDependencyValidationException(alreadyExistsBookException); + } + catch (Exception exception) + { + var failedBookServiceException = + new FailedBookServiceException(exception); + + throw CreateAndLogServiceException(failedBookServiceException); + } + } + + private BookValidationException CreateAndLogValidationException(Xeption exception) + { + var bookValidationException = + new BookValidationException(exception); + + this.loggingBroker.LogError(bookValidationException); + + return bookValidationException; + } + + private BookDependencyException CreateAndLogCriticalDependencyException(Xeption exception) + { + var bookDependencyException = + new BookDependencyException(exception); + + this.loggingBroker.LogCritical(bookDependencyException); + + return bookDependencyException; + } + + private BookDependencyValidationException CreateAndLogDependencyValidationException(Xeption exception) + { + var bookDependencyValidationException = + new BookDependencyValidationException(exception); + + this.loggingBroker.LogError(bookDependencyValidationException); + + return bookDependencyValidationException; + } + + private BookServiceException CreateAndLogServiceException(Xeption exception) + { + var bookServiceException = + new BookServiceException(exception); + + this.loggingBroker.LogError(bookServiceException); + + return bookServiceException; + } + } +} diff --git a/LibraryManagement.Api/Services/Foundations/Books/BookService.Validations.cs b/LibraryManagement.Api/Services/Foundations/Books/BookService.Validations.cs new file mode 100644 index 0000000..be3a158 --- /dev/null +++ b/LibraryManagement.Api/Services/Foundations/Books/BookService.Validations.cs @@ -0,0 +1,61 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using LibraryManagement.Api.Models.Foundations.Books; +using LibraryManagement.Api.Models.Foundations.Books.Exceptions; + +namespace LibraryManagement.Api.Services.Foundations.Books +{ + public partial class BookService + { + private void ValidateBookOnAdd(Book book) + { + ValidateBookNotNull(book); + + Validate( + (Rule: IsInvalid(book.BookId), Parameter: nameof(Book.BookId)), + (Rule: IsInvalid(book.ReaderId), Parameter: nameof(Book.ReaderId)), + (Rule: IsInvalid(book.BookTitle), Parameter: nameof(Book.BookTitle)), + (Rule: IsInvalid(book.Author), Parameter: nameof(Book.Author)), + (Rule: IsInvalid(book.Genre), Parameter: nameof(Book.Genre))); + } + private void ValidateBookNotNull(Book book) + { + if (book is null) + { + throw new NullBookException(); + } + } + + private static dynamic IsInvalid(Guid id) => new + { + Condition = id == Guid.Empty, + Message = "Id is required" + }; + + private static dynamic IsInvalid(string text) => new + { + Condition = string.IsNullOrWhiteSpace(text), + Message = "Text is required" + }; + + private static void Validate(params (dynamic Rule, string Parameter)[] validations) + { + var invalidBookException = new InvalidBookException(); + + foreach ((dynamic rule, string parameter) in validations) + { + if (rule.Condition) + { + invalidBookException.UpsertDataList( + key: parameter, + value: rule.Message); + } + } + + invalidBookException.ThrowIfContainsErrors(); + } + } +} diff --git a/LibraryManagement.Api/Services/Foundations/Books/BookService.cs b/LibraryManagement.Api/Services/Foundations/Books/BookService.cs new file mode 100644 index 0000000..463db8c --- /dev/null +++ b/LibraryManagement.Api/Services/Foundations/Books/BookService.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using LibraryManagement.Api.Brokers.Loggings; +using LibraryManagement.Api.Brokers.Storages; +using LibraryManagement.Api.Models.Foundations.Books; + +namespace LibraryManagement.Api.Services.Foundations.Books +{ + public partial class BookService : IBookService + { + private readonly IStorageBroker storageBroker; + private readonly ILoggingBroker loggingBroker; + + public BookService( + IStorageBroker storageBroker, + ILoggingBroker loggingBroker) + { + this.storageBroker = storageBroker; + this.loggingBroker = loggingBroker; + } + + public ValueTask AddBookAsync(Book book) => + TryCatch(async () => + { + ValidateBookOnAdd(book); + + return await this.storageBroker.InsertBookAsync(book); + }); + } +} diff --git a/LibraryManagement.Api/Services/Foundations/Books/IBookService.cs b/LibraryManagement.Api/Services/Foundations/Books/IBookService.cs new file mode 100644 index 0000000..c6dea03 --- /dev/null +++ b/LibraryManagement.Api/Services/Foundations/Books/IBookService.cs @@ -0,0 +1,14 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using LibraryManagement.Api.Models.Foundations.Books; + +namespace LibraryManagement.Api.Services.Foundations.Books +{ + public interface IBookService + { + ValueTask AddBookAsync(Book book); + } +}