From bd9e68f268e70e039af7b18c3fba605fc5bccdf1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 12 Oct 2024 00:26:15 +0100 Subject: [PATCH 1/3] Implemented create user target --- src/PinguApps.Appwrite.Playground/App.cs | 8 +++++--- .../Clients/IUsersClient.cs | 8 +++++++- .../Clients/UsersClient.cs | 17 +++++++++++++++-- .../Requests/Users/CreateUserTargetRequest.cs | 5 ++++- .../Users/CreateUserTargetRequestTests.cs | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index e5ee56e0..0d000e57 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,12 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new ListUserTargetsRequest() + var request = new CreateUserTargetRequest() { - UserId = "664aac1a00113f82e620" + UserId = "664aac1a00113f82e620", + ProviderType = Shared.Enums.TargetProviderType.Push, + Identifier = "token" }; - var response = await _server.Users.ListUserTargets(request); + var response = await _server.Users.CreateUserTarget(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 ff723ad8..2c17d538 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs @@ -294,7 +294,13 @@ public interface IUsersClient /// The request content /// The target list Task> ListUserTargets(ListUserTargetsRequest request); - [Obsolete("This method hasn't yet been implemented.", true)] + + /// + /// Create a messaging target + /// Appwrite Docs + /// + /// The request content + /// The target Task> CreateUserTarget(CreateUserTargetRequest request); [Obsolete("This method hasn't yet been implemented.", true)] Task DeleteUserTarget(DeleteUserTargetRequest request); diff --git a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs index e5cb9f3a..be98a2c8 100644 --- a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs @@ -616,9 +616,22 @@ public async Task> ListUserTargets(ListUserTargetsReq } } - [ExcludeFromCodeCoverage] /// - public Task> CreateUserTarget(CreateUserTargetRequest request) => throw new NotImplementedException(); + public async Task> CreateUserTarget(CreateUserTargetRequest request) + { + try + { + request.Validate(true); + + var result = await _usersApi.CreateUserTarget(request.UserId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs index 0ae20f86..fb6011f9 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs @@ -1,6 +1,7 @@ using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Users.Validators; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Requests.Users; @@ -13,7 +14,7 @@ public class CreateUserTargetRequest : UserIdBaseRequest [JsonPropertyName("targetId")] - public string TargetId { get; set; } = string.Empty; + public string TargetId { get; set; } = IdUtils.GenerateUniqueId(); /// /// The target provider type. Can be one of the following: , , @@ -31,11 +32,13 @@ public class CreateUserTargetRequest : UserIdBaseRequest [JsonPropertyName("providerId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProviderId { get; set; } /// /// Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23 /// [JsonPropertyName("name")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Name { get; set; } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Users/CreateUserTargetRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Users/CreateUserTargetRequestTests.cs index 16cc996a..5e50f7c1 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Users/CreateUserTargetRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Users/CreateUserTargetRequestTests.cs @@ -21,7 +21,7 @@ public void Constructor_InitializesWithExpectedValues() var request = new CreateUserTargetRequest(); // Assert - Assert.Equal(string.Empty, request.TargetId); + Assert.NotEmpty(request.TargetId); Assert.Equal(TargetProviderType.Email, request.ProviderType); Assert.Equal(string.Empty, request.Identifier); Assert.Null(request.ProviderId); From 409dd215d3f5a00da2f2df0f574410463e614d1c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 12 Oct 2024 00:42:06 +0100 Subject: [PATCH 2/3] added tests for create user target --- .../Account/Create2faChallengeRequest.cs | 1 + .../Requests/Users/CreateUserTargetRequest.cs | 1 + .../UsersClientTests.CreateUserTarget.cs | 81 +++++++++++++++++++ .../Constants.cs | 13 +++ 4 files changed, 96 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserTarget.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs index feeb1bb1..78cdc679 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs @@ -18,5 +18,6 @@ public class Create2faChallengeRequest : BaseRequest /// [JsonPropertyName("factor")] + [JsonConverter(typeof(JsonStringEnumConverter))] public SecondFactor Factor { get; set; } } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs index fb6011f9..b4e48df0 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs @@ -20,6 +20,7 @@ public class CreateUserTargetRequest : UserIdBaseRequest, , /// [JsonPropertyName("providerType")] + [JsonConverter(typeof(JsonStringEnumConverter))] public TargetProviderType ProviderType { get; set; } /// diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserTarget.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserTarget.cs new file mode 100644 index 00000000..a02ea17c --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.CreateUserTarget.cs @@ -0,0 +1,81 @@ +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 +{ + [Fact] + public async Task CreateUserTarget_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateUserTargetRequest + { + UserId = IdUtils.GenerateUniqueId(), + ProviderType = Shared.Enums.TargetProviderType.Push, + Identifier = "token" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{Constants.Endpoint}/users/{request.UserId}/targets") + .ExpectedHeaders() + .WithJsonContent(request) + .Respond(Constants.AppJson, Constants.TargetResponse); + + // Act + var result = await _appwriteClient.Users.CreateUserTarget(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateUserTarget_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateUserTargetRequest + { + UserId = IdUtils.GenerateUniqueId(), + ProviderType = Shared.Enums.TargetProviderType.Push, + Identifier = "token" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{Constants.Endpoint}/users/{request.UserId}/targets") + .ExpectedHeaders() + .WithJsonContent(request) + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + // Act + var result = await _appwriteClient.Users.CreateUserTarget(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateUserTarget_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateUserTargetRequest + { + UserId = IdUtils.GenerateUniqueId(), + ProviderType = Shared.Enums.TargetProviderType.Push, + Identifier = "token" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{Constants.Endpoint}/users/{request.UserId}/targets") + .ExpectedHeaders() + .WithJsonContent(request) + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Users.CreateUserTarget(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Constants.cs b/tests/PinguApps.Appwrite.Shared.Tests/Constants.cs index 0ff588f2..c9d38650 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Constants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Constants.cs @@ -348,4 +348,17 @@ public static class Constants ] } """; + + public const string TargetResponse = """ + { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "name": "Aegon apple token", + "userId": "259125845563242502", + "providerId": "259125845563242502", + "providerType": "email", + "identifier": "token" + } + """; } From d519f9e9ce695fc75afaafd71f02a672850d2538 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 12 Oct 2024 00:43:05 +0100 Subject: [PATCH 3/3] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2a510827..659b42cf 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 90 / 291](https://img.shields.io/badge/Server_&_Client-90%20%2F%20291-red?style=for-the-badge) +![Server & Client - 91 / 291](https://img.shields.io/badge/Server_&_Client-91%20%2F%20291-red?style=for-the-badge) -![Server - 46 / 201](https://img.shields.io/badge/Server-46%20%2F%20201-red?style=for-the-badge) +![Server - 47 / 201](https://img.shields.io/badge/Server-47%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) @@ -208,7 +208,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Create Phone Verification (Confirmation)](https://appwrite.io/docs/references/1.6.x/client-rest/account#updatePhoneVerification) | ✅ | ❌ | | ### Users -![Account - 35 / 41](https://img.shields.io/badge/Users-35%20%2F%2041-forestgreen?style=for-the-badge) +![Account - 36 / 41](https://img.shields.io/badge/Users-36%20%2F%2041-forestgreen?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -247,7 +247,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Delete User Session](https://appwrite.io/docs/references/1.6.x/server-rest/users#deleteSession) | ❌ | ✅ | | [Update User Status](https://appwrite.io/docs/references/1.6.x/server-rest/users#updateStatus) | ❌ | ✅ | | [List User Targets](https://appwrite.io/docs/references/1.6.x/server-rest/users#listTargets) | ❌ | ✅ | -| [Create User Target](https://appwrite.io/docs/references/1.6.x/server-rest/users#createTarget) | ❌ | ⬛ | +| [Create User Target](https://appwrite.io/docs/references/1.6.x/server-rest/users#createTarget) | ❌ | ✅ | | [Get User Target](https://appwrite.io/docs/references/1.6.x/server-rest/users#getTarget) | ❌ | ⬛ | | [Update User Target](https://appwrite.io/docs/references/1.6.x/server-rest/users#updateTarget) | ❌ | ⬛ | | [Delete User Target](https://appwrite.io/docs/references/1.6.x/server-rest/users#deleteTarget) | ❌ | ⬛ |