diff --git a/README.md b/README.md index 15c46a85..3b40edc7 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 156 / 317](https://img.shields.io/badge/Server_&_Client-156%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 158 / 317](https://img.shields.io/badge/Server_&_Client-158%20%2F%20317-gold?style=for-the-badge) -![Server - 96 / 224](https://img.shields.io/badge/Server-96%20%2F%20224-gold?style=for-the-badge) +![Server - 98 / 224](https://img.shields.io/badge/Server-98%20%2F%20224-gold?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,7 +275,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 31 / 47](https://img.shields.io/badge/Databases-31%20%2F%2047-gold?style=for-the-badge) +![Databases - 33 / 47](https://img.shields.io/badge/Databases-33%20%2F%2047-forestgreen?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -309,8 +309,8 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update String Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateStringAttribute) | ❌ | ✅ | | [Create URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createUrlAttribute) | ❌ | ✅ | | [Update URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateUrlAttribute) | ❌ | ✅ | -| [Get Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getAttribute) | ❌ | ⬛ | -| [Delete Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteAttribute) | ❌ | ⬛ | +| [Get Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getAttribute) | ❌ | ✅ | +| [Delete Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteAttribute) | ❌ | ✅ | | [Update Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateRelationshipAttribute) | ❌ | ✅ | | [List Documents](https://appwrite.io/docs/references/1.6.x/client-rest/databases#listDocuments) | ⬛ | ⬛ | | [Create Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#createDocument) | ⬛ | ⬛ | diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 90251f27..198259c1 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,38 +17,16 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var key = "url"; - var newKey = $"new_{key}"; - - var createRequest = new CreateRelationshipAttributeRequest() - { - DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f", - Key = key, - RelatedCollectionId = "674f57a30011310c0b3c" - }; - - var createResponse = await _server.Databases.CreateRelationshipAttribute(createRequest); - - Console.WriteLine(createResponse.Result.Match( - result => result.ToString(), - appwriteError => appwriteError.Message, - internalError => internalError.Message)); - - Console.WriteLine("#############################################################################################"); - Console.ReadKey(); - - var updateRequest = new UpdateRelationshipAttributeRequest() + var request = new DeleteAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = key, - NewKey = newKey + Key = "test23" }; - var updateResponse = await _server.Databases.UpdateRelationshipAttribute(updateRequest); + var response = await _server.Databases.DeleteAttribute(request); - Console.WriteLine(updateResponse.Result.Match( + Console.WriteLine(response.Result.Match( result => result.ToString(), appwriteError => appwriteError.Message, internalError => internalError.Message)); diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 88ee2213..08ac332b 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -531,13 +531,39 @@ public async Task> UpdateUrlAttribute(UpdateURLAttr } } - [ExcludeFromCodeCoverage] /// - public Task DeleteAttribute(DeleteAttributeRequest request) => throw new NotImplementedException(); + public async Task DeleteAttribute(DeleteAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteAttribute(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> GetAttribute(GetAttributeRequest request) => throw new NotImplementedException(); + public async Task> GetAttribute(GetAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetAttribute(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } /// public async Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request) diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 990699de..11bbdf02 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -257,10 +257,20 @@ public interface IDatabasesClient /// The url attribute Task> UpdateUrlAttribute(UpdateURLAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Deletes an attribute. + /// Appwrite Docs + /// + /// The request content + /// 204 success code Task DeleteAttribute(DeleteAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Get attribute by ID. + /// Appwrite Docs + /// + /// The request content + /// The attribute Task> GetAttribute(GetAttributeRequest request); /// diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs index cc4a44b5..d8cb83c1 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -17,6 +17,7 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Is attribute an array? /// Attribute creation date in ISO 8601 format /// Attribute update date in ISO 8601 format +[JsonConverter(typeof(AttributeJsonConverter))] public abstract record Attribute( [property: JsonPropertyName("key")] string Key, [property: JsonPropertyName("type")] string Type, diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs new file mode 100644 index 00000000..71a11f06 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteAttribute(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/Databases/DatabasesClientTests.GetAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetAttribute.cs new file mode 100644 index 00000000..af2e7396 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeResponse); + + // Act + var result = await _appwriteClient.Databases.GetAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetAttribute(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/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 7fa034f2..6a663f5b 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -609,6 +609,20 @@ public static class TestConstants } """; + public const string AttributeResponse = """ + { + "key": "isEnabled", + "type": "boolean", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "default": false + } + """; + public const string IndexResponse = """ { "key": "index1",