From f11e417735d73e1c68aadebbd42df4c44b6e7e4c Mon Sep 17 00:00:00 2001 From: tr00d Date: Tue, 15 Oct 2024 08:43:23 +0200 Subject: [PATCH] refactor: improve error information with tailored format --- Vonage.Test/Common/Client/ApiErrorsTest.cs | 66 ++++++++++++++++++ .../Client/Data/ApiErrorNetwork-response.json | 5 ++ .../Data/ApiErrorStandard-response.json | 6 ++ .../Client/Data/ApiErrorVideo-response.json | 4 ++ .../Common/Client/VonageHttpClientTest.cs | 69 +++++++++++-------- .../Common/Extensions/FsCheckExtensions.cs | 4 +- .../Meetings/UpdateThemeLogo/E2ETest.cs | 28 ++++---- Vonage.Test/Vonage.Test.csproj | 9 +++ Vonage/Common/Client/UserAgentProvider.cs | 4 +- Vonage/Common/Client/VonageHttpClient.cs | 50 +++++++------- Vonage/Common/ErrorResponse.cs | 7 -- Vonage/Common/Failures/HttpFailure.cs | 67 +++++------------- Vonage/Common/VideoApiError.cs | 26 +++++++ Vonage/Conversations/ConversationsClient.cs | 10 ++- Vonage/Meetings/MeetingsClient.cs | 9 ++- .../UpdateThemeLogo/UpdateThemeLogoUseCase.cs | 35 +++++----- .../NumberInsightV2/NumberInsightV2Client.cs | 9 ++- .../NumberVerificationClient.cs | 19 +++-- .../ProactiveConnectClient.cs | 8 ++- Vonage/SimSwap/SimSwapClient.cs | 40 ++++++----- Vonage/SubAccounts/SubAccountsClient.cs | 41 ++++++----- Vonage/Users/UsersClient.cs | 10 ++- Vonage/VerifyV2/VerifyV2Client.cs | 6 +- Vonage/Video/Archives/ArchiveClient.cs | 9 ++- .../AudioConnector/AudioConnectorClient.cs | 5 +- Vonage/Video/Broadcast/BroadcastClient.cs | 9 ++- .../ExperienceComposerClient.cs | 9 ++- .../Video/LiveCaptions/LiveCaptionsClient.cs | 5 +- Vonage/Video/Moderation/ModerationClient.cs | 9 ++- .../CreateSession/CreateSessionUseCase.cs | 15 ++-- Vonage/Video/Sessions/SessionClient.cs | 7 +- Vonage/Video/Signaling/SignalingClient.cs | 9 ++- Vonage/Video/Sip/SipClient.cs | 9 ++- 33 files changed, 389 insertions(+), 229 deletions(-) create mode 100644 Vonage.Test/Common/Client/ApiErrorsTest.cs create mode 100644 Vonage.Test/Common/Client/Data/ApiErrorNetwork-response.json create mode 100644 Vonage.Test/Common/Client/Data/ApiErrorStandard-response.json create mode 100644 Vonage.Test/Common/Client/Data/ApiErrorVideo-response.json delete mode 100644 Vonage/Common/ErrorResponse.cs create mode 100644 Vonage/Common/VideoApiError.cs diff --git a/Vonage.Test/Common/Client/ApiErrorsTest.cs b/Vonage.Test/Common/Client/ApiErrorsTest.cs new file mode 100644 index 000000000..b5afa18f8 --- /dev/null +++ b/Vonage.Test/Common/Client/ApiErrorsTest.cs @@ -0,0 +1,66 @@ +#region +using System.Net; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Vonage.Common; +using Vonage.Common.Client; +using Vonage.Common.Failures; +using Vonage.Common.Monads; +using Vonage.Serialization; +using Vonage.Test.Common.Extensions; +using Vonage.Test.Common.TestHelpers; +using Xunit; +#endregion + +namespace Vonage.Test.Common.Client; + +[Trait("Category", "Unit")] +public class ApiErrorsTest +{ + private readonly SerializationTestHelper helper = new SerializationTestHelper(typeof(ApiErrorsTest).Namespace, + JsonSerializerBuilder.BuildWithSnakeCase()); + + private readonly JsonSerializer serializer = new JsonSerializer(); + + [Fact] + public async Task ShouldReturnErrorContent_GivenVideoApiError() + { + var expectedJson = this.helper.GetResponseJson("ApiErrorVideo"); + var sut = this.BuildClient(expectedJson); + var result = await sut.SendAsync(BuildFakeRequest()); + result.Should().BeFailure(HttpFailure.From(HttpStatusCode.BadRequest, + "You do not pass in a session ID or you pass in an invalid session ID.", expectedJson)); + } + + [Fact] + public async Task ShouldReturnErrorContent_GivenNetworkApiError() + { + var expectedJson = this.helper.GetResponseJson("ApiErrorNetwork"); + var sut = this.BuildClient(expectedJson); + var result = await sut.SendAsync(BuildFakeRequest()); + result.Should().BeFailure(HttpFailure.From(HttpStatusCode.BadRequest, + "Client specified an invalid argument, request body or query param", expectedJson)); + } + + [Fact] + public async Task ShouldReturnErrorContent_GivenStandardApiError() + { + var expectedJson = this.helper.GetResponseJson("ApiErrorStandard"); + var sut = this.BuildClient(expectedJson); + var result = await sut.SendAsync(BuildFakeRequest()); + result.Should().BeFailure(HttpFailure.From(HttpStatusCode.BadRequest, + "You did not provide correct credentials.", expectedJson)); + } + + private static Result BuildFakeRequest() => + Result.FromSuccess(new VonageHttpClientTest.FakeRequest()); + + private VonageHttpClient BuildClient(string responseContent) where T : IApiError + { + var messageHandler = FakeHttpRequestHandler.Build(HttpStatusCode.BadRequest) + .WithResponseContent(responseContent); + var configuration = new VonageHttpClientConfiguration(messageHandler.ToHttpClient(), + new AuthenticationHeaderValue("Anonymous"), "userAgent"); + return new VonageHttpClient(configuration, this.serializer); + } +} \ No newline at end of file diff --git a/Vonage.Test/Common/Client/Data/ApiErrorNetwork-response.json b/Vonage.Test/Common/Client/Data/ApiErrorNetwork-response.json new file mode 100644 index 000000000..4f0d1dc29 --- /dev/null +++ b/Vonage.Test/Common/Client/Data/ApiErrorNetwork-response.json @@ -0,0 +1,5 @@ +{ + "status": 400, + "code": "INVALID_ARGUMENT", + "message": "Client specified an invalid argument, request body or query param" +} \ No newline at end of file diff --git a/Vonage.Test/Common/Client/Data/ApiErrorStandard-response.json b/Vonage.Test/Common/Client/Data/ApiErrorStandard-response.json new file mode 100644 index 000000000..8337005e4 --- /dev/null +++ b/Vonage.Test/Common/Client/Data/ApiErrorStandard-response.json @@ -0,0 +1,6 @@ +{ + "type": "https://developer.vonage.com/api-errors/#unathorized", + "title": "You did not provide correct credentials.", + "detail": "Check that you're using the correct credentials, and that your account has this feature enabled", + "instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf" +} \ No newline at end of file diff --git a/Vonage.Test/Common/Client/Data/ApiErrorVideo-response.json b/Vonage.Test/Common/Client/Data/ApiErrorVideo-response.json new file mode 100644 index 000000000..a80337402 --- /dev/null +++ b/Vonage.Test/Common/Client/Data/ApiErrorVideo-response.json @@ -0,0 +1,4 @@ +{ + "code": 400, + "message": "You do not pass in a session ID or you pass in an invalid session ID." +} \ No newline at end of file diff --git a/Vonage.Test/Common/Client/VonageHttpClientTest.cs b/Vonage.Test/Common/Client/VonageHttpClientTest.cs index 89619c5d3..0b7346d44 100644 --- a/Vonage.Test/Common/Client/VonageHttpClientTest.cs +++ b/Vonage.Test/Common/Client/VonageHttpClientTest.cs @@ -1,4 +1,5 @@ -using System; +#region +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -13,6 +14,7 @@ using Vonage.Test.Common.Extensions; using Vonage.Test.Common.TestHelpers; using Xunit; +#endregion namespace Vonage.Test.Common.Client; @@ -39,27 +41,30 @@ public async Task SendAsync_ShouldThrowException_GivenOperationExceedsTimeout() [Property] public Property SendAsync_VerifyReturnsFailureGivenApiResponseIsError() => this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(), - configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); + configuration => + new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); [Property] public Property SendAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() => this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(), - configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); + configuration => + new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); [Fact] public async Task SendAsync_VerifyReturnsFailureGivenRequestIsFailure() => await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) => - new VonageHttpClient(configuration, this.serializer).SendAsync(failureRequest)); + new VonageHttpClient(configuration, this.serializer).SendAsync(failureRequest)); [Fact] public async Task SendAsync_VerifyReturnsFailureGivenTokenGenerationFails() => await this.VerifyReturnsFailureGivenTokenGenerationFails(configuration => - new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); + new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); [Fact] public async Task SendAsync_VerifyReturnsUnitGivenApiResponseIsSuccess() => await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequest(), - configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); + configuration => + new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); [Fact] public async Task SendWithoutHeaderAsync_ShouldThrowException_GivenOperationExceedsTimeout() => @@ -69,24 +74,28 @@ await this.VerifyReturnsFailureGivenOperationExceedsTimeout(client => [Property] public Property SendWithoutHeaderAsync_VerifyReturnsFailureGivenApiResponseIsError() => this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(), - configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); + configuration => + new VonageHttpClient(configuration, this.serializer).SendAsync(this.request)); [Property] public Property SendWithoutHeaderAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() => this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync(this.request)); + new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync( + this.request)); [Fact] public async Task SendWithoutHeaderAsync_VerifyReturnsFailureGivenRequestIsFailure() => await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) => - new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync(failureRequest)); + new VonageHttpClient(configuration, this.serializer) + .SendWithoutHeadersAsync(failureRequest)); [Fact] public async Task SendWithoutHeaderAsync_VerifyReturnsUnitGivenApiResponseIsSuccess() => await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync(this.request)); + new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync( + this.request)); [Fact] public async Task SendWithRawResponseAsync_ShouldThrowException_GivenOperationExceedsTimeout() => @@ -97,29 +106,33 @@ await this.VerifyReturnsFailureGivenOperationExceedsTimeout(client => public async Task SendWithRawResponseAsync_VerifyReturnsExpectedValueGivenApiResponseIsSuccess() => await this.VerifyReturnsRawContentGivenApiResponseIsSuccess(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(this.request)); + new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync( + this.request)); [Property] public Property SendWithRawResponseAsync_VerifyReturnsFailureGivenApiResponseIsError() => this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(this.request)); + new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync( + this.request)); [Property] public Property SendWithRawResponseAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() => this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(this.request)); + new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync( + this.request)); [Fact] public async Task SendWithRawResponseAsync_VerifyReturnsFailureGivenRequestIsFailure() => await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) => - new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(failureRequest)); + new VonageHttpClient(configuration, this.serializer) + .SendWithRawResponseAsync(failureRequest)); [Fact] public async Task SendWithRawResponseAsync_VerifyReturnsFailureGivenTokenGenerationFails() => await this.VerifyReturnsFailureGivenTokenGenerationFails(configuration => - new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync( + new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync( this.request)); [Fact] @@ -131,7 +144,7 @@ await this.VerifyReturnsFailureGivenOperationExceedsTimeout(client => public async Task SendWithResponseAsync_VerifyReturnsExpectedValueGivenApiResponseIsSuccess() => await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer) + new VonageHttpClient(configuration, this.serializer) .SendWithResponseAsync( this.request)); @@ -139,7 +152,7 @@ await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequ public async Task SendWithResponseAsync_VerifyReturnsFailureGivenApiResponseCannotBeParsed() => await this.VerifyReturnsFailureGivenApiResponseCannotBeParsed(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer) + new VonageHttpClient(configuration, this.serializer) .SendWithResponseAsync( this.request)); @@ -147,7 +160,7 @@ await this.VerifyReturnsFailureGivenApiResponseCannotBeParsed(BuildExpectedReque public Property SendWithResponseAsync_VerifyReturnsFailureGivenApiResponseIsError() => this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer) + new VonageHttpClient(configuration, this.serializer) .SendWithResponseAsync( this.request)); @@ -155,21 +168,23 @@ public Property SendWithResponseAsync_VerifyReturnsFailureGivenApiResponseIsErro public Property SendWithResponseAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() => this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(), configuration => - new VonageHttpClient(configuration, this.serializer) + new VonageHttpClient(configuration, this.serializer) .SendWithResponseAsync( this.request)); [Fact] public async Task SendWithResponseAsync_VerifyReturnsFailureGivenRequestIsFailure() => await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) => - new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync( - failureRequest)); + new VonageHttpClient(configuration, this.serializer) + .SendWithResponseAsync( + failureRequest)); [Fact] public async Task SendWithResponseAsync_VerifyReturnsFailureGivenTokenGenerationFails() => await this.VerifyReturnsFailureGivenTokenGenerationFails(configuration => - new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync( - this.request)); + new VonageHttpClient(configuration, this.serializer) + .SendWithResponseAsync( + this.request)); private static ExpectedRequest BuildExpectedRequest() => new ExpectedRequest @@ -246,18 +261,18 @@ private Property VerifyReturnsFailureGivenErrorCannotBeParsed( .Result .Should() .BeFailure(HttpFailure.From(statusCode, - DeserializationFailure.From(typeof(ErrorResponse), jsonError).GetFailureMessage(), + DeserializationFailure.From(typeof(VideoApiError), jsonError).GetFailureMessage(), jsonError)); }); private async Task VerifyReturnsFailureGivenOperationExceedsTimeout( - Func>> operation) + Func, Task>> operation) { var httpClient = FakeHttpRequestHandler.Build(HttpStatusCode.OK).WithDelay(TimeSpan.FromSeconds(5)) .ToHttpClient(); httpClient.Timeout = TimeSpan.FromMilliseconds(100); var client = - new VonageHttpClient( + new VonageHttpClient( new VonageHttpClientConfiguration(httpClient, new AuthenticationHeaderValue("Anonymous"), this.fixture.Create()), this.serializer); await operation(client).Should().BeFailureAsync(); @@ -296,7 +311,7 @@ private async Task VerifyReturnsRawContentGivenApiResponseIsSuccess(ExpectedRequ result.Should().BeSuccess(expectedResponse); } - private struct FakeRequest : IVonageRequest + internal struct FakeRequest : IVonageRequest { public Guid Id { get; set; } diff --git a/Vonage.Test/Common/Extensions/FsCheckExtensions.cs b/Vonage.Test/Common/Extensions/FsCheckExtensions.cs index 681e4fdd9..f04cc3345 100644 --- a/Vonage.Test/Common/Extensions/FsCheckExtensions.cs +++ b/Vonage.Test/Common/Extensions/FsCheckExtensions.cs @@ -15,10 +15,10 @@ public static class FsCheckExtensions /// Retrieves a generator that produces error responses with invalid status codes. /// /// An Arbitrary of ErrorResponse. - internal static Arbitrary GetErrorResponses() => + internal static Arbitrary GetErrorResponses() => Arb.From(from message in GetAny().Generator from code in GetInvalidStatusCodes().Generator - select new ErrorResponse(code, message)); + select new VideoApiError(code, message)); /// /// Retrieves a HttpStatusCode generator that produces only invalid codes. diff --git a/Vonage.Test/Meetings/UpdateThemeLogo/E2ETest.cs b/Vonage.Test/Meetings/UpdateThemeLogo/E2ETest.cs index e02cae029..180281607 100644 --- a/Vonage.Test/Meetings/UpdateThemeLogo/E2ETest.cs +++ b/Vonage.Test/Meetings/UpdateThemeLogo/E2ETest.cs @@ -1,4 +1,5 @@ -using System; +#region +using System; using System.Collections.Generic; using System.IO.Abstractions.TestingHelpers; using System.Net; @@ -17,6 +18,7 @@ using Vonage.Test.Common.Extensions; using Vonage.Test.Common.TestHelpers; using Xunit; +#endregion namespace Vonage.Test.Meetings.UpdateThemeLogo; @@ -80,7 +82,7 @@ public void ShouldReturnFailureWhenFinalizingLogo_GivenApiErrorCannotBeParsed() .Result .Should() .BeFailure(HttpFailure.From(statusCode, - DeserializationFailure.From(typeof(ErrorResponse), expectedContent).GetFailureMessage(), + DeserializationFailure.From(typeof(StandardApiError), expectedContent).GetFailureMessage(), expectedContent)); } @@ -89,11 +91,11 @@ public void ShouldReturnFailureWhenFinalizingLogo_GivenStatusCodeIsFailure() { this.RetrievingLogosUrlReturnsValidResponse(); this.UploadingLogoReturnsValidResponse(); - var error = new ErrorResponse(HttpStatusCode.Unauthorized, "Some content."); + var error = new StandardApiError("type", "title", "detail", "instance"); var errorContent = this.serializer.SerializeObject(error); var expectedResponse = new MappingResponse { - Code = error.Code, + Code = HttpStatusCode.BadRequest, Content = errorContent, }; this.customHandler = this.customHandler.GivenRequest(this.BuildExpectedRequestForFinalizing()) @@ -101,7 +103,7 @@ public void ShouldReturnFailureWhenFinalizingLogo_GivenStatusCodeIsFailure() this.Operation(this.BuildConfiguration()) .Result .Should() - .BeFailure(HttpFailure.From(error.Code, error.Message, errorContent)); + .BeFailure(HttpFailure.From(HttpStatusCode.BadRequest, error.Title, errorContent)); } [Fact] @@ -119,7 +121,7 @@ public void ShouldReturnFailureWhenRetrievingUploadUrls_GivenApiErrorCannotBePar .Result .Should() .BeFailure(HttpFailure.From(statusCode, - DeserializationFailure.From(typeof(ErrorResponse), expectedContent).GetFailureMessage(), + DeserializationFailure.From(typeof(StandardApiError), expectedContent).GetFailureMessage(), expectedContent)); } @@ -154,17 +156,17 @@ public async Task ShouldReturnFailureWhenRetrievingUploadUrls_GivenResponseConta [Fact] public void ShouldReturnFailureWhenRetrievingUploadUrls_GivenStatusCodeIsFailure() { - var error = new ErrorResponse(HttpStatusCode.BadRequest, "Some content"); + var error = new StandardApiError("type", "title", "detail", "instance"); var errorContent = this.serializer.SerializeObject(error); var expectedResponse = new MappingResponse { - Code = error.Code, + Code = HttpStatusCode.BadRequest, Content = errorContent, }; this.customHandler = this.customHandler.GivenRequest(BuildExpectedRequestForUrlRetrieval()) .RespondWith(expectedResponse); this.Operation(this.BuildConfiguration()).Result.Should() - .BeFailure(HttpFailure.From(error.Code, error.Message, errorContent)); + .BeFailure(HttpFailure.From(HttpStatusCode.BadRequest, error.Title, errorContent)); } [Fact] @@ -183,7 +185,7 @@ public void ShouldReturnFailureWhenUploadingLogo_GivenApiErrorCannotBeParsed() .Result .Should() .BeFailure(HttpFailure.From(statusCode, - DeserializationFailure.From(typeof(ErrorResponse), expectedContent).GetFailureMessage(), + DeserializationFailure.From(typeof(StandardApiError), expectedContent).GetFailureMessage(), expectedContent)); } @@ -191,11 +193,11 @@ public void ShouldReturnFailureWhenUploadingLogo_GivenApiErrorCannotBeParsed() public void ShouldReturnFailureWhenUploadingLogo_GivenStatusCodeIsFailure() { this.RetrievingLogosUrlReturnsValidResponse(); - var error = new ErrorResponse(HttpStatusCode.Unauthorized, "Some content."); + var error = new StandardApiError("type", "title", "detail", "instance"); var errorContent = this.serializer.SerializeObject(error); var expectedResponse = new MappingResponse { - Code = error.Code, + Code = HttpStatusCode.BadRequest, Content = errorContent, }; this.customHandler = this.customHandler.GivenRequest(this.BuildExpectedRequestForUploading()) @@ -203,7 +205,7 @@ public void ShouldReturnFailureWhenUploadingLogo_GivenStatusCodeIsFailure() this.Operation(this.BuildConfiguration()) .Result .Should() - .BeFailure(HttpFailure.From(error.Code, error.Message, errorContent)); + .BeFailure(HttpFailure.From(HttpStatusCode.BadRequest, error.Title, errorContent)); } [Fact] diff --git a/Vonage.Test/Vonage.Test.csproj b/Vonage.Test/Vonage.Test.csproj index a1f598caa..412ada3ad 100644 --- a/Vonage.Test/Vonage.Test.csproj +++ b/Vonage.Test/Vonage.Test.csproj @@ -1343,6 +1343,15 @@ PreserveNewest + + Always + + + Always + + + Always + diff --git a/Vonage/Common/Client/UserAgentProvider.cs b/Vonage/Common/Client/UserAgentProvider.cs index 260ac088c..fe5584ffb 100644 --- a/Vonage/Common/Client/UserAgentProvider.cs +++ b/Vonage/Common/Client/UserAgentProvider.cs @@ -1,5 +1,7 @@ +#region using System.Reflection; using System.Runtime.InteropServices; +#endregion namespace Vonage.Common.Client; @@ -21,7 +23,7 @@ public static string GetFormattedUserAgent(string userAgent) .GetVersionInfo(typeof(int).Assembly.Location) .ProductVersion; #endif - var libraryVersion = typeof(VonageHttpClient) + var libraryVersion = typeof(VonageHttpClient) .GetTypeInfo() .Assembly .GetCustomAttribute() diff --git a/Vonage/Common/Client/VonageHttpClient.cs b/Vonage/Common/Client/VonageHttpClient.cs index 314b9a773..537de18e9 100644 --- a/Vonage/Common/Client/VonageHttpClient.cs +++ b/Vonage/Common/Client/VonageHttpClient.cs @@ -1,23 +1,22 @@ -using System; +#region +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using Vonage.Common.Failures; using Vonage.Common.Monads; +#endregion namespace Vonage.Common.Client; -/// -/// Represents a custom http client for Vonage APIs. -/// -public class VonageHttpClient +internal class VonageHttpClient where TError : IApiError { private readonly HttpClient client; private readonly IJsonSerializer jsonSerializer; private readonly Result requestOptions; private readonly string userAgent; - + /// /// Creates a custom Http Client for Vonage purposes. /// @@ -32,11 +31,11 @@ public VonageHttpClient(VonageHttpClientConfiguration configuration, IJsonSerial .Map(header => new HttpClientOptions(header, UserAgentProvider.GetFormattedUserAgent(this.userAgent))); } - - internal VonageHttpClient WithDifferentHeader(Result header) => - new VonageHttpClient(new VonageHttpClientConfiguration(this.client, header, this.userAgent), + + internal VonageHttpClient WithDifferentHeader(Result header) where T : IApiError => + new VonageHttpClient(new VonageHttpClientConfiguration(this.client, header, this.userAgent), this.jsonSerializer); - + /// /// Sends a HttpRequest. /// @@ -45,7 +44,7 @@ internal VonageHttpClient WithDifferentHeader(Result public async Task> SendAsync(Result request) where T : IVonageRequest => await this.SendRequest(request, this.BuildHttpRequestMessage, this.ParseFailure, CreateSuccessResult) .ConfigureAwait(false); - + /// /// Sends a HttpRequest without Authorization and UserAgent headers. /// @@ -54,7 +53,7 @@ await this.SendRequest(request, this.BuildHttpRequestMessage, this.ParseFailure< public async Task> SendWithoutHeadersAsync(Result request) where T : IVonageRequest => await this.SendRequest(request, value => value.BuildRequestMessage(), this.ParseFailure, CreateSuccessResult).ConfigureAwait(false); - + /// /// Sends a HttpRequest and returns the raw content. /// @@ -65,7 +64,7 @@ public async Task> SendWithRawResponseAsync(Result await this.SendRequest(request, this.BuildHttpRequestMessage, this.ParseFailure, responseData => responseData.Content).ConfigureAwait(false); - + /// /// Sends a HttpRequest and parses the response. /// @@ -75,38 +74,39 @@ public async Task> SendWithResponseAsync( where TRequest : IVonageRequest => await this.SendRequest(request, this.BuildHttpRequestMessage, this.ParseFailure, this.ParseSuccess).ConfigureAwait(false); - + private Result BuildHttpRequestMessage(T value) where T : IVonageRequest => this.requestOptions .Map(options => value .BuildRequestMessage() .WithAuthenticationHeader(options.AuthenticationHeader) .WithUserAgent(options.UserAgent)); - + private HttpFailure CreateFailureResult(HttpStatusCode code, string responseContent) => this.jsonSerializer - .DeserializeObject(responseContent) - .Match(success => HttpFailure.From(code, success.Message, responseContent), + .DeserializeObject(responseContent) + .Match( + success => success.ToFailure() with {Code = code, Json = responseContent}, failure => HttpFailure.From(code, failure.GetFailureMessage(), responseContent)); - + private static HttpFailure CreateFailureResult(HttpStatusCode code) => HttpFailure.From(code); - + private static Result CreateSuccessResult(ResponseData response) => Result.FromSuccess(Unit.Default); - + private static async Task ExtractResponseData(HttpResponseMessage response) => new ResponseData(response.StatusCode, response.IsSuccessStatusCode, await response.Content.ReadAsStringAsync().ConfigureAwait(false)); - + private Result ParseFailure(ResponseData response) => MaybeExtensions.From(response.Content) .Match(value => this.CreateFailureResult(response.Code, value), () => CreateFailureResult(response.Code)) .ToResult(); - + private Result ParseSuccess(ResponseData response) => this.jsonSerializer .DeserializeObject(response.Content) .Match(Result.FromSuccess, Result.FromFailure); - + private async Task> SendRequest( Result request, Func> httpRequestConversion, @@ -118,8 +118,8 @@ await request .MapAsync(ExtractResponseData) .Bind(response => !response.IsSuccessStatusCode ? failure(response) : success(response)) .ConfigureAwait(false); - + private sealed record ResponseData(HttpStatusCode Code, bool IsSuccessStatusCode, string Content); - + private sealed record HttpClientOptions(AuthenticationHeaderValue AuthenticationHeader, string UserAgent); } \ No newline at end of file diff --git a/Vonage/Common/ErrorResponse.cs b/Vonage/Common/ErrorResponse.cs deleted file mode 100644 index 136f9b936..000000000 --- a/Vonage/Common/ErrorResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -#region -using System.Net; -#endregion - -namespace Vonage.Common; - -internal record ErrorResponse(HttpStatusCode Code, string Message); \ No newline at end of file diff --git a/Vonage/Common/Failures/HttpFailure.cs b/Vonage/Common/Failures/HttpFailure.cs index 670593988..7d68311e4 100644 --- a/Vonage/Common/Failures/HttpFailure.cs +++ b/Vonage/Common/Failures/HttpFailure.cs @@ -1,47 +1,32 @@ -using System; +#region +using System; using System.Net; -using System.Text.Json.Serialization; using Vonage.Common.Exceptions; using Vonage.Common.Monads; +#endregion namespace Vonage.Common.Failures; /// -public readonly struct HttpFailure : IResultFailure +public record HttpFailure(HttpStatusCode Code, string Message, string Json) : IResultFailure { - /// - /// The status code. - /// - /// Mandatory for deserialization. - public HttpStatusCode Code { get; } - - /// - /// The JSON content. - /// - public string Json { get; } - - /// - /// The failure message. - /// - /// Mandatory for deserialization. - public string Message { get; } - /// public Type Type => typeof(HttpFailure); - /// - /// Create a HttpFailure. - /// - /// The status code. - /// The failure message. - /// The JSON content. - [JsonConstructor] - public HttpFailure(HttpStatusCode code, string message, string json) + /// + public string GetFailureMessage() => string.IsNullOrWhiteSpace(this.Message) + ? $"{(int) this.Code}." + : $"{(int) this.Code} - {this.Message} - {this.Json}."; + + /// + public Exception ToException() => new VonageHttpRequestException(this.Message) { - this.Code = code; - this.Message = message; - this.Json = json; - } + HttpStatusCode = this.Code, + Json = this.Json, + }; + + /// + public Result ToResult() => Result.FromFailure(this); /// /// Creates a HttpFailure. @@ -50,7 +35,8 @@ public HttpFailure(HttpStatusCode code, string message, string json) /// The message. /// The JSON content. /// The failure. - public static HttpFailure From(HttpStatusCode code, string message, string json) => new(code, message, json); + public static HttpFailure From(HttpStatusCode code, string message, string json) => + new HttpFailure(code, message, json); /// /// Creates a HttpFailure. @@ -58,19 +44,4 @@ public HttpFailure(HttpStatusCode code, string message, string json) /// The status code. /// The failure. public static HttpFailure From(HttpStatusCode code) => From(code, string.Empty, string.Empty); - - /// - public string GetFailureMessage() => string.IsNullOrWhiteSpace(this.Message) - ? $"{(int) this.Code}." - : $"{(int) this.Code} - {this.Message} - {this.Json}."; - - /// - public Exception ToException() => new VonageHttpRequestException(this.Message) - { - HttpStatusCode = this.Code, - Json = this.Json, - }; - - /// - public Result ToResult() => Result.FromFailure(this); } \ No newline at end of file diff --git a/Vonage/Common/VideoApiError.cs b/Vonage/Common/VideoApiError.cs new file mode 100644 index 000000000..1ad6ded15 --- /dev/null +++ b/Vonage/Common/VideoApiError.cs @@ -0,0 +1,26 @@ +#region +using System.Net; +using Vonage.Common.Failures; +#endregion + +namespace Vonage.Common; + +internal interface IApiError +{ + HttpFailure ToFailure(); +} + +internal record VideoApiError(HttpStatusCode Code, string Message) : IApiError +{ + public HttpFailure ToFailure() => HttpFailure.From(this.Code, this.Message, null); +} + +internal record StandardApiError(string Type, string Title, string Detail, string Instance) : IApiError +{ + public HttpFailure ToFailure() => HttpFailure.From(HttpStatusCode.Accepted, this.Title, null); +} + +internal record NetworkApiError(int Status, string Code, string Message) : IApiError +{ + public HttpFailure ToFailure() => HttpFailure.From(HttpStatusCode.Accepted, this.Message, null); +} \ No newline at end of file diff --git a/Vonage/Conversations/ConversationsClient.cs b/Vonage/Conversations/ConversationsClient.cs index 28d8aacee..2ef5b6eda 100644 --- a/Vonage/Conversations/ConversationsClient.cs +++ b/Vonage/Conversations/ConversationsClient.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Conversations.CreateConversation; @@ -16,19 +18,21 @@ using Vonage.Conversations.UpdateConversation; using Vonage.Conversations.UpdateMember; using Vonage.Serialization; +#endregion namespace Vonage.Conversations; internal class ConversationsClient : IConversationsClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal ConversationsClient(VonageHttpClientConfiguration configuration) => - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); /// public Task> CreateConversationAsync(Result request) => diff --git a/Vonage/Meetings/MeetingsClient.cs b/Vonage/Meetings/MeetingsClient.cs index 68aff2f84..b538023a6 100644 --- a/Vonage/Meetings/MeetingsClient.cs +++ b/Vonage/Meetings/MeetingsClient.cs @@ -1,5 +1,7 @@ -using System.IO.Abstractions; +#region +using System.IO.Abstractions; using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Meetings.Common; @@ -20,6 +22,7 @@ using Vonage.Meetings.UpdateTheme; using Vonage.Meetings.UpdateThemeLogo; using Vonage.Serialization; +#endregion namespace Vonage.Meetings; @@ -27,7 +30,7 @@ namespace Vonage.Meetings; public class MeetingsClient : IMeetingsClient { private readonly UpdateThemeLogoUseCase updateThemeLogoUseCase; - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. @@ -37,7 +40,7 @@ public class MeetingsClient : IMeetingsClient public MeetingsClient(VonageHttpClientConfiguration configuration, IFileSystem fileSystem) { this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); this.updateThemeLogoUseCase = new UpdateThemeLogoUseCase(this.vonageClient, fileSystem.File.Exists, fileSystem.File.ReadAllBytes); } diff --git a/Vonage/Meetings/UpdateThemeLogo/UpdateThemeLogoUseCase.cs b/Vonage/Meetings/UpdateThemeLogo/UpdateThemeLogoUseCase.cs index 1afd2a436..a7c18a833 100644 --- a/Vonage/Meetings/UpdateThemeLogo/UpdateThemeLogoUseCase.cs +++ b/Vonage/Meetings/UpdateThemeLogo/UpdateThemeLogoUseCase.cs @@ -1,10 +1,13 @@ -using System; +#region +using System; using System.Linq; using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Failures; using Vonage.Common.Monads; using Vonage.Meetings.Common; +#endregion namespace Vonage.Meetings.UpdateThemeLogo; @@ -14,12 +17,12 @@ internal class UpdateThemeLogoUseCase /// Indicates no matching logo was found. /// public const string NoMatchingLogo = "No logo matches the requested type."; - + private readonly Func fileExistOperation; private readonly Func readFileOperation; - private readonly VonageHttpClient vonageHttpClient; - - internal UpdateThemeLogoUseCase(VonageHttpClient client, + private readonly VonageHttpClient vonageHttpClient; + + internal UpdateThemeLogoUseCase(VonageHttpClient client, Func fileExistOperation, Func readFileOperation) { @@ -27,50 +30,50 @@ internal UpdateThemeLogoUseCase(VonageHttpClient client, this.readFileOperation = readFileOperation; this.fileExistOperation = fileExistOperation; } - + private static Result FilterLogosOnType(GetUploadLogosUrlResponse[] url, ThemeLogoType logoType) => url.Any(VerifyMatchingLogoType(logoType)) ? url.Where(VerifyMatchingLogoType(logoType)).ToArray()[0] : ResultFailure.FromErrorMessage(NoMatchingLogo).ToResult(); - + private Task> FinalizeLogoAsync(UploadData request) => this.vonageHttpClient.SendAsync(new FinalizeLogoRequest(request.ThemeData.ThemeId, request.Request.Fields.Key)); - + private async Task> GetLogoUrlAsync(UpdateThemeData themeData) => await this.vonageHttpClient .SendWithResponseAsync(GetUploadLogosUrlRequest .Default) .Bind(responses => FilterLogosOnType(responses, themeData.Type)) .Map(response => new LogoUrlData(response, themeData)).ConfigureAwait(false); - + private Result LoadFile(UpdateThemeLogoRequest request) => this.fileExistOperation(request.FilePath) ? new UpdateThemeData(this.readFileOperation(request.FilePath), request.ThemeId, request.Type) : ResultFailure.FromErrorMessage("The file cannot be found.").ToResult(); - + private static UploadData ToUploadLogoRequest(LogoUrlData logoUrl) => new(UploadLogoRequest.FromLogosUrl(logoUrl.Response, logoUrl.ThemeData.File), logoUrl.ThemeData); - + private async Task> UploadLogoAsync(UploadData uploadLogoRequest) => (await this.vonageHttpClient.SendWithoutHeadersAsync(uploadLogoRequest.Request) .ConfigureAwait(false)) .Match(_ => uploadLogoRequest, Result.FromFailure); - + private static Func VerifyMatchingLogoType(ThemeLogoType logoType) => response => response.MatchesLogoType(logoType); - + internal Task> UpdateThemeLogoAsync(Result request) => request.Bind(this.LoadFile) .BindAsync(this.GetLogoUrlAsync) .Map(ToUploadLogoRequest) .BindAsync(this.UploadLogoAsync) .BindAsync(this.FinalizeLogoAsync); - + private record UpdateThemeData(byte[] File, Guid ThemeId, ThemeLogoType Type); - + private record LogoUrlData(GetUploadLogosUrlResponse Response, UpdateThemeData ThemeData); - + private record UploadData(UploadLogoRequest Request, UpdateThemeData ThemeData); } \ No newline at end of file diff --git a/Vonage/NumberInsightV2/NumberInsightV2Client.cs b/Vonage/NumberInsightV2/NumberInsightV2Client.cs index 55a564050..42ea8f5f3 100644 --- a/Vonage/NumberInsightV2/NumberInsightV2Client.cs +++ b/Vonage/NumberInsightV2/NumberInsightV2Client.cs @@ -1,17 +1,20 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.NumberInsightV2.FraudCheck; using Vonage.Serialization; +#endregion namespace Vonage.NumberInsightV2; internal class NumberInsightV2Client : INumberInsightV2Client { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; public NumberInsightV2Client(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); public Task> PerformFraudCheckAsync(Result request) => this.vonageClient.SendWithResponseAsync(request); diff --git a/Vonage/NumberVerification/NumberVerificationClient.cs b/Vonage/NumberVerification/NumberVerificationClient.cs index af808e54f..612ceccf5 100644 --- a/Vonage/NumberVerification/NumberVerificationClient.cs +++ b/Vonage/NumberVerification/NumberVerificationClient.cs @@ -1,24 +1,29 @@ -using System.Net.Http.Headers; +#region +using System.Net.Http.Headers; using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.NumberVerification.Authenticate; using Vonage.NumberVerification.Verify; using Vonage.Serialization; +#endregion namespace Vonage.NumberVerification; internal class NumberVerificationClient : INumberVerificationClient { - private readonly VonageHttpClient authorizationClient; - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient authorizationClient; + private readonly VonageHttpClient vonageClient; internal NumberVerificationClient(VonageHttpClientConfiguration configuration, VonageHttpClientConfiguration authorizationConfiguration) { - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); this.authorizationClient = - new VonageHttpClient(authorizationConfiguration, JsonSerializerBuilder.BuildWithSnakeCase()); + new VonageHttpClient(authorizationConfiguration, + JsonSerializerBuilder.BuildWithSnakeCase()); } /// @@ -39,8 +44,8 @@ await request .BindAsync(client => client.SendWithResponseAsync(request)) .Map(response => response.Verified); - private VonageHttpClient BuildClientWithAuthenticationHeader(AuthenticationHeaderValue header) => - this.vonageClient.WithDifferentHeader(header); + private VonageHttpClient BuildClientWithAuthenticationHeader(AuthenticationHeaderValue header) => + this.vonageClient.WithDifferentHeader(header); private static Result BuildAuthenticationRequest(VerifyRequest request) => request.BuildAuthenticationRequest(); diff --git a/Vonage/ProactiveConnect/ProactiveConnectClient.cs b/Vonage/ProactiveConnect/ProactiveConnectClient.cs index b371ae527..5612a5bcc 100644 --- a/Vonage/ProactiveConnect/ProactiveConnectClient.cs +++ b/Vonage/ProactiveConnect/ProactiveConnectClient.cs @@ -1,4 +1,6 @@ +#region using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.ProactiveConnect.Events.GetEvents; @@ -19,19 +21,21 @@ using Vonage.ProactiveConnect.Lists.ReplaceItems; using Vonage.ProactiveConnect.Lists.UpdateList; using Vonage.Serialization; +#endregion namespace Vonage.ProactiveConnect; internal class ProactiveConnectClient : IProactiveConnectClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal ProactiveConnectClient(VonageHttpClientConfiguration configuration) => - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); /// public Task> ClearListAsync(Result request) => diff --git a/Vonage/SimSwap/SimSwapClient.cs b/Vonage/SimSwap/SimSwapClient.cs index c9d939d93..5c226eb23 100644 --- a/Vonage/SimSwap/SimSwapClient.cs +++ b/Vonage/SimSwap/SimSwapClient.cs @@ -1,22 +1,26 @@ -using System; +#region +using System; using System.Net.Http.Headers; using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; using Vonage.SimSwap.Authenticate; using Vonage.SimSwap.Check; using Vonage.SimSwap.GetSwapDate; +#endregion namespace Vonage.SimSwap; internal class SimSwapClient : ISimSwapClient { - private readonly VonageHttpClient vonageClient; - + private readonly VonageHttpClient vonageClient; + internal SimSwapClient(VonageHttpClientConfiguration configuration) => - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); - + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + /// public Task> AuthenticateAsync(Result request) => request.Map(BuildAuthorizeRequest) @@ -24,7 +28,7 @@ public Task> AuthenticateAsync(Result public async Task> CheckAsync(Result request) => await request @@ -34,7 +38,7 @@ await request .Map(this.BuildClientWithAuthenticationHeader) .BindAsync(client => client.SendWithResponseAsync(request)) .Map(response => response.Swapped); - + /// public async Task> GetSwapDateAsync(Result request) => await request @@ -44,30 +48,30 @@ await request .Map(this.BuildClientWithAuthenticationHeader) .BindAsync(client => client.SendWithResponseAsync(request)) .Map(response => response.LatestSimChange); - + private static Result BuildAuthenticationRequest(CheckRequest request) => request.BuildAuthenticationRequest(); - + private static Result BuildAuthenticationRequest(GetSwapDateRequest request) => request.BuildAuthenticationRequest(); - - private VonageHttpClient BuildClientWithAuthenticationHeader(AuthenticationHeaderValue header) => - this.vonageClient.WithDifferentHeader(header); - + + private VonageHttpClient BuildClientWithAuthenticationHeader(AuthenticationHeaderValue header) => + this.vonageClient.WithDifferentHeader(header); + private static AuthenticationHeaderValue BuildAuthenticationHeader(AuthenticateResponse authentication) => authentication.BuildAuthenticationHeader(); - + private static AuthenticateResponse BuildAuthenticateResponse(GetTokenResponse response) => new AuthenticateResponse(response.AccessToken); - + private Task> SendGetTokenRequest(GetTokenRequest request) => this.vonageClient.SendWithResponseAsync(request); - + private Task> SendAuthorizeRequest(AuthorizeRequest request) => this.vonageClient.SendWithResponseAsync(request); - + private static GetTokenRequest BuildGetTokenRequest(AuthorizeResponse request) => request.BuildGetTokenRequest(); - + private static AuthorizeRequest BuildAuthorizeRequest(AuthenticateRequest request) => request.BuildAuthorizeRequest(); } \ No newline at end of file diff --git a/Vonage/SubAccounts/SubAccountsClient.cs b/Vonage/SubAccounts/SubAccountsClient.cs index 9d71c8255..f7a0c6489 100644 --- a/Vonage/SubAccounts/SubAccountsClient.cs +++ b/Vonage/SubAccounts/SubAccountsClient.cs @@ -1,4 +1,5 @@ -using System; +#region +using System; using System.Threading.Tasks; using Vonage.Common; using Vonage.Common.Client; @@ -11,6 +12,7 @@ using Vonage.SubAccounts.TransferAmount; using Vonage.SubAccounts.TransferNumber; using Vonage.SubAccounts.UpdateSubAccount; +#endregion namespace Vonage.SubAccounts; @@ -18,17 +20,17 @@ namespace Vonage.SubAccounts; public class SubAccountsClient : ISubAccountsClient { private readonly string apiKey; - + private readonly TransferMapping balanceTransferMapping = new(GetTransfersRequest.BalanceTransfer, TransferAmountRequest.BalanceTransfer, response => response.BalanceTransfers); - + private readonly TransferMapping creditTransferMapping = new(GetTransfersRequest.CreditTransfer, TransferAmountRequest.CreditTransfer, response => response.CreditTransfers); - - private readonly VonageHttpClient vonageClient; - + + private readonly VonageHttpClient vonageClient; + /// /// Creates a new client. /// @@ -36,58 +38,59 @@ public class SubAccountsClient : ISubAccountsClient /// The account Id. public SubAccountsClient(VonageHttpClientConfiguration configuration, string apiKey) { - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); this.apiKey = apiKey; } - + /// public Task> CreateSubAccountAsync(Result request) => request.Map(incompleteRequest => incompleteRequest.WithApiKey(this.apiKey)) .BindAsync(completeRequest => this.vonageClient.SendWithResponseAsync(completeRequest)); - + /// public Task> GetBalanceTransfersAsync(Result request) => this.MapTransfersAsync(request, this.balanceTransferMapping); - + /// public Task> GetCreditTransfersAsync(Result request) => this.MapTransfersAsync(request, this.creditTransferMapping); - + /// public Task> GetSubAccountAsync(Result request) => request.Map(incompleteRequest => incompleteRequest.WithApiKey(this.apiKey)) .BindAsync(completeRequest => this.vonageClient.SendWithResponseAsync(completeRequest)); - + /// public async Task> GetSubAccountsAsync() => await this.vonageClient .SendWithResponseAsync>( GetSubAccountsRequest.Build(this.apiKey)) .Map(value => value.Content).ConfigureAwait(false); - + /// public Task> TransferBalanceAsync(Result request) => this.MapTransfersAsync(request, this.balanceTransferMapping); - + /// public Task> TransferCreditAsync(Result request) => this.MapTransfersAsync(request, this.creditTransferMapping); - + /// public Task> TransferNumberAsync(Result request) => request.Map(incompleteRequest => incompleteRequest.WithApiKey(this.apiKey)) .BindAsync(completeRequest => this.vonageClient .SendWithResponseAsync(completeRequest)); - + /// public Task> UpdateSubAccountAsync(Result request) => request.Map(incompleteRequest => incompleteRequest.WithApiKey(this.apiKey)) .BindAsync(completeRequest => this.vonageClient.SendWithResponseAsync(completeRequest)); - + private Task> MapTransfersAsync(Result request, TransferMapping mapping) => request.Map(incompleteRequest => incompleteRequest.WithApiKey(this.apiKey)) .Map(incompleteRequest => incompleteRequest.WithEndpoint(mapping.GetEndpointKey)) @@ -95,13 +98,13 @@ private Task> MapTransfersAsync(Result r this.vonageClient.SendWithResponseAsync>( completeRequest)) .Map(value => mapping.GetMapping(value.Content)); - + private Task> MapTransfersAsync(Result request, TransferMapping mapping) => request.Map(incompleteRequest => incompleteRequest.WithApiKey(this.apiKey)) .Map(incompleteRequest => incompleteRequest.WithEndpoint(mapping.UpdateEndpointKey)) .BindAsync(completeRequest => this.vonageClient.SendWithResponseAsync(completeRequest)); - + private sealed record TransferMapping( string GetEndpointKey, string UpdateEndpointKey, diff --git a/Vonage/Users/UsersClient.cs b/Vonage/Users/UsersClient.cs index 75487fabc..8c7b4f555 100644 --- a/Vonage/Users/UsersClient.cs +++ b/Vonage/Users/UsersClient.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -7,19 +9,21 @@ using Vonage.Users.GetUser; using Vonage.Users.GetUsers; using Vonage.Users.UpdateUser; +#endregion namespace Vonage.Users; internal class UsersClient : IUsersClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal UsersClient(VonageHttpClientConfiguration configuration) => - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); /// public Task> CreateUserAsync(Result request) => diff --git a/Vonage/VerifyV2/VerifyV2Client.cs b/Vonage/VerifyV2/VerifyV2Client.cs index 3926a40ac..85d82994e 100644 --- a/Vonage/VerifyV2/VerifyV2Client.cs +++ b/Vonage/VerifyV2/VerifyV2Client.cs @@ -1,5 +1,6 @@ #region using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -23,14 +24,15 @@ namespace Vonage.VerifyV2; internal class VerifyV2Client : IVerifyV2Client { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal VerifyV2Client(VonageHttpClientConfiguration configuration) => - this.vonageClient = new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); + this.vonageClient = + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithSnakeCase()); /// public Task> CancelAsync(Result request) => diff --git a/Vonage/Video/Archives/ArchiveClient.cs b/Vonage/Video/Archives/ArchiveClient.cs index 6846728f8..ff8bb2402 100644 --- a/Vonage/Video/Archives/ArchiveClient.cs +++ b/Vonage/Video/Archives/ArchiveClient.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -10,6 +12,7 @@ using Vonage.Video.Archives.GetArchives; using Vonage.Video.Archives.RemoveStream; using Vonage.Video.Archives.StopArchive; +#endregion namespace Vonage.Video.Archives; @@ -18,10 +21,10 @@ namespace Vonage.Video.Archives; /// public class ArchiveClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; internal ArchiveClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Adds the stream included in a composed archive that was started with the streamMode set to "manual". diff --git a/Vonage/Video/AudioConnector/AudioConnectorClient.cs b/Vonage/Video/AudioConnector/AudioConnectorClient.cs index 288107df8..5f139b961 100644 --- a/Vonage/Video/AudioConnector/AudioConnectorClient.cs +++ b/Vonage/Video/AudioConnector/AudioConnectorClient.cs @@ -1,5 +1,6 @@ #region using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -13,10 +14,10 @@ namespace Vonage.Video.AudioConnector; /// public class AudioConnectorClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; internal AudioConnectorClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Sends audio from a Vonage Video API session to a WebSocket. diff --git a/Vonage/Video/Broadcast/BroadcastClient.cs b/Vonage/Video/Broadcast/BroadcastClient.cs index cf987f674..808cc4318 100644 --- a/Vonage/Video/Broadcast/BroadcastClient.cs +++ b/Vonage/Video/Broadcast/BroadcastClient.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -9,6 +11,7 @@ using Vonage.Video.Broadcast.RemoveStreamFromBroadcast; using Vonage.Video.Broadcast.StartBroadcast; using Vonage.Video.Broadcast.StopBroadcast; +#endregion namespace Vonage.Video.Broadcast; @@ -17,14 +20,14 @@ namespace Vonage.Video.Broadcast; /// public class BroadcastClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal BroadcastClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Adds a stream to a live streaming broadcast. diff --git a/Vonage/Video/ExperienceComposer/ExperienceComposerClient.cs b/Vonage/Video/ExperienceComposer/ExperienceComposerClient.cs index e9f713d74..8e42f03ea 100644 --- a/Vonage/Video/ExperienceComposer/ExperienceComposerClient.cs +++ b/Vonage/Video/ExperienceComposer/ExperienceComposerClient.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -6,6 +8,7 @@ using Vonage.Video.ExperienceComposer.GetSessions; using Vonage.Video.ExperienceComposer.Start; using Vonage.Video.ExperienceComposer.Stop; +#endregion namespace Vonage.Video.ExperienceComposer; @@ -14,10 +17,10 @@ namespace Vonage.Video.ExperienceComposer; /// public class ExperienceComposerClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; internal ExperienceComposerClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Retrieves details on an Experience Composer session. diff --git a/Vonage/Video/LiveCaptions/LiveCaptionsClient.cs b/Vonage/Video/LiveCaptions/LiveCaptionsClient.cs index b7b11a325..58180b2b4 100644 --- a/Vonage/Video/LiveCaptions/LiveCaptionsClient.cs +++ b/Vonage/Video/LiveCaptions/LiveCaptionsClient.cs @@ -1,5 +1,6 @@ #region using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -14,10 +15,10 @@ namespace Vonage.Video.LiveCaptions; /// public class LiveCaptionsClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; internal LiveCaptionsClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Stops live captions for a session diff --git a/Vonage/Video/Moderation/ModerationClient.cs b/Vonage/Video/Moderation/ModerationClient.cs index cb7d0a417..82d7594dd 100644 --- a/Vonage/Video/Moderation/ModerationClient.cs +++ b/Vonage/Video/Moderation/ModerationClient.cs @@ -1,10 +1,13 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; using Vonage.Video.Moderation.DisconnectConnection; using Vonage.Video.Moderation.MuteStream; using Vonage.Video.Moderation.MuteStreams; +#endregion namespace Vonage.Video.Moderation; @@ -13,14 +16,14 @@ namespace Vonage.Video.Moderation; /// public class ModerationClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal ModerationClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Forces a client to disconnect from a session diff --git a/Vonage/Video/Sessions/CreateSession/CreateSessionUseCase.cs b/Vonage/Video/Sessions/CreateSession/CreateSessionUseCase.cs index 0a72be201..3749997ef 100644 --- a/Vonage/Video/Sessions/CreateSession/CreateSessionUseCase.cs +++ b/Vonage/Video/Sessions/CreateSession/CreateSessionUseCase.cs @@ -1,23 +1,26 @@ -using System.Linq; +#region +using System.Linq; using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Failures; using Vonage.Common.Monads; +#endregion namespace Vonage.Video.Sessions.CreateSession; internal class CreateSessionUseCase { - private readonly VonageHttpClient vonageHttpClient; - - internal CreateSessionUseCase(VonageHttpClient client) => this.vonageHttpClient = client; - + private readonly VonageHttpClient vonageHttpClient; + + internal CreateSessionUseCase(VonageHttpClient client) => this.vonageHttpClient = client; + private static Result GetFirstSessionIfAvailable(CreateSessionResponse[] sessions) => sessions.Any() ? sessions[0] : Result.FromFailure( ResultFailure.FromErrorMessage(CreateSessionResponse.NoSessionCreated)); - + internal async Task> CreateSessionAsync(Result request) { var result = diff --git a/Vonage/Video/Sessions/SessionClient.cs b/Vonage/Video/Sessions/SessionClient.cs index 77f34c529..f5c446ae1 100644 --- a/Vonage/Video/Sessions/SessionClient.cs +++ b/Vonage/Video/Sessions/SessionClient.cs @@ -1,4 +1,6 @@ +#region using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; @@ -6,6 +8,7 @@ using Vonage.Video.Sessions.CreateSession; using Vonage.Video.Sessions.GetStream; using Vonage.Video.Sessions.GetStreams; +#endregion namespace Vonage.Video.Sessions; @@ -15,7 +18,7 @@ namespace Vonage.Video.Sessions; public class SessionClient { private readonly CreateSessionUseCase createSessionUseCase; - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. @@ -24,7 +27,7 @@ public class SessionClient internal SessionClient(VonageHttpClientConfiguration configuration) { this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); this.createSessionUseCase = new CreateSessionUseCase(this.vonageClient); } diff --git a/Vonage/Video/Signaling/SignalingClient.cs b/Vonage/Video/Signaling/SignalingClient.cs index e0eccc13e..4d2d0a43d 100644 --- a/Vonage/Video/Signaling/SignalingClient.cs +++ b/Vonage/Video/Signaling/SignalingClient.cs @@ -1,9 +1,12 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; using Vonage.Video.Signaling.SendSignal; using Vonage.Video.Signaling.SendSignals; +#endregion namespace Vonage.Video.Signaling; @@ -12,14 +15,14 @@ namespace Vonage.Video.Signaling; /// public class SignalingClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal SignalingClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Sends signals to a single participant in an active Vonage Video session. diff --git a/Vonage/Video/Sip/SipClient.cs b/Vonage/Video/Sip/SipClient.cs index 41b63739a..6b1b47d5a 100644 --- a/Vonage/Video/Sip/SipClient.cs +++ b/Vonage/Video/Sip/SipClient.cs @@ -1,10 +1,13 @@ -using System.Threading.Tasks; +#region +using System.Threading.Tasks; +using Vonage.Common; using Vonage.Common.Client; using Vonage.Common.Monads; using Vonage.Serialization; using Vonage.Video.Sip.InitiateCall; using Vonage.Video.Sip.PlayToneIntoCall; using Vonage.Video.Sip.PlayToneIntoConnection; +#endregion namespace Vonage.Video.Sip; @@ -13,14 +16,14 @@ namespace Vonage.Video.Sip; /// public class SipClient { - private readonly VonageHttpClient vonageClient; + private readonly VonageHttpClient vonageClient; /// /// Creates a new client. /// /// The client configuration. internal SipClient(VonageHttpClientConfiguration configuration) => this.vonageClient = - new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); + new VonageHttpClient(configuration, JsonSerializerBuilder.BuildWithCamelCase()); /// /// Connects your SIP platform to an OpenTok session.