diff --git a/README.md b/README.md index 1d215cb0..6c9b3704 100644 --- a/README.md +++ b/README.md @@ -138,14 +138,14 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ``` ## βŒ› Progress - -![Server & Client - 27 / 288](https://img.shields.io/badge/Server_&_Client-27%20%2F%20288-red?style=for-the-badge) + +![Server & Client - 28 / 288](https://img.shields.io/badge/Server_&_Client-28%20%2F%20288-red?style=for-the-badge) ![Server - 2 / 195](https://img.shields.io/badge/Server-2%20%2F%20195-red?style=for-the-badge) - -![Client - 25 / 93](https://img.shields.io/badge/Client-25%20%2F%2093-red?style=for-the-badge) + +![Client - 26 / 93](https://img.shields.io/badge/Client-26%20%2F%2093-red?style=for-the-badge) ### πŸ”‘ Key | Icon | Definition | @@ -155,8 +155,8 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | ❌ | There is currently no intention to implement the endpoint for the given SDK type (client or server) | ### Account - -![Account - 27 / 52](https://img.shields.io/badge/Account-27%20%2F%2052-yellow?style=for-the-badge) + +![Account - 28 / 52](https://img.shields.io/badge/Account-28%20%2F%2052-yellow?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -176,7 +176,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [List Factors](https://appwrite.io/docs/references/1.5.x/client-rest/account#listMfaFactors) | βœ… | ❌ | | [Get MFA Recovery Codes](https://appwrite.io/docs/references/1.5.x/client-rest/account#getMfaRecoveryCodes) | βœ… | ❌ | | [Create MFA Recovery Codes](https://appwrite.io/docs/references/1.5.x/client-rest/account#createMfaRecoveryCodes) | βœ… | ❌ | -| [Regenerate MFA Recovery Codes](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateMfaRecoveryCodes) | ⬛ | ❌ | +| [Regenerate MFA Recovery Codes](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateMfaRecoveryCodes) | βœ… | ❌ | | [Update Name](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateName) | βœ… | ❌ | | [Update Password](https://appwrite.io/docs/references/1.5.x/client-rest/account#updatePassword) | βœ… | ❌ | | [Update Phone](https://appwrite.io/docs/references/1.5.x/client-rest/account#updatePhone) | βœ… | ❌ | diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index 9566a822..d306e2ee 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -450,4 +450,19 @@ public async Task> GetMfaRecoveryCodes() return e.GetExceptionResponse(); } } + + /// + public async Task> RegenerateMfaRecoveryCodes() + { + try + { + var result = await _accountApi.RegenerateMfaRecoveryCodes(GetCurrentSessionOrThrow()); + + 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 5df325e2..c4925d50 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs @@ -211,4 +211,11 @@ public interface IAccountClient /// /// The Mfa Recovery Codes Task> GetMfaRecoveryCodes(); + + /// + /// Regenerate recovery codes that can be used as backup for MFA flow. Before regenerating codes, they must be first generated using method. An OTP challenge is required to regenreate recovery codes + /// Appwrite Docs + /// + /// + Task> RegenerateMfaRecoveryCodes(); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs index bdec188e..1e375894 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs @@ -83,4 +83,7 @@ internal interface IAccountApi : IBaseApi [Get("/account/mfa/recovery-codes")] Task> GetMfaRecoveryCodes([Header("x-appwrite-session")] string session); + + [Patch("/account/mfa/recovery-codes")] + Task> RegenerateMfaRecoveryCodes([Header("x-appwrite-session")] string session); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 2172d0ce..e43b44e8 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -20,7 +20,7 @@ public async Task Run(string[] args) { _client.SetSession(_session); - var response = await _client.Account.GetMfaRecoveryCodes(); + var response = await _client.Account.RegenerateMfaRecoveryCodes(); Console.WriteLine(response.Result.Match( account => account.ToString(), diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.RegenerateMfaRecoveryCodes.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.RegenerateMfaRecoveryCodes.cs new file mode 100644 index 00000000..72407f5a --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.RegenerateMfaRecoveryCodes.cs @@ -0,0 +1,74 @@ +ο»Ώusing System.Net; +using PinguApps.Appwrite.Client.Clients; +using PinguApps.Appwrite.Shared.Tests; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Account; +public partial class AccountClientTests +{ + [Fact] + public async Task RegenerateMfaRecoveryCodes_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/mfa/recovery-codes") + .ExpectedHeaders(true) + .Respond(Constants.AppJson, Constants.JwtResponse); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.RegenerateMfaRecoveryCodes(); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task RegenerateMfaRecoveryCodes_ShouldReturnError_WhenSessionIsNull() + { + // Act + var result = await _appwriteClient.Account.RegenerateMfaRecoveryCodes(); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsInternalError); + Assert.Equal(ISessionAware.SessionExceptionMessage, result.Result.AsT2.Message); + } + + [Fact] + public async Task RegenerateMfaRecoveryCodes_ShouldHandleException_WhenApiCallFails() + { + // Arrange + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/mfa/recovery-codes") + .ExpectedHeaders(true) + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.RegenerateMfaRecoveryCodes(); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task RegenerateMfaRecoveryCodes_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/mfa/recovery-codes") + .ExpectedHeaders(true) + .Throw(new HttpRequestException("An error occurred")); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.RegenerateMfaRecoveryCodes(); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +}