From 9b57f08a9a8f4d5c8b04465a3db93e8bc34271f9 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 4 Aug 2024 16:41:33 +0200 Subject: [PATCH 1/4] implemented create email verification confirmation --- .../Clients/AccountClient.cs | 17 +++++++++++++++++ .../Clients/IAccountClient.cs | 8 ++++++++ .../Internals/IAccountApi.cs | 5 ++++- src/PinguApps.Appwrite.Playground/App.cs | 14 +++++++++++--- ...eateEmailVerificationConfirmationRequest.cs | 18 ++++++++++++++++++ ...VerificationConfirmationRequestValidator.cs | 11 +++++++++++ 6 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/CreateEmailVerificationConfirmationRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Validators/CreateEmailVerificationConfirmationRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index 88e6bfe0..26b5dac7 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -246,4 +246,21 @@ public async Task> CreateEmailVerification(CreateEmailVeri return e.GetExceptionResponse(); } } + + /// + public async Task> CreateEmailVerificationConfirmation(CreateEmailVerificationConfirmationRequest request) + { + try + { + request.Validate(true); + + var result = await _accountApi.CreateEmailVerificationConfirmation(Session, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } } diff --git a/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs index 3d53e61b..bbac581b 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs @@ -117,4 +117,12 @@ public interface IAccountClient /// The request content /// The token Task> CreateEmailVerification(CreateEmailVerificationRequest request); + + /// + /// Use this endpoint to complete the user email verification process. Use both the userId and secret parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code. + /// Appwrite Docs + /// + /// The request content + /// The token + Task> CreateEmailVerificationConfirmation(CreateEmailVerificationConfirmationRequest request); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs index 2985dd38..90e2f1d7 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs @@ -44,6 +44,9 @@ internal interface IAccountApi : IBaseApi [Patch("/account/sessions/{sessionId}")] Task> UpdateSession([Header("x-appwrite-session")] string? session, string sessionId); - [Post(("/account/verification"))] + [Post("/account/verification")] Task> CreateEmailVerification([Header("x-appwrite-session")] string? session, CreateEmailVerificationRequest request); + + [Put("/account/verification")] + Task> CreateEmailVerificationConfirmation([Header("x-appwrite-session")] string? session, CreateEmailVerificationConfirmationRequest request); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index f0d5173c..fc984800 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -21,12 +21,20 @@ public async Task Run(string[] args) { _client.SetSession(_session); - var request = new CreateEmailVerificationRequest + //var request = new CreateEmailVerificationRequest + //{ + // Url = "https://localhost:5001/abc123" + //}; + + //var response = await _client.Account.CreateEmailVerification(request); + + var request = new CreateEmailVerificationConfirmationRequest { - Url = "https://localhost:5001/abc123" + Secret = "secret", + UserId = "userid" }; - var response = await _client.Account.CreateEmailVerification(request); + var response = await _client.Account.CreateEmailVerificationConfirmation(request); Console.WriteLine(response.Result.Match( account => account.ToString(), diff --git a/src/PinguApps.Appwrite.Shared/Requests/CreateEmailVerificationConfirmationRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/CreateEmailVerificationConfirmationRequest.cs new file mode 100644 index 00000000..62c21f56 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/CreateEmailVerificationConfirmationRequest.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests; +public class CreateEmailVerificationConfirmationRequest : BaseRequest +{ + /// + /// User ID. + /// + [JsonPropertyName("userId")] + public string UserId { get; set; } = string.Empty; + + /// + /// Valid verification token. + /// + [JsonPropertyName("secret")] + public string Secret { get; set; } = string.Empty; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Validators/CreateEmailVerificationConfirmationRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Validators/CreateEmailVerificationConfirmationRequestValidator.cs new file mode 100644 index 00000000..ba49448b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Validators/CreateEmailVerificationConfirmationRequestValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Validators; +public class CreateEmailVerificationConfirmationRequestValidator : AbstractValidator +{ + public CreateEmailVerificationConfirmationRequestValidator() + { + RuleFor(x => x.UserId).NotEmpty().Matches("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$"); + RuleFor(x => x.Secret).NotEmpty(); + } +} From f7fb1980b13b5471c7b16468903dc4623af4b8d0 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 4 Aug 2024 16:45:37 +0200 Subject: [PATCH 2/4] added shared tests --- ...ailVerificationRequestConfirmationTests.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/CreateEmailVerificationRequestConfirmationTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/CreateEmailVerificationRequestConfirmationTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/CreateEmailVerificationRequestConfirmationTests.cs new file mode 100644 index 00000000..3781ee7a --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/CreateEmailVerificationRequestConfirmationTests.cs @@ -0,0 +1,107 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests; + +namespace PinguApps.Appwrite.Shared.Tests.Requests; +public class CreateEmailVerificationRequestConfirmationTests +{ + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateEmailVerificationConfirmationRequest(); + + // Assert + Assert.Equal(string.Empty, request.UserId); + Assert.Equal(string.Empty, request.Secret); + } + + [Fact] + public void Properties_CanBeSet() + { + var userId = "123456"; + var secret = "test@example.com"; + + // Arrange + var request = new CreateEmailVerificationConfirmationRequest(); + + // Act + request.UserId = userId; + request.Secret = secret; + + // Assert + Assert.Equal(userId, request.UserId); + Assert.Equal(secret, request.Secret); + } + + [Fact] + public void IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest + { + UserId = "123456", + Secret = "654321" + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData("badChar^", "654321")] + [InlineData(".bad", "654321")] + [InlineData("_bad", "654321")] + [InlineData("-bad", "654321")] + [InlineData("", "654321")] + [InlineData("1234567890123456789012345678901234567", "654321")] + [InlineData("123456", "")] + public void IsValid_WithInvalidData_ReturnsFalse(string userId, string secret) + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest + { + UserId = userId, + Secret = secret + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest + { + UserId = ".badChar^", + Secret = "" + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest + { + UserId = ".badChar^", + Secret = "" + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 9f60a02e35299c1209e18f2dceadd35cf1665f28 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 4 Aug 2024 17:03:24 +0200 Subject: [PATCH 3/4] Removed need for session and added client tests --- .../Clients/AccountClient.cs | 2 +- .../Internals/IAccountApi.cs | 2 +- src/PinguApps.Appwrite.Playground/App.cs | 22 +++--- ...sts.CreateEmailVerificationConfirmation.cs | 77 +++++++++++++++++++ 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.CreateEmailVerificationConfirmation.cs diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index 26b5dac7..15f4cb27 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -254,7 +254,7 @@ public async Task> CreateEmailVerificationConfirmation(Cre { request.Validate(true); - var result = await _accountApi.CreateEmailVerificationConfirmation(Session, request); + var result = await _accountApi.CreateEmailVerificationConfirmation(request); return result.GetApiResponse(); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs index 90e2f1d7..d93e4851 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs @@ -48,5 +48,5 @@ internal interface IAccountApi : IBaseApi Task> CreateEmailVerification([Header("x-appwrite-session")] string? session, CreateEmailVerificationRequest request); [Put("/account/verification")] - Task> CreateEmailVerificationConfirmation([Header("x-appwrite-session")] string? session, CreateEmailVerificationConfirmationRequest request); + Task> CreateEmailVerificationConfirmation(CreateEmailVerificationConfirmationRequest request); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index fc984800..f1983854 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -21,20 +21,20 @@ public async Task Run(string[] args) { _client.SetSession(_session); - //var request = new CreateEmailVerificationRequest - //{ - // Url = "https://localhost:5001/abc123" - //}; - - //var response = await _client.Account.CreateEmailVerification(request); - - var request = new CreateEmailVerificationConfirmationRequest + var request = new CreateEmailVerificationRequest { - Secret = "secret", - UserId = "userid" + Url = "https://localhost:5001/abc123" }; - var response = await _client.Account.CreateEmailVerificationConfirmation(request); + var response = await _client.Account.CreateEmailVerification(request); + + //var request = new CreateEmailVerificationConfirmationRequest + //{ + // Secret = "secret", + // UserId = "userId" + //}; + + //var response = await _client.Account.CreateEmailVerificationConfirmation(request); Console.WriteLine(response.Result.Match( account => account.ToString(), diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.CreateEmailVerificationConfirmation.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.CreateEmailVerificationConfirmation.cs new file mode 100644 index 00000000..4acef7d7 --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.CreateEmailVerificationConfirmation.cs @@ -0,0 +1,77 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests; +using PinguApps.Appwrite.Shared.Tests; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Account; +public partial class AccountClientTests +{ + [Fact] + public async Task CreateEmailVerificationConfirmation_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest() + { + UserId = "123456", + Secret = "654321" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{Constants.Endpoint}/account/verification") + .ExpectedHeaders() + .WithJsonContent(request) + .Respond(Constants.AppJson, Constants.TokenResponse); + + // Act + var result = await _appwriteClient.Account.CreateEmailVerificationConfirmation(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateEmailVerificationConfirmation_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest() + { + UserId = "123456", + Secret = "654321" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{Constants.Endpoint}/account/verification") + .ExpectedHeaders() + .WithJsonContent(request) + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + // Act + var result = await _appwriteClient.Account.CreateEmailVerificationConfirmation(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateEmailVerificationConfirmation_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateEmailVerificationConfirmationRequest() + { + UserId = "123456", + Secret = "654321" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{Constants.Endpoint}/account/verification") + .ExpectedHeaders() + .WithJsonContent(request) + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Account.CreateEmailVerificationConfirmation(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 4ce93634d4c1f6e5a44927943528c43f91eaeb1c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 4 Aug 2024 17:04:24 +0200 Subject: [PATCH 4/4] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 19aa45a7..67e7cb3b 100644 --- a/README.md +++ b/README.md @@ -139,11 +139,11 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress ### Server & Client -![15 / 288](https://progress-bar.dev/15/?scale=288&suffix=%20/%20288&width=500) +![16 / 288](https://progress-bar.dev/16/?scale=288&suffix=%20/%20288&width=500) ### Server Only ![2 / 195](https://progress-bar.dev/2/?scale=195&suffix=%20/%20195&width=300) ### Client Only -![13 / 93](https://progress-bar.dev/13/?scale=93&suffix=%20/%2093&width=300) +![14 / 93](https://progress-bar.dev/14/?scale=93&suffix=%20/%2093&width=300) ### 🔑 Key | Icon | Definition | @@ -153,7 +153,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | ❌ | There is currently no intention to implement the endpoint for the given SDK type (client or server) | ### Account -![15 / 52](https://progress-bar.dev/15/?scale=52&suffix=%20/%2052&width=120) +![16 / 52](https://progress-bar.dev/16/?scale=52&suffix=%20/%2052&width=120) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -201,7 +201,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Create OAuth2 Token](https://appwrite.io/docs/references/1.5.x/client-rest/account#createOAuth2Token) | ⬛ | ⬛ | | [Create Phone Token](https://appwrite.io/docs/references/1.5.x/client-rest/account#createPhoneToken) | ⬛ | ⬛ | | [Create Email Verification](https://appwrite.io/docs/references/1.5.x/client-rest/account#createVerification) | ✅ | ❌ | -| [Create Email Verification (Confirmation)](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateVerification) | ⬛ | ❌ | +| [Create Email Verification (Confirmation)](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateVerification) | ✅ | ❌ | | [Create Phone Verification](https://appwrite.io/docs/references/1.5.x/client-rest/account#createPhoneVerification) | ⬛ | ❌ | | [Create Phone Verification (Confirmation)](https://appwrite.io/docs/references/1.5.x/client-rest/account#updatePhoneVerification) | ⬛ | ❌ |