From 295e52f315dfe45bc2ff0fc09985e7f904079cdd Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 8 Oct 2024 23:20:48 +0100 Subject: [PATCH 1/4] Implemented create user jwt --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/IUsersClient.cs | 8 +++++++- .../Clients/UsersClient.cs | 17 +++++++++++++++-- .../Requests/Users/CreateUserJwtRequest.cs | 2 ++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 0b21e143..65d85c36 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,13 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new UpdateUserLabelsRequest() + var request = new CreateUserJwtRequest() { UserId = "664aac1a00113f82e620", - Labels = ["test", "admin"] + }; - var response = await _server.Users.UpdateUserLabels(request); + var response = await _server.Users.CreateUserJwt(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs b/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs index 0e18e25b..cc29cb26 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs @@ -124,7 +124,13 @@ public interface IUsersClient /// The request content /// The user Task> UpdateEmail(UpdateEmailRequest request); - [Obsolete("This method hasn't yet been implemented.", true)] + + /// + /// Use this endpoint to create a JSON Web Token for user by its unique ID. You can use the resulting JWT to authenticate on behalf of the user. The JWT secret will become invalid if the session it uses gets deleted. + /// Appwrite Docs + /// + /// The request content + /// The jwt Task> CreateUserJwt(CreateUserJwtRequest request); /// diff --git a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs index 89e11bda..25dbd4ad 100644 --- a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs @@ -264,9 +264,22 @@ public async Task> UpdateEmail(UpdateEmailRequest request) } } - [ExcludeFromCodeCoverage] /// - public Task> CreateUserJwt(CreateUserJwtRequest request) => throw new NotImplementedException(); + public async Task> CreateUserJwt(CreateUserJwtRequest request) + { + try + { + request.Validate(true); + + var result = await _usersApi.CreateUserJwt(request.UserId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } /// public async Task> UpdateUserLabels(UpdateUserLabelsRequest request) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserJwtRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserJwtRequest.cs index 2eeafac8..e5724f26 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserJwtRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserJwtRequest.cs @@ -12,11 +12,13 @@ public class CreateUserJwtRequest : UserIdBaseRequestrecent to use the most recent session. Defaults to the most recent session /// [JsonPropertyName("sessionId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? SessionId { get; set; } /// /// Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds /// [JsonPropertyName("duration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Duration { get; set; } } From 0c8b3416872d02143de2c564e4dc7507ef6a2cd8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 8 Oct 2024 23:29:55 +0100 Subject: [PATCH 2/4] added tests for create user jwt --- .../Users/UsersClientTests.CreateUserJwt.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserJwt.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserJwt.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserJwt.cs new file mode 100644 index 00000000..86cf5da7 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserJwt.cs @@ -0,0 +1,85 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Users; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Users; +public partial class UsersClientTests +{ + public static TheoryData CreateUserJwt_ValidRequestData = + [ + new CreateUserJwtRequest + { + UserId = IdUtils.GenerateUniqueId() + }, + new CreateUserJwtRequest + { + UserId = IdUtils.GenerateUniqueId(), + Duration = 1800, + SessionId = IdUtils.GenerateUniqueId() + } + ]; + + [Theory] + [MemberData(nameof(CreateUserJwt_ValidRequestData))] + public async Task CreateUserJwt_ShouldReturnSuccess_WhenApiCallSucceeds(CreateUserJwtRequest request) + { + // Arrange + _mockHttp.Expect(HttpMethod.Post, $"{Constants.Endpoint}/users/{request.UserId}/jwts") + .WithJsonContent(request) + .ExpectedHeaders() + .Respond(Constants.AppJson, Constants.JwtResponse); + + // Act + var result = await _appwriteClient.Users.CreateUserJwt(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateUserJwt_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateUserJwtRequest + { + UserId = "user123" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{Constants.Endpoint}/users/{request.UserId}/jwts") + .WithJsonContent(request) + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + // Act + var result = await _appwriteClient.Users.CreateUserJwt(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateUserJwt_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateUserJwtRequest + { + UserId = "user123" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{Constants.Endpoint}/users/{request.UserId}/jwts") + .WithJsonContent(request) + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Users.CreateUserJwt(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 2303959a1a4aec98cbac4fc24d1d24b2b7fcd9a9 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 8 Oct 2024 23:30:46 +0100 Subject: [PATCH 3/4] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3dde581..fd84febd 100644 --- a/README.md +++ b/README.md @@ -140,9 +140,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ``` ## ⌛ Progress -![Server & Client - 70 / 291](https://img.shields.io/badge/Server_&_Client-70%20%2F%20291-red?style=for-the-badge) +![Server & Client - 71 / 291](https://img.shields.io/badge/Server_&_Client-71%20%2F%20291-red?style=for-the-badge) -![Server - 26 / 201](https://img.shields.io/badge/Server-26%20%2F%20201-red?style=for-the-badge) +![Server - 27 / 201](https://img.shields.io/badge/Server-27%20%2F%20201-red?style=for-the-badge) ![Client - 44 / 90](https://img.shields.io/badge/Client-44%20%2F%2090-gold?style=for-the-badge) @@ -207,7 +207,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Create Phone Verification (Confirmation)](https://appwrite.io/docs/references/1.6.x/client-rest/account#updatePhoneVerification) | ✅ | ❌ | | ### Users -![Account - 15 / 41](https://img.shields.io/badge/Users-15%20%2F%2041-gold?style=for-the-badge) +![Account - 16 / 41](https://img.shields.io/badge/Users-16%20%2F%2041-gold?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -225,7 +225,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Get User](https://appwrite.io/docs/references/1.6.x/server-rest/users#get) | ❌ | ✅ | | [Delete User](https://appwrite.io/docs/references/1.6.x/server-rest/users#delete) | ❌ | ✅ | | [Update Email](https://appwrite.io/docs/references/1.6.x/server-rest/users#updateEmail) | ❌ | ✅ | -| [Create User JWT](https://appwrite.io/docs/references/1.6.x/server-rest/users#createJWT) | ❌ | ⬛ | +| [Create User JWT](https://appwrite.io/docs/references/1.6.x/server-rest/users#createJWT) | ❌ | ✅ | | [Update User Labels](https://appwrite.io/docs/references/1.6.x/server-rest/users#updateLabels) | ❌ | ✅ | | [List User Logs](https://appwrite.io/docs/references/1.6.x/server-rest/users#listLogs) | ❌ | ⬛ | | [List User Memberships](https://appwrite.io/docs/references/1.6.x/server-rest/users#listMemberships) | ❌ | ⬛ | From 6c05ca9439fda8cd8732b8a3726b60e63f6a1adb Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 8 Oct 2024 23:32:26 +0100 Subject: [PATCH 4/4] Update src/PinguApps.Appwrite.Playground/App.cs Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- src/PinguApps.Appwrite.Playground/App.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 65d85c36..a1b8147e 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -20,7 +20,6 @@ public async Task Run(string[] args) var request = new CreateUserJwtRequest() { UserId = "664aac1a00113f82e620", - }; var response = await _server.Users.CreateUserJwt(request);