Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database indexes #555

Merged
merged 9 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match(

## ⌛ Progress
<!-- `red` for first third, `gold` for second third, `forestgreen` for final third, `blue` for 100% -->
![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)

Expand Down Expand Up @@ -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 |
|:-:|:-:|:-:|
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
68 changes: 60 additions & 8 deletions src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -602,19 +602,71 @@ public async Task<AppwriteResult<AttributeRelationship>> UpdateRelationshipAttri
/// <inheritdoc/>
public Task<AppwriteResult<Document>> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException();

[ExcludeFromCodeCoverage]
/// <inheritdoc/>
public Task<AppwriteResult<IndexesList>> ListIndexes(ListIndexesRequest request) => throw new NotImplementedException();
public async Task<AppwriteResult<IndexesList>> ListIndexes(ListIndexesRequest request)
{
try
{
request.Validate(true);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category Functionality

Validation Error Handling Not Specific

Tell me more

What is the issue?
The validation of requests is marked as required (true), but there's no error handling specific to validation failures, which could mask validation errors as generic exceptions.

Why this matters
When validation fails, it will be caught in the generic catch block, making it difficult to distinguish between validation errors and other types of exceptions. This affects error handling and debugging.

Chat with Korbit by mentioning @korbit-ai, and give a 👍 or 👎 to help Korbit improve your reviews.


var result = await _databasesApi.ListIndexes(request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries));

return result.GetApiResponse();
}
catch (Exception e)
{
return e.GetExceptionResponse<IndexesList>();
}
}

[ExcludeFromCodeCoverage]
/// <inheritdoc/>
public Task<AppwriteResult<Index>> CreateIndex(CreateIndexRequest request) => throw new NotImplementedException();
public async Task<AppwriteResult<Index>> 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<Index>();
}
}

[ExcludeFromCodeCoverage]
/// <inheritdoc/>
public Task<AppwriteResult> DeleteIndex(DeleteIndexRequest request) => throw new NotImplementedException();
public async Task<AppwriteResult> 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]
/// <inheritdoc/>
public Task<AppwriteResult<Index>> GetIndex(GetIndexRequest request) => throw new NotImplementedException();
public async Task<AppwriteResult<Index>> 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<Index>();
}
}
}
43 changes: 32 additions & 11 deletions src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -284,27 +285,47 @@ public interface IDatabasesClient
[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<Document>> CreateDocument(CreateDocumentRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<Index>> CreateIndex(CreateIndexRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult> DeleteDocument(DeleteDocumentRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult> DeleteIndex(DeleteIndexRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<Document>> GetDocument(GetDocumentRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<Index>> GetIndex(GetIndexRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<DocumentsList>> ListDocuments(ListDocumentsRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<Document>> UpdateDocument(UpdateDocumentRequest request);

/// <summary>
/// List indexes in the collection.
/// <para><see href="https://appwrite.io/docs/references/cloud/server-rest/databases#listIndexes">Appwrite Docs</see></para>
/// </summary>
/// <param name="request">The request content</param>
/// <returns>The indexes list</returns>
Task<AppwriteResult<IndexesList>> ListIndexes(ListIndexesRequest request);

[Obsolete("Endpoint not yet implemented.")]
Task<AppwriteResult<Document>> UpdateDocument(UpdateDocumentRequest request);
/// <summary>
/// Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. Attributes can be <see cref="IndexType.Key"/>, <see cref="IndexType.Fulltext"/> and <see cref="IndexType.Unique"/>.
/// <para><see href="https://appwrite.io/docs/references/cloud/server-rest/databases#createIndex">Appwrite Docs</see></para>
/// </summary>
/// <param name="request">The request content</param>
/// <returns>The index</returns>
Task<AppwriteResult<Index>> CreateIndex(CreateIndexRequest request);

/// <summary>
/// Delete an index.
/// <para><see href="https://appwrite.io/docs/references/cloud/server-rest/databases#deleteIndex">Appwrite Docs</see></para>
/// </summary>
/// <param name="request">The request content</param>
/// <returns>204 success code</returns>
Task<AppwriteResult> DeleteIndex(DeleteIndexRequest request);

/// <summary>
/// Get index by ID.
/// <para><see href="https://appwrite.io/docs/references/cloud/server-rest/databases#getIndex">Appwrite Docs</see></para>
/// </summary>
/// <param name="request">The request content</param>
/// <returns>The index</returns>
Task<AppwriteResult<Index>> GetIndex(GetIndexRequest request);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading
Loading