diff --git a/README.md b/README.md index 3b40edc7..13cb326a 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 158 / 317](https://img.shields.io/badge/Server_&_Client-158%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 162 / 317](https://img.shields.io/badge/Server_&_Client-162%20%2F%20317-gold?style=for-the-badge) -![Server - 98 / 224](https://img.shields.io/badge/Server-98%20%2F%20224-gold?style=for-the-badge) +![Server - 102 / 224](https://img.shields.io/badge/Server-102%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 - 33 / 47](https://img.shields.io/badge/Databases-33%20%2F%2047-forestgreen?style=for-the-badge) +![Databases - 37 / 47](https://img.shields.io/badge/Databases-37%20%2F%2047-forestgreen?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -317,10 +317,10 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Get Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#getDocument) | ⬛ | ⬛ | | [Update Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#updateDocument) | ⬛ | ⬛ | | [Delete Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#deleteDocument) | ⬛ | ⬛ | -| [List Indexes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listIndexes) | ❌ | ⬛ | -| [Create Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIndex) | ❌ | ⬛ | -| [Get Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getIndex) | ❌ | ⬛ | -| [Delete Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteIndex) | ❌ | ⬛ | +| [List Indexes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listIndexes) | ❌ | ✅ | +| [Create Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIndex) | ❌ | ✅ | +| [Get Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getIndex) | ❌ | ✅ | +| [Delete Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteIndex) | ❌ | ✅ | ### Storage ![storage - 0 / 21](https://img.shields.io/badge/Storage-0%20%2F%2021-red?style=for-the-badge) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 198259c1..20e50e11 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,14 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new DeleteAttributeRequest() + var request = new GetIndexRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "test23" + Key = "index_1" }; - var response = await _server.Databases.DeleteAttribute(request); + var response = await _server.Databases.GetIndex(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 08ac332b..58df7c69 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -602,19 +602,71 @@ public async Task> UpdateRelationshipAttri /// public Task> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException(); - [ExcludeFromCodeCoverage] /// - public Task> ListIndexes(ListIndexesRequest request) => throw new NotImplementedException(); + public async Task> ListIndexes(ListIndexesRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListIndexes(request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateIndex(CreateIndexRequest request) => throw new NotImplementedException(); + public async Task> CreateIndex(CreateIndexRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateIndex(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task DeleteIndex(DeleteIndexRequest request) => throw new NotImplementedException(); + public async Task DeleteIndex(DeleteIndexRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteIndex(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> GetIndex(GetIndexRequest request) => throw new NotImplementedException(); + public async Task> GetIndex(GetIndexRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetIndex(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } } diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 11bbdf02..2351eeec 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using PinguApps.Appwrite.Shared; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; @@ -284,27 +285,47 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> CreateDocument(CreateDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> CreateIndex(CreateIndexRequest request); - [Obsolete("Endpoint not yet implemented.")] Task DeleteDocument(DeleteDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task DeleteIndex(DeleteIndexRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> GetDocument(GetDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> GetIndex(GetIndexRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> ListDocuments(ListDocumentsRequest request); [Obsolete("Endpoint not yet implemented.")] + Task> UpdateDocument(UpdateDocumentRequest request); + + /// + /// List indexes in the collection. + /// Appwrite Docs + /// + /// The request content + /// The indexes list Task> ListIndexes(ListIndexesRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> UpdateDocument(UpdateDocumentRequest request); + /// + /// Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. Attributes can be , and . + /// Appwrite Docs + /// + /// The request content + /// The index + Task> CreateIndex(CreateIndexRequest request); + + /// + /// Delete an index. + /// Appwrite Docs + /// + /// The request content + /// 204 success code + Task DeleteIndex(DeleteIndexRequest request); + + /// + /// Get index by ID. + /// Appwrite Docs + /// + /// The request content + /// The index + Task> GetIndex(GetIndexRequest request); } diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs new file mode 100644 index 00000000..b6cf11e6 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs @@ -0,0 +1,88 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Enums; +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 CreateIndex_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myIndex", + IndexType = IndexType.Unique, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.IndexResponse); + + // Act + var result = await _appwriteClient.Databases.CreateIndex(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateIndex_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myIndex", + IndexType = IndexType.Unique, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateIndex(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myIndex", + IndexType = IndexType.Unique, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateIndex(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.DeleteIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteIndex.cs new file mode 100644 index 00000000..13a0fcba --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteIndex.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 DeleteIndex_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteIndex(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteIndex_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteIndex(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteIndex(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.GetIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetIndex.cs new file mode 100644 index 00000000..383471ed --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetIndex.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 GetIndex_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.IndexResponse); + + // Act + var result = await _appwriteClient.Databases.GetIndex(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetIndex_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetIndex(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetIndex(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.ListIndexes.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListIndexes.cs new file mode 100644 index 00000000..2fbef873 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListIndexes.cs @@ -0,0 +1,99 @@ +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 ListIndexes_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.IndexesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListIndexes_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Limit(5); + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.IndexesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListIndexes_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListIndexes_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +}