diff --git a/README.md b/README.md
index 05a9b5ff..1de1514c 100644
--- a/README.md
+++ b/README.md
@@ -141,11 +141,11 @@ string emailAddressOrErrorMessage = userResponse.Result.Match(
## ⌛ Progress
-![Server & Client - 116 / 318](https://img.shields.io/badge/Server_&_Client-116%20%2F%20318-gold?style=for-the-badge)
+![Server & Client - 118 / 318](https://img.shields.io/badge/Server_&_Client-118%20%2F%20318-gold?style=for-the-badge)
-![Server - 61 / 225](https://img.shields.io/badge/Server-61%20%2F%20225-red?style=for-the-badge)
+![Server - 62 / 225](https://img.shields.io/badge/Server-62%20%2F%20225-red?style=for-the-badge)
-![Client - 55 / 93](https://img.shields.io/badge/Client-55%20%2F%2093-gold?style=for-the-badge)
+![Client - 56 / 93](https://img.shields.io/badge/Client-56%20%2F%2093-gold?style=for-the-badge)
### 🔑 Key
| Icon | Definition |
@@ -256,7 +256,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match(
| [Update Phone Verification](https://appwrite.io/docs/references/1.6.x/server-rest/users#updatePhoneVerification) | ❌ | ✅ |
### Teams
-![Teams - 16 / 26](https://img.shields.io/badge/Teams-16%20%2F%2026-gold?style=for-the-badge)
+![Teams - 18 / 26](https://img.shields.io/badge/Teams-18%20%2F%2026-gold?style=for-the-badge)
| Endpoint | Client | Server |
|:-:|:-:|:-:|
@@ -267,7 +267,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match(
| [Delete Team](https://appwrite.io/docs/references/1.6.x/client-rest/teams#delete) | ✅ | ✅ |
| [List Team Memberships](https://appwrite.io/docs/references/1.6.x/client-rest/teams#listMemberships) | ✅ | ✅ |
| [Create Team Membership](https://appwrite.io/docs/references/1.6.x/client-rest/teams#createMembership) | ✅ | ✅ |
-| [Get Team Membership](https://appwrite.io/docs/references/1.6.x/client-rest/teams#getMembership) | ⬛ | ⬛ |
+| [Get Team Membership](https://appwrite.io/docs/references/1.6.x/client-rest/teams#getMembership) | ✅ | ✅ |
| [Update Membership](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updateMembership) | ⬛ | ⬛ |
| [Delete Team Membership](https://appwrite.io/docs/references/1.6.x/client-rest/teams#deleteMembership) | ✅ | ✅ |
| [Update Team Membership Status](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updateMembershipStatus) | ⬛ | ⬛ |
diff --git a/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs b/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs
index 858ab4c0..c6d1ccb7 100644
--- a/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs
+++ b/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs
@@ -79,7 +79,13 @@ public interface ITeamsClient
/// The request content
/// 204 success code
Task DeleteTeamMembership(DeleteTeamMembershipRequest request);
- [Obsolete("This method hasn't yet been implemented!")]
+
+ ///
+ /// Get a team member by the membership unique id. All team members have read access for this resource.
+ /// Appwrite Docs
+ ///
+ /// The request content
+ /// The membership
Task> GetTeamMembership(GetTeamMembershipRequest request);
[Obsolete("This method hasn't yet been implemented!")]
Task> UpdateMembership(UpdateMembershipRequest request);
diff --git a/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs b/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs
index 1a09c4a5..7e580097 100644
--- a/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs
+++ b/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs
@@ -6,6 +6,7 @@
using PinguApps.Appwrite.Client.Internals;
using PinguApps.Appwrite.Client.Utils;
using PinguApps.Appwrite.Shared;
+using PinguApps.Appwrite.Shared.Enums;
using PinguApps.Appwrite.Shared.Requests.Teams;
using PinguApps.Appwrite.Shared.Responses;
@@ -130,6 +131,7 @@ public async Task> CreateTeamMembership(CreateTeamMem
{
try
{
+ request.ValidationContext = ValidationContext.Client;
request.Validate(true);
var result = await _teamsApi.CreateTeamMembership(GetCurrentSessionOrThrow(), request.TeamId, request);
@@ -159,9 +161,22 @@ public async Task DeleteTeamMembership(DeleteTeamMembershipReque
}
}
- [ExcludeFromCodeCoverage]
///
- public Task> GetTeamMembership(GetTeamMembershipRequest request) => throw new NotImplementedException();
+ public async Task> GetTeamMembership(GetTeamMembershipRequest request)
+ {
+ try
+ {
+ request.Validate(true);
+
+ var result = await _teamsApi.GetTeamMembership(GetCurrentSessionOrThrow(), request.TeamId, request.MembershipId);
+
+ return result.GetApiResponse();
+ }
+ catch (Exception e)
+ {
+ return e.GetExceptionResponse();
+ }
+ }
[ExcludeFromCodeCoverage]
///
diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs
index 7d26ce76..444aa331 100644
--- a/src/PinguApps.Appwrite.Playground/App.cs
+++ b/src/PinguApps.Appwrite.Playground/App.cs
@@ -19,22 +19,23 @@ public async Task Run(string[] args)
{
_client.SetSession(_session);
- var request = new DeleteTeamMembershipRequest()
+ var request = new CreateTeamMembershipRequest()
{
TeamId = "67142b78001c379958cb",
- MembershipId = "671448a87af3cd4babc7"
+ Email = "pingu@example.com",
+ Name = "Pingu"
};
- //var clientResponse = await _client.Teams.CreateTeamMembership(request);
+ var clientResponse = await _client.Teams.CreateTeamMembership(request);
- //Console.WriteLine(clientResponse.Result.Match(
- // result => result.ToString(),
- // appwriteError => appwriteError.Message,
- // internalError => internalError.Message));
+ Console.WriteLine(clientResponse.Result.Match(
+ result => result.ToString(),
+ appwriteError => appwriteError.Message,
+ internalError => internalError.Message));
Console.WriteLine("############################################################################");
- var serverResponse = await _server.Teams.DeleteTeamMembership(request);
+ var serverResponse = await _server.Teams.CreateTeamMembership(request);
Console.WriteLine(serverResponse.Result.Match(
result => result.ToString(),
diff --git a/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs b/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs
index a67fa469..04a8c655 100644
--- a/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs
+++ b/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs
@@ -79,7 +79,13 @@ public interface ITeamsClient
/// The request content
/// 204 success code
Task DeleteTeamMembership(DeleteTeamMembershipRequest request);
- [Obsolete("This method hasn't yet been implemented!")]
+
+ ///
+ /// Get a team member by the membership unique id. All team members have read access for this resource.
+ /// Appwrite Docs
+ ///
+ /// The request content
+ /// The membership
Task> GetTeamMembership(GetTeamMembershipRequest request);
[Obsolete("This method hasn't yet been implemented!")]
Task> UpdateMembership(UpdateMembershipRequest request);
diff --git a/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs b/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs
index 21542ba9..3245a507 100644
--- a/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs
+++ b/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs
@@ -6,6 +6,7 @@
using PinguApps.Appwrite.Server.Internals;
using PinguApps.Appwrite.Server.Utils;
using PinguApps.Appwrite.Shared;
+using PinguApps.Appwrite.Shared.Enums;
using PinguApps.Appwrite.Shared.Requests.Teams;
using PinguApps.Appwrite.Shared.Responses;
@@ -130,6 +131,7 @@ public async Task> CreateTeamMembership(CreateTeamMem
{
try
{
+ request.ValidationContext = ValidationContext.Server;
request.Validate(true);
var result = await _teamsApi.CreateTeamMembership(request.TeamId, request);
@@ -159,9 +161,22 @@ public async Task DeleteTeamMembership(DeleteTeamMembershipReque
}
}
- [ExcludeFromCodeCoverage]
///
- public Task> GetTeamMembership(GetTeamMembershipRequest request) => throw new NotImplementedException();
+ public async Task> GetTeamMembership(GetTeamMembershipRequest request)
+ {
+ try
+ {
+ request.Validate(true);
+
+ var result = await _teamsApi.GetTeamMembership(request.TeamId, request.MembershipId);
+
+ return result.GetApiResponse();
+ }
+ catch (Exception e)
+ {
+ return e.GetExceptionResponse();
+ }
+ }
[ExcludeFromCodeCoverage]
///
diff --git a/src/PinguApps.Appwrite.Shared/Enums/ValidationContext.cs b/src/PinguApps.Appwrite.Shared/Enums/ValidationContext.cs
new file mode 100644
index 00000000..bfce1c0a
--- /dev/null
+++ b/src/PinguApps.Appwrite.Shared/Enums/ValidationContext.cs
@@ -0,0 +1,7 @@
+namespace PinguApps.Appwrite.Shared.Enums;
+public enum ValidationContext
+{
+ None,
+ Client,
+ Server
+}
diff --git a/src/PinguApps.Appwrite.Shared/Requests/BaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/BaseRequest.cs
index d4da9baf..b98db5a7 100644
--- a/src/PinguApps.Appwrite.Shared/Requests/BaseRequest.cs
+++ b/src/PinguApps.Appwrite.Shared/Requests/BaseRequest.cs
@@ -1,5 +1,8 @@
-using FluentValidation;
+using System.Text.Json.Serialization;
+using FluentValidation;
using FluentValidation.Results;
+using PinguApps.Appwrite.Shared.Attributes;
+using PinguApps.Appwrite.Shared.Enums;
namespace PinguApps.Appwrite.Shared.Requests;
@@ -12,6 +15,13 @@ public abstract class BaseRequest
where TRequest : class
where TValidator : IValidator, new()
{
+ ///
+ /// A flag to determine whether we are validating from the server or the client. Often not needed.
+ ///
+ [JsonIgnore]
+ [SdkExclude]
+ public ValidationContext ValidationContext { get; set; } = ValidationContext.None;
+
///
/// True if the request object passes all validation
///
diff --git a/src/PinguApps.Appwrite.Shared/Requests/Teams/Validators/CreateTeamMembershipRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Teams/Validators/CreateTeamMembershipRequestValidator.cs
index f038f16a..a44e2c1f 100644
--- a/src/PinguApps.Appwrite.Shared/Requests/Teams/Validators/CreateTeamMembershipRequestValidator.cs
+++ b/src/PinguApps.Appwrite.Shared/Requests/Teams/Validators/CreateTeamMembershipRequestValidator.cs
@@ -1,5 +1,6 @@
using System;
using FluentValidation;
+using PinguApps.Appwrite.Shared.Enums;
namespace PinguApps.Appwrite.Shared.Requests.Teams.Validators;
public class CreateTeamMembershipRequestValidator : AbstractValidator
@@ -36,6 +37,11 @@ public CreateTeamMembershipRequestValidator()
.When(x => x.Url is not null)
.WithMessage("Invalid URL format.");
+ RuleFor(x => x.Url)
+ .NotEmpty()
+ .When(x => x.ValidationContext == ValidationContext.Client)
+ .WithMessage("Url is required.");
+
RuleFor(x => x.Name)
.NotEmpty()
.WithMessage("Name must either be null or a non empty string.")
diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.CreateTeamMembership.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.CreateTeamMembership.cs
index 0c45c9a0..c3f20de9 100644
--- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.CreateTeamMembership.cs
+++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.CreateTeamMembership.cs
@@ -16,7 +16,8 @@ public async Task CreateTeamMembership_ShouldReturnSuccess_WhenApiCallSucceeds()
{
TeamId = IdUtils.GenerateUniqueId(),
Email = "test@example.com",
- Roles = ["role1", "role2"]
+ Roles = ["role1", "role2"],
+ Url = "https://localhost:1234"
};
_mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships")
@@ -41,7 +42,8 @@ public async Task CreateTeamMembership_ShouldReturnError_WhenSessionIsNull()
{
TeamId = IdUtils.GenerateUniqueId(),
Email = "test@example.com",
- Roles = ["role1", "role2"]
+ Roles = ["role1", "role2"],
+ Url = "https://localhost:1234"
};
// Act
@@ -61,7 +63,8 @@ public async Task CreateTeamMembership_ShouldHandleException_WhenApiCallFails()
{
TeamId = IdUtils.GenerateUniqueId(),
Email = "test@example.com",
- Roles = ["role1", "role2"]
+ Roles = ["role1", "role2"],
+ Url = "https://localhost:1234"
};
_mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships")
@@ -87,7 +90,8 @@ public async Task CreateTeamMembership_ShouldReturnErrorResponse_WhenExceptionOc
{
TeamId = IdUtils.GenerateUniqueId(),
Email = "test@example.com",
- Roles = ["role1", "role2"]
+ Roles = ["role1", "role2"],
+ Url = "https://localhost:1234"
};
_mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships")
diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.GetTeamMembership.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.GetTeamMembership.cs
new file mode 100644
index 00000000..fbbe082a
--- /dev/null
+++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Teams/TeamsClientTests.GetTeamMembership.cs
@@ -0,0 +1,101 @@
+using System.Net;
+using PinguApps.Appwrite.Client.Clients;
+using PinguApps.Appwrite.Shared.Requests.Teams;
+using PinguApps.Appwrite.Shared.Tests;
+using PinguApps.Appwrite.Shared.Utils;
+using RichardSzalay.MockHttp;
+
+namespace PinguApps.Appwrite.Client.Tests.Clients.Teams;
+public partial class TeamsClientTests
+{
+ [Fact]
+ public async Task GetTeamMembership_ShouldReturnSuccess_WhenApiCallSucceeds()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships/{request.MembershipId}")
+ .ExpectedHeaders(true)
+ .Respond(TestConstants.AppJson, TestConstants.MembershipResponse);
+
+ _appwriteClient.SetSession(TestConstants.Session);
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(request);
+
+ // Assert
+ Assert.True(result.Success);
+ }
+
+ [Fact]
+ public async Task GetTeamMembership_ShouldReturnError_WhenSessionIsNull()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(request);
+
+ // Assert
+ Assert.True(result.IsError);
+ Assert.True(result.IsInternalError);
+ Assert.Equal(ISessionAware.SessionExceptionMessage, result.Result.AsT2.Message);
+ }
+
+ [Fact]
+ public async Task GetTeamMembership_ShouldHandleException_WhenApiCallFails()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships/{request.MembershipId}")
+ .ExpectedHeaders(true)
+ .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError);
+
+ _appwriteClient.SetSession(TestConstants.Session);
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(request);
+
+ // Assert
+ Assert.True(result.IsError);
+ Assert.True(result.IsAppwriteError);
+ }
+
+ [Fact]
+ public async Task GetTeamMembership_ShouldReturnErrorResponse_WhenExceptionOccurs()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships/{request.MembershipId}")
+ .ExpectedHeaders(true)
+ .Throw(new HttpRequestException("An error occurred"));
+
+ _appwriteClient.SetSession(TestConstants.Session);
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(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.Server.Tests/Clients/Teams/TeamsClientTests.GetTeamMembership.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Teams/TeamsClientTests.GetTeamMembership.cs
new file mode 100644
index 00000000..383936dd
--- /dev/null
+++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Teams/TeamsClientTests.GetTeamMembership.cs
@@ -0,0 +1,75 @@
+using System.Net;
+using PinguApps.Appwrite.Shared.Requests.Teams;
+using PinguApps.Appwrite.Shared.Tests;
+using PinguApps.Appwrite.Shared.Utils;
+using RichardSzalay.MockHttp;
+
+namespace PinguApps.Appwrite.Server.Tests.Clients.Teams;
+public partial class TeamsClientTests
+{
+ [Fact]
+ public async Task GetTeamMembership_ShouldReturnSuccess_WhenApiCallSucceeds()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships/{request.MembershipId}")
+ .ExpectedHeaders()
+ .Respond(TestConstants.AppJson, TestConstants.MembershipResponse);
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(request);
+
+ // Assert
+ Assert.True(result.Success);
+ }
+
+ [Fact]
+ public async Task GetTeamMembership_ShouldHandleException_WhenApiCallFails()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships/{request.MembershipId}")
+ .ExpectedHeaders()
+ .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError);
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(request);
+
+ // Assert
+ Assert.True(result.IsError);
+ Assert.True(result.IsAppwriteError);
+ }
+
+ [Fact]
+ public async Task GetTeamMembership_ShouldReturnErrorResponse_WhenExceptionOccurs()
+ {
+ // Arrange
+ var request = new GetTeamMembershipRequest
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ MembershipId = IdUtils.GenerateUniqueId()
+ };
+
+ _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/teams/{request.TeamId}/memberships/{request.MembershipId}")
+ .ExpectedHeaders()
+ .Throw(new HttpRequestException("An error occurred"));
+
+ // Act
+ var result = await _appwriteClient.Teams.GetTeamMembership(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/Requests/Teams/CreateTeamMembershipRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Teams/CreateTeamMembershipRequestTests.cs
index b6bfca93..648ec623 100644
--- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Teams/CreateTeamMembershipRequestTests.cs
+++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Teams/CreateTeamMembershipRequestTests.cs
@@ -1,4 +1,5 @@
using FluentValidation;
+using PinguApps.Appwrite.Shared.Enums;
using PinguApps.Appwrite.Shared.Requests.Teams;
using PinguApps.Appwrite.Shared.Requests.Teams.Validators;
using PinguApps.Appwrite.Shared.Utils;
@@ -71,7 +72,15 @@ public void Properties_CanBeSet()
new()
{
TeamId = IdUtils.GenerateUniqueId(),
- UserId = IdUtils.GenerateUniqueId()
+ UserId = IdUtils.GenerateUniqueId(),
+ ValidationContext = ValidationContext.Server
+ },
+ new()
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ UserId = IdUtils.GenerateUniqueId(),
+ Url = "https://example.com",
+ ValidationContext = ValidationContext.Client
}
];
@@ -147,6 +156,12 @@ public void IsValid_WithValidData_ReturnsTrue(CreateTeamMembershipRequest reques
new()
{
TeamId = IdUtils.GenerateUniqueId()
+ },
+ new()
+ {
+ TeamId = IdUtils.GenerateUniqueId(),
+ UserId = IdUtils.GenerateUniqueId(),
+ ValidationContext = ValidationContext.Client
}
};