From f8f5be9efe676a808475288ffa9e3d69e0c20bf1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 10 Jul 2024 04:04:00 +0100 Subject: [PATCH 1/4] Implemented Update Prefs --- .../Clients/AccountClient.cs | 17 +++++++++++++++++ .../Clients/IAccountClient.cs | 8 ++++++++ .../Internals/IAccountApi.cs | 9 ++++++--- src/PinguApps.Appwrite.Playground/App.cs | 9 ++++++++- .../Requests/UpdatePreferencesRequest.cs | 17 +++++++++++++++++ .../UpdatePreferencesRequestValidator.cs | 9 +++++++++ 6 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/UpdatePreferencesRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index 84e9a266..05ff9053 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -147,4 +147,21 @@ public async Task>> GetAccoun return e.GetExceptionResponse>(); } } + + /// + public async Task> UpdatePreferences(UpdatePreferencesRequest request) + { + try + { + request.Validate(true); + + var result = await _accountApi.UpdatePreferences(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 ec81f8d3..4f6d3147 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IAccountClient.cs @@ -67,4 +67,12 @@ public interface IAccountClient /// /// A dictionary of the user preferences Task>> GetAccountPreferences(); + + /// + /// Update currently logged in user account preferences. The object you pass is stored as is, and replaces any previous value. The maximum allowed prefs size is 64kB and throws error if exceeded + /// Appwrite Docs + /// + /// The request content + /// The user + Task> UpdatePreferences(UpdatePreferencesRequest request); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs index 39fd4cd9..8b4b12fd 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs @@ -18,14 +18,17 @@ public interface IAccountApi : IBaseApi Task> UpdateEmail([Header("x-appwrite-session")] string? session, UpdateEmailRequest request); [Patch("/account/name")] - Task> UpdateName([Header("x-appwrite-session")] string? session, UpdateNameRequest name); + Task> UpdateName([Header("x-appwrite-session")] string? session, UpdateNameRequest request); [Patch("/account/password")] - Task> UpdatePassword([Header("x-appwrite-session")] string? session, UpdatePasswordRequest name); + Task> UpdatePassword([Header("x-appwrite-session")] string? session, UpdatePasswordRequest request); [Patch("/account/phone")] - Task> UpdatePhone([Header("x-appwrite-session")] string? session, UpdatePhoneRequest name); + Task> UpdatePhone([Header("x-appwrite-session")] string? session, UpdatePhoneRequest request); [Get("/account/prefs")] Task>> GetAccountPreferences([Header("x-appwrite-session")] string? session); + + [Patch("/account/prefs")] + Task> UpdatePreferences([Header("x-appwrite-session")] string? session, UpdatePreferencesRequest request); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index ee0955a1..77f17c6e 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -29,7 +29,14 @@ public async Task Run(string[] args) var f = request.IsValid(); - var result = await _client.Account.GetAccountPreferences(); + var result = await _client.Account.UpdatePreferences(new UpdatePreferencesRequest + { + Preferences = new Dictionary + { + { "key1", "val1" }, + { "key2", "val2" } + } + }); result.Result.Switch( account => Console.WriteLine(string.Join(',', account)), diff --git a/src/PinguApps.Appwrite.Shared/Requests/UpdatePreferencesRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/UpdatePreferencesRequest.cs new file mode 100644 index 00000000..73f11915 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/UpdatePreferencesRequest.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests; + +/// +/// The request for updating a users preferences +/// +public class UpdatePreferencesRequest : BaseRequest +{ + /// + /// New value for preferences + /// + [JsonPropertyName("prefs")] + public IDictionary Preferences { get; set; } = new Dictionary(); +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs new file mode 100644 index 00000000..082a7cfb --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs @@ -0,0 +1,9 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Validators; +public class UpdatePreferencesRequestValidator : AbstractValidator +{ + public UpdatePreferencesRequestValidator() + { + } +} From f6311606f1a90ce1866a6c1509f388161eb323ac Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 10 Jul 2024 04:13:30 +0100 Subject: [PATCH 2/4] Added shared tests --- .../UpdatePreferencesRequestValidator.cs | 1 + .../Requests/UpdatePreferencesRequestTests.cs | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdatePreferencesRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs index 082a7cfb..fb0e8bdb 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Validators/UpdatePreferencesRequestValidator.cs @@ -5,5 +5,6 @@ public class UpdatePreferencesRequestValidator : AbstractValidator x.Preferences).NotNull(); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdatePreferencesRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdatePreferencesRequestTests.cs new file mode 100644 index 00000000..931ce327 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/UpdatePreferencesRequestTests.cs @@ -0,0 +1,98 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests; + +namespace PinguApps.Appwrite.Shared.Tests.Requests; +public class UpdatePreferencesRequestTests +{ + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdatePreferencesRequest(); + + // Assert + Assert.NotNull(request.Preferences); + Assert.Empty(request.Preferences); + } + + [Theory] + [MemberData(nameof(PropertiesToBeSet))] + public void Properties_CanBeSet(IDictionary dict) + { + // Arrange + var request = new UpdatePreferencesRequest(); + + // Act + request.Preferences = dict; + + // Assert + Assert.Equal(dict, request.Preferences); + } + + public static IEnumerable PropertiesToBeSet() + { + yield return new object[] { new Dictionary { { "key1", "val1" }, { "key2", "val2" } } }; + yield return new object[] { new Dictionary() }; + } + + [Fact] + public void IsValid_WithValidPhoneAndPassword_ReturnsTrue() + { + // Arrange + var request = new UpdatePreferencesRequest + { + Preferences = new Dictionary { { "key1", "val1" }, { "key2", "val2" } } + }; + + // Act + bool isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void IsValid_WithInvalidInputs_ReturnsFalse() + { + // Arrange + var request = new UpdatePreferencesRequest + { + Preferences = null! + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdatePreferencesRequest + { + Preferences = null! + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdatePreferencesRequest + { + Preferences = null! + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From a1783cf6710eb01e69968c8ed45a289268d65224 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 10 Jul 2024 04:16:26 +0100 Subject: [PATCH 3/4] added client tests --- .../AccountClientTests.UpdatePreferences.cs | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdatePreferences.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdatePreferences.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdatePreferences.cs new file mode 100644 index 00000000..032be346 --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.UpdatePreferences.cs @@ -0,0 +1,80 @@ +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 UpdatePreferences_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdatePreferencesRequest() + { + Preferences = new Dictionary { { "key1", "val1" }, { "key2", "val2" } } + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/prefs") + .ExpectedHeaders(true) + .WithJsonContent(request) + .Respond(Constants.AppJson, Constants.UserResponse); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.UpdatePreferences(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdatePreferences_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdatePreferencesRequest() + { + Preferences = new Dictionary { { "key1", "val1" }, { "key2", "val2" } } + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/prefs") + .ExpectedHeaders(true) + .WithJsonContent(request) + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.UpdatePreferences(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdatePreferences_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdatePreferencesRequest() + { + Preferences = new Dictionary { { "key1", "val1" }, { "key2", "val2" } } + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{Constants.Endpoint}/account/prefs") + .ExpectedHeaders(true) + .WithJsonContent(request) + .Throw(new HttpRequestException("An error occurred")); + + _appwriteClient.SetSession(Constants.Session); + + // Act + var result = await _appwriteClient.Account.UpdatePreferences(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 65a1a53e50c624f1c0dc3b8c972ac8f77efb926a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 10 Jul 2024 04:17:19 +0100 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 102d038f..4baf03fd 100644 --- a/README.md +++ b/README.md @@ -139,11 +139,11 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress ### Server & Client -![8 / 298](https://progress-bar.dev/8/?scale=298&suffix=%20/%20298&width=500) +![9 / 298](https://progress-bar.dev/9/?scale=298&suffix=%20/%20298&width=500) ### Server Only ![1 / 195](https://progress-bar.dev/1/?scale=195&suffix=%20/%20195&width=300) ### Client Only -![7 / 93](https://progress-bar.dev/7/?scale=93&suffix=%20/%2093&width=300) +![8 / 93](https://progress-bar.dev/8/?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 -![8 / 52](https://progress-bar.dev/8/?scale=52&suffix=%20/%2052&width=120) +![9 / 52](https://progress-bar.dev/9/?scale=52&suffix=%20/%2052&width=120) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -178,7 +178,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [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) | ✅ | ❌ | | [Get Account Preferences](https://appwrite.io/docs/references/1.5.x/client-rest/account#getPrefs) | ✅ | ❌ | -| [Update Preferences](https://appwrite.io/docs/references/1.5.x/client-rest/account#updatePrefs) | ⬛ | ❌ | +| [Update Preferences](https://appwrite.io/docs/references/1.5.x/client-rest/account#updatePrefs) | ✅ | ❌ | | [Create Password Recovery](https://appwrite.io/docs/references/1.5.x/client-rest/account#createRecovery) | ⬛ | ❌ | | [Create Password Recovery (Confirmation)](https://appwrite.io/docs/references/1.5.x/client-rest/account#updateRecovery) | ⬛ | ❌ | | [List Sessions](https://appwrite.io/docs/references/1.5.x/client-rest/account#listSessions) | ⬛ | ❌ |