From 5b1b57502f77e1b0fdebcbf734cc284c9e052de9 Mon Sep 17 00:00:00 2001 From: Guillaume Faas Date: Wed, 6 Dec 2023 16:46:15 +0100 Subject: [PATCH] feat: implement builder for Name & DisplayName in CreateConversation --- .../CreateConversation/RequestBuilderTest.cs | 74 +++++++++++++++++ .../CreateConversationRequest.cs | 15 ++++ .../CreateConversationRequestBuilder.cs | 79 +++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 Vonage.Test.Unit/Conversations/CreateConversation/RequestBuilderTest.cs create mode 100644 Vonage/Conversations/CreateConversation/CreateConversationRequestBuilder.cs diff --git a/Vonage.Test.Unit/Conversations/CreateConversation/RequestBuilderTest.cs b/Vonage.Test.Unit/Conversations/CreateConversation/RequestBuilderTest.cs new file mode 100644 index 000000000..f9665f6ad --- /dev/null +++ b/Vonage.Test.Unit/Conversations/CreateConversation/RequestBuilderTest.cs @@ -0,0 +1,74 @@ +using Vonage.Common.Test.Extensions; +using Vonage.Conversations.CreateConversation; +using Xunit; + +namespace Vonage.Test.Unit.Conversations.CreateConversation +{ + public class RequestBuilderTest + { + [Fact] + public void Build_ShouldReturnDefaultValues_GivenNoValuesHaveBeenSet() => + CreateConversationRequest.Build() + .Create() + .Should() + .BeSuccess(); + + [Theory] + [InlineData("")] + [InlineData(" ")] + public void Build_ShouldReturnFailure_GivenDisplayNameIsProvidedButEmpty(string invalidDisplayName) => + CreateConversationRequest.Build() + .WithDisplayName(invalidDisplayName) + .Create() + .Map(request => request.DisplayName) + .Should() + .BeParsingFailure("DisplayName cannot be null or whitespace."); + + [Fact] + public void Build_ShouldReturnFailure_GivenDisplayNameLengthIsAbove50Characters() => + CreateConversationRequest.Build() + .WithDisplayName(new string('a', 51)) + .Create() + .Map(request => request.DisplayName) + .Should() + .BeParsingFailure("DisplayName length cannot be higher than 50."); + + [Theory] + [InlineData("")] + [InlineData(" ")] + public void Build_ShouldReturnFailure_GivenNameIsProvidedButEmpty(string invalidName) => + CreateConversationRequest.Build() + .WithName(invalidName) + .Create() + .Map(request => request.Name) + .Should() + .BeParsingFailure("Name cannot be null or whitespace."); + + [Fact] + public void Build_ShouldReturnFailure_GivenNameLengthIsAbove100Characters() => + CreateConversationRequest.Build() + .WithName(new string('a', 101)) + .Create() + .Map(request => request.Name) + .Should() + .BeParsingFailure("Name length cannot be higher than 100."); + + [Fact] + public void Build_ShouldSetDisplayName() => + CreateConversationRequest.Build() + .WithDisplayName(new string('a', 50)) + .Create() + .Map(request => request.DisplayName) + .Should() + .BeSuccess(new string('a', 50)); + + [Fact] + public void Build_ShouldSetName() => + CreateConversationRequest.Build() + .WithName(new string('a', 100)) + .Create() + .Map(request => request.Name) + .Should() + .BeSuccess(new string('a', 100)); + } +} \ No newline at end of file diff --git a/Vonage/Conversations/CreateConversation/CreateConversationRequest.cs b/Vonage/Conversations/CreateConversation/CreateConversationRequest.cs index 11d2d0572..56908e307 100644 --- a/Vonage/Conversations/CreateConversation/CreateConversationRequest.cs +++ b/Vonage/Conversations/CreateConversation/CreateConversationRequest.cs @@ -1,12 +1,27 @@ using System; using System.Net.Http; using Vonage.Common.Client; +using Vonage.Common.Monads; namespace Vonage.Conversations.CreateConversation; /// public readonly struct CreateConversationRequest : IVonageRequest { + /// + /// + public Maybe DisplayName { get; internal init; } + + /// + /// + public Maybe Name { get; internal init; } + + /// + /// Initializes a builder for CreateConversationRequest. + /// + /// The builder. + public static IBuilderForOptional Build() => new CreateConversationRequestBuilder(); + /// public HttpRequestMessage BuildRequestMessage() => throw new NotImplementedException(); diff --git a/Vonage/Conversations/CreateConversation/CreateConversationRequestBuilder.cs b/Vonage/Conversations/CreateConversation/CreateConversationRequestBuilder.cs new file mode 100644 index 000000000..578d92717 --- /dev/null +++ b/Vonage/Conversations/CreateConversation/CreateConversationRequestBuilder.cs @@ -0,0 +1,79 @@ +using Vonage.Common.Client; +using Vonage.Common.Monads; +using Vonage.Common.Validation; + +namespace Vonage.Conversations.CreateConversation; + +internal class CreateConversationRequestBuilder : IBuilderForOptional +{ + private const int DisplayNameMaxLength = 50; + private const int NameMaxLength = 100; + private Maybe name; + private Maybe displayName; + + public Result Create() => Result.FromSuccess( + new CreateConversationRequest + { + Name = this.name, + DisplayName = this.displayName, + }) + .Map(InputEvaluation.Evaluate) + .Bind(evaluation => evaluation.WithRules( + VerifyName, + VerifyNameLength, + VerifyDisplayName, + VerifyDisplayNameLength)); + + public IBuilderForOptional WithDisplayName(string value) + { + this.displayName = value; + return this; + } + + public IBuilderForOptional WithName(string value) + { + this.name = value; + return this; + } + + private static Result VerifyDisplayName(CreateConversationRequest request) => + request.DisplayName.Match( + some => InputValidation.VerifyNotEmpty(request, some, nameof(request.DisplayName)), + () => request); + + private static Result VerifyDisplayNameLength(CreateConversationRequest request) => + request.DisplayName.Match( + some => InputValidation.VerifyLengthLowerOrEqualThan(request, some, DisplayNameMaxLength, + nameof(request.DisplayName)), + () => request); + + private static Result VerifyName(CreateConversationRequest request) => + request.Name.Match( + some => InputValidation.VerifyNotEmpty(request, some, nameof(request.Name)), + () => request); + + private static Result VerifyNameLength(CreateConversationRequest request) => + request.Name.Match( + some => InputValidation.VerifyLengthLowerOrEqualThan(request, some, NameMaxLength, nameof(request.Name)), + () => request); +} + +/// +/// Represents a builder for optional values. +/// +public interface IBuilderForOptional : IVonageRequestBuilder +{ + /// + /// Sets the Display Name. + /// + /// The display name. + /// The builder. + IBuilderForOptional WithDisplayName(string value); + + /// + /// Sets the Name + /// + /// The name. + /// The builder. + IBuilderForOptional WithName(string value); +} \ No newline at end of file