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

Added update preferences implementation #67

Merged
merged 4 commits into from
Jul 10, 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
![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 |
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
![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 |
|:-:|:-:|:-:|
Expand All @@ -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) | ⬛ | ❌ |
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 @@ -147,4 +147,21 @@ public async Task<AppwriteResult<IReadOnlyDictionary<string, string>>> GetAccoun
return e.GetExceptionResponse<IReadOnlyDictionary<string, string>>();
}
}

/// <inheritdoc/>
public async Task<AppwriteResult<User>> UpdatePreferences(UpdatePreferencesRequest request)
{
try
{
request.Validate(true);

var result = await _accountApi.UpdatePreferences(Session, request);

return result.GetApiResponse();
}
catch (Exception e)
{
return e.GetExceptionResponse<User>();
}
}
}
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 @@ -67,4 +67,12 @@ public interface IAccountClient
/// </summary>
/// <returns>A dictionary of the user preferences</returns>
Task<AppwriteResult<IReadOnlyDictionary<string, string>>> GetAccountPreferences();

/// <summary>
/// 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
/// <para><see href="https://appwrite.io/docs/references/1.5.x/client-rest/account#updatePrefs">Appwrite Docs</see></para>
/// </summary>
/// <param name="preferences">The request content</param>
/// <returns>The user</returns>
Task<AppwriteResult<User>> UpdatePreferences(UpdatePreferencesRequest request);
}
9 changes: 6 additions & 3 deletions src/PinguApps.Appwrite.Client/Internals/IAccountApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ public interface IAccountApi : IBaseApi
Task<IApiResponse<User>> UpdateEmail([Header("x-appwrite-session")] string? session, UpdateEmailRequest request);

[Patch("/account/name")]
Task<IApiResponse<User>> UpdateName([Header("x-appwrite-session")] string? session, UpdateNameRequest name);
Task<IApiResponse<User>> UpdateName([Header("x-appwrite-session")] string? session, UpdateNameRequest request);

[Patch("/account/password")]
Task<IApiResponse<User>> UpdatePassword([Header("x-appwrite-session")] string? session, UpdatePasswordRequest name);
Task<IApiResponse<User>> UpdatePassword([Header("x-appwrite-session")] string? session, UpdatePasswordRequest request);

[Patch("/account/phone")]
Task<IApiResponse<User>> UpdatePhone([Header("x-appwrite-session")] string? session, UpdatePhoneRequest name);
Task<IApiResponse<User>> UpdatePhone([Header("x-appwrite-session")] string? session, UpdatePhoneRequest request);

[Get("/account/prefs")]
Task<IApiResponse<IReadOnlyDictionary<string, string>>> GetAccountPreferences([Header("x-appwrite-session")] string? session);

[Patch("/account/prefs")]
Task<IApiResponse<User>> UpdatePreferences([Header("x-appwrite-session")] string? session, UpdatePreferencesRequest request);
}
9 changes: 8 additions & 1 deletion src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>
{
{ "key1", "val1" },
{ "key2", "val2" }
}
});

result.Result.Switch(
account => Console.WriteLine(string.Join(',', account)),
Expand Down
17 changes: 17 additions & 0 deletions src/PinguApps.Appwrite.Shared/Requests/UpdatePreferencesRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using PinguApps.Appwrite.Shared.Requests.Validators;

namespace PinguApps.Appwrite.Shared.Requests;

/// <summary>
/// The request for updating a users preferences
/// </summary>
public class UpdatePreferencesRequest : BaseRequest<UpdatePreferencesRequest, UpdatePreferencesRequestValidator>
{
/// <summary>
/// New value for preferences
/// </summary>
[JsonPropertyName("prefs")]
public IDictionary<string, string> Preferences { get; set; } = new Dictionary<string, string>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using FluentValidation;

namespace PinguApps.Appwrite.Shared.Requests.Validators;
public class UpdatePreferencesRequestValidator : AbstractValidator<UpdatePreferencesRequest>
{
public UpdatePreferencesRequestValidator()
{
RuleFor(x => x.Preferences).NotNull();
}
}
Original file line number Diff line number Diff line change
@@ -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<string, string> { { "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<string, string> { { "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<string, string> { { "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);
}
}
Original file line number Diff line number Diff line change
@@ -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<string, string> dict)
{
// Arrange
var request = new UpdatePreferencesRequest();

// Act
request.Preferences = dict;

// Assert
Assert.Equal(dict, request.Preferences);
}

public static IEnumerable<object[]> PropertiesToBeSet()
{
yield return new object[] { new Dictionary<string, string> { { "key1", "val1" }, { "key2", "val2" } } };
yield return new object[] { new Dictionary<string, string>() };
}

[Fact]
public void IsValid_WithValidPhoneAndPassword_ReturnsTrue()
{
// Arrange
var request = new UpdatePreferencesRequest
{
Preferences = new Dictionary<string, string> { { "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<ValidationException>(() => 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);
}
}