diff --git a/README.md b/README.md index fd84febd..8a85a2c7 100644 --- a/README.md +++ b/README.md @@ -140,9 +140,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ``` ## ⌛ Progress -![Server & Client - 71 / 291](https://img.shields.io/badge/Server_&_Client-71%20%2F%20291-red?style=for-the-badge) +![Server & Client - 72 / 291](https://img.shields.io/badge/Server_&_Client-72%20%2F%20291-red?style=for-the-badge) -![Server - 27 / 201](https://img.shields.io/badge/Server-27%20%2F%20201-red?style=for-the-badge) +![Server - 28 / 201](https://img.shields.io/badge/Server-28%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 - 16 / 41](https://img.shields.io/badge/Users-16%20%2F%2041-gold?style=for-the-badge) +![Account - 17 / 41](https://img.shields.io/badge/Users-17%20%2F%2041-gold?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -227,7 +227,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [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) | ❌ | ✅ | | [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 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) | ❌ | ⬛ | | [Update MFA](https://appwrite.io/docs/references/1.6.x/server-rest/users#updateMfa) | ❌ | ⬛ | | [Delete Authenticator](https://appwrite.io/docs/references/1.6.x/server-rest/users#deleteMfaAuthenticator) | ❌ | ⬛ | diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index b1b3f29c..6c5a9ee1 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Client.Clients; @@ -102,9 +101,7 @@ public async Task> ListIdentities(ListIdentitiesR { request.Validate(true); - var queryStrings = request.Queries?.Select(x => x.GetQueryString()) ?? []; - - var result = await _accountApi.ListIdentities(GetCurrentSessionOrThrow(), queryStrings); + var result = await _accountApi.ListIdentities(GetCurrentSessionOrThrow(), RequestUtils.GetQueryStrings(request.Queries)); return result.GetApiResponse(); } @@ -153,9 +150,7 @@ public async Task> ListLogs(ListLogsRequest request) { request.Validate(true); - var queryStrings = request.Queries?.Select(x => x.GetQueryString()) ?? []; - - var result = await _accountApi.ListLogs(GetCurrentSessionOrThrow(), queryStrings); + var result = await _accountApi.ListLogs(GetCurrentSessionOrThrow(), RequestUtils.GetQueryStrings(request.Queries)); return result.GetApiResponse(); } diff --git a/src/PinguApps.Appwrite.Client/Utils/RequestUtils.cs b/src/PinguApps.Appwrite.Client/Utils/RequestUtils.cs new file mode 100644 index 00000000..2c778faf --- /dev/null +++ b/src/PinguApps.Appwrite.Client/Utils/RequestUtils.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Linq; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Client.Utils; +internal static class RequestUtils +{ + internal static IEnumerable GetQueryStrings(List? queries) => + queries?.Select(x => x.GetQueryString()) ?? []; +} diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index a1b8147e..3731d71b 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,12 +17,12 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new CreateUserJwtRequest() + var request = new ListUserLogsRequest() { UserId = "664aac1a00113f82e620", }; - var response = await _server.Users.CreateUserJwt(request); + var response = await _server.Users.ListUserLogs(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 cc29cb26..d7f7dad8 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IUsersClient.cs @@ -141,7 +141,13 @@ public interface IUsersClient /// The request content /// The user Task> UpdateUserLabels(UpdateUserLabelsRequest request); - [Obsolete("This method hasn't yet been implemented.", true)] + + /// + /// Get the user activity logs list by its unique ID. + /// Appwrite Docs + /// + /// The request content + /// The user Task> ListUserLogs(ListUserLogsRequest request); [Obsolete("This method hasn't yet been implemented.", true)] Task> ListUserMemberships(ListUserMembershipsRequest request); diff --git a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs index 25dbd4ad..f45064e9 100644 --- a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Server.Internals; @@ -29,9 +28,7 @@ public async Task> ListUsers(ListUsersRequest request) { request.Validate(true); - var queryStrings = request.Queries?.Select(x => x.GetQueryString()) ?? []; - - var result = await _usersApi.ListUsers(queryStrings, request.Search); + var result = await _usersApi.ListUsers(RequestUtils.GetQueryStrings(request.Queries), request.Search); return result.GetApiResponse(); } @@ -99,9 +96,7 @@ public async Task> ListIdentities(ListIdentitiesR { request.Validate(true); - var queryStrings = request.Queries?.Select(x => x.GetQueryString()) ?? []; - - var result = await _usersApi.ListIdentities(queryStrings, request.Search); + var result = await _usersApi.ListIdentities(RequestUtils.GetQueryStrings(request.Queries), request.Search); return result.GetApiResponse(); } @@ -298,9 +293,22 @@ public async Task> UpdateUserLabels(UpdateUserLabelsRequest } } - [ExcludeFromCodeCoverage] /// - public Task> ListUserLogs(ListUserLogsRequest request) => throw new NotImplementedException(); + public async Task> ListUserLogs(ListUserLogsRequest request) + { + try + { + request.Validate(true); + + var result = await _usersApi.ListUserLogs(request.UserId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Utils/RequestUtils.cs b/src/PinguApps.Appwrite.Server/Utils/RequestUtils.cs new file mode 100644 index 00000000..ce001c11 --- /dev/null +++ b/src/PinguApps.Appwrite.Server/Utils/RequestUtils.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Linq; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Server.Utils; +internal static class RequestUtils +{ + internal static IEnumerable GetQueryStrings(List? queries) => + queries?.Select(x => x.GetQueryString()) ?? []; +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.ListUserLogs.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.ListUserLogs.cs new file mode 100644 index 00000000..0da2a4fd --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Users/UsersClientTests.ListUserLogs.cs @@ -0,0 +1,104 @@ +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 ListUserLogs_ValidRequestData = + new TheoryData + { + new ListUserLogsRequest + { + UserId = IdUtils.GenerateUniqueId(), + }, + new ListUserLogsRequest + { + UserId = IdUtils.GenerateUniqueId() + } + }; + + [Theory] + [MemberData(nameof(ListUserLogs_ValidRequestData))] + public async Task ListUserLogs_ShouldReturnSuccess_WhenApiCallSucceeds(ListUserLogsRequest request) + { + // Arrange + _mockHttp.Expect(HttpMethod.Get, $"{Constants.Endpoint}/users/{request.UserId}/logs") + .ExpectedHeaders() + .Respond(Constants.AppJson, Constants.LogsListResponse); + + // Act + var result = await _appwriteClient.Users.ListUserLogs(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListUserLogs_ShouldProvideQueries_WhenQueriesProvided() + { + // Arrange + var query = Query.Limit(5); + var request = new ListUserLogsRequest() + { + UserId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{Constants.Endpoint}/users/{request.UserId}/logs") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(Constants.AppJson, Constants.LogsListResponse); + + // Act + var result = await _appwriteClient.Users.ListUserLogs(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListUserLogs_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListUserLogsRequest + { + UserId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{Constants.Endpoint}/users/{request.UserId}/logs") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, Constants.AppJson, Constants.AppwriteError); + + // Act + var result = await _appwriteClient.Users.ListUserLogs(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListUserLogs_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListUserLogsRequest + { + UserId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{Constants.Endpoint}/users/{request.UserId}/logs") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Users.ListUserLogs(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +}