Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create email verification confirmation #81

Merged
merged 4 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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 |
|:-:|:-:|:-:|
Expand Down Expand Up @@ -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) | ⬛ | ❌ |

Expand Down
17 changes: 17 additions & 0 deletions src/PinguApps.Appwrite.Client/Clients/AccountClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,21 @@ public async Task<AppwriteResult<Token>> CreateEmailVerification(CreateEmailVeri
return e.GetExceptionResponse<Token>();
}
}

/// <inheritdoc/>
public async Task<AppwriteResult<Token>> CreateEmailVerificationConfirmation(CreateEmailVerificationConfirmationRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.CreateEmailVerificationConfirmation(request);

return result.GetApiResponse();
}
catch (Exception e)
{
return e.GetExceptionResponse<Token>();
}
}
}
8 changes: 8 additions & 0 deletions src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,12 @@ public interface IAccountClient
/// <param name="request">The request content</param>
/// <returns>The token</returns>
Task<AppwriteResult<Token>> CreateEmailVerification(CreateEmailVerificationRequest request);

/// <summary>
/// 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.
/// <para><see href="https://appwrite.io/docs/references/1.5.x/client-rest/account#updateVerification">Appwrite Docs</see></para>
/// </summary>
/// <param name="request">The request content</param>
/// <returns>The token</returns>
Task<AppwriteResult<Token>> CreateEmailVerificationConfirmation(CreateEmailVerificationConfirmationRequest request);
}
5 changes: 4 additions & 1 deletion src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ internal interface IAccountApi : IBaseApi
[Patch("/account/sessions/{sessionId}")]
Task<IApiResponse<Session>> UpdateSession([Header("x-appwrite-session")] string? session, string sessionId);

[Post(("/account/verification"))]
[Post("/account/verification")]
Task<IApiResponse<Token>> CreateEmailVerification([Header("x-appwrite-session")] string? session, CreateEmailVerificationRequest request);

[Put("/account/verification")]
Task<IApiResponse<Token>> CreateEmailVerificationConfirmation(CreateEmailVerificationConfirmationRequest request);
}
8 changes: 8 additions & 0 deletions src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public async Task Run(string[] args)

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(),
appwriteError => appwriteError.Message,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;
public class CreateEmailVerificationConfirmationRequest : BaseRequest<CreateEmailVerificationConfirmationRequest, CreateEmailVerificationConfirmationRequestValidator>
{
/// <summary>
/// User ID.
/// </summary>
[JsonPropertyName("userId")]
public string UserId { get; set; } = string.Empty;

/// <summary>
/// Valid verification token.
/// </summary>
[JsonPropertyName("secret")]
public string Secret { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class CreateEmailVerificationConfirmationRequestValidator : AbstractValidator<CreateEmailVerificationConfirmationRequest>
{
public CreateEmailVerificationConfirmationRequestValidator()
{
RuleFor(x => x.UserId).NotEmpty().Matches("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$");
RuleFor(x => x.Secret).NotEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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 = "[email protected]";

// 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<ValidationException>(() => 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);
}
}