Skip to content

Database aliases, new terminologies and a migration to modules #9693

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

Open
wants to merge 122 commits into
base: 1.7.x
Choose a base branch
from

Conversation

ItzNotABug
Copy link
Member

@ItzNotABug ItzNotABug commented Apr 26, 2025

What does this PR do?

This PR does the following -

  1. Moves the database controller to a module structure
  2. Introduces a new Table API, similar to Collections with new Column & Row APIs
  3. Refactor the Realtime events to send both Collection and Table events until we fully deprecate Collection APIs
  4. Updates the tests to integrate the Table APIs alongside Collection APIs for improved coverage to make sure everything is consistent across versions!

Test Plan

Unit & E2E Tests!

Related PRs and Issues

N/A.

Checklist

  • Have you read the Contributing Guidelines on issues?
  • If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?

@ItzNotABug ItzNotABug self-assigned this Apr 26, 2025
Copy link

coderabbitai bot commented Apr 26, 2025

Walkthrough

This update introduces a comprehensive "Tables" API to the database module, paralleling the existing "Collections" API. It adds new endpoints, models, validators, error codes, and test suites for managing tables, columns, rows, and their indexes. The update also adapts event handling, response schemas, and usage statistics to support both collections/documents and tables/rows, ensuring consistency and backward compatibility across the platform.

Changes

File(s) / Area Change Summary
Configuration & Constants Added new context constants for tables/columns/rows; updated events, errors, and service configs to support tables API; clarified internal resource comments.
Core Platform & Event System Extended event, exception, and messaging classes to support tables/rows/columns alongside collections/documents/attributes; registered new Databases module; updated error handling for new API contexts.
Database Module (HTTP Actions & Services) Introduced new module/service structure for databases; added HTTP endpoints for CRUD operations on databases, tables, columns, rows, and their indexes; implemented context-aware base classes for collections/tables, attributes/columns, documents/rows, and indexes; registered all actions via service registries.
Database Models & Response Schemas Added new response models for tables, columns, rows, and column indexes; updated usage models to include tables/rows metrics; enhanced list and base models for new resource types and terminology.
Database Query Validators Added query validators for tables and columns; updated OpenAPI and GraphQL type mappers to support new validators and dynamic model resolution.
Event & Realtime Handling Updated event generation, mirroring, and validation logic to support both collections/documents and tables/rows schemas; improved test coverage for new event patterns.
E2E & Unit Tests (Collections & Tables) Added/updated extensive end-to-end and unit tests for tables/columns/rows, including permissions, abuse, usage, and GraphQL scenarios; adapted existing tests for new terminology; ensured parallel coverage for collections and tables APIs.
GraphQL Layer Added/updated GraphQL queries, mutations, and fragments for tables/columns/rows; unified attribute/column type mapping; updated test cases for new schema keys.
Miscellaneous Minor improvements to code style, assertion order, docblocks, and method signatures for clarity and consistency.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API
    participant DBModule
    participant EventQueue
    participant Database

    Client->>API: POST /v1/databases/:databaseId/tables
    API->>DBModule: Create Table
    DBModule->>Database: Create Table Document & Collection
    Database-->>DBModule: Table Created
    DBModule->>EventQueue: Queue Table Created Event
    DBModule-->>API: Table Model Response
    API-->>Client: 201 Created (Table)

    Client->>API: POST /v1/databases/:databaseId/tables/:tableId/rows
    API->>DBModule: Create Row
    DBModule->>Database: Insert Row Document
    Database-->>DBModule: Row Created
    DBModule->>EventQueue: Queue Row Created Event
    DBModule-->>API: Row Model Response
    API-->>Client: 201 Created (Row)

    Client->>API: GET /v1/databases/:databaseId/tables/:tableId/columns
    API->>DBModule: List Columns
    DBModule->>Database: Fetch Columns
    Database-->>DBModule: Columns List
    DBModule-->>API: Column List Model
    API-->>Client: 200 OK (Columns)
Loading

Poem

(\(\
( -.-) Tables and rows, columns in tow,
((")(") A new API garden for data to grow.

Collections and docs, now joined by new friends,
With indexes and usage, the coverage extends.

So hop on, dear devs, and give it a try—
The database rabbit has multiplied!
🐇📊

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Apr 26, 2025

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
binutils 2.42-r0 CVE-2025-0840 HIGH
libexpat 2.6.4-r0 CVE-2024-8176 HIGH
libxml2 2.12.7-r0 CVE-2024-56171 HIGH
libxml2 2.12.7-r0 CVE-2025-24928 HIGH
libxml2 2.12.7-r0 CVE-2025-27113 HIGH
sqlite-libs 3.45.3-r1 CVE-2025-29087 HIGH
xz 5.6.2-r0 CVE-2025-31115 HIGH
xz-libs 5.6.2-r0 CVE-2025-31115 HIGH
golang.org/x/crypto v0.31.0 CVE-2025-22869 HIGH

Source Code Scan Results

🎉 No vulnerabilities found!

Copy link

github-actions bot commented Apr 26, 2025

✨ Benchmark results

  • Requests per second: 992
  • Requests with 200 status code: 178,684
  • P99 latency: 0.190736059

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 992 1,459
200 178,684 262,747
P99 0.190736059 0.126087605

@ItzNotABug ItzNotABug changed the title Database aliases <> new terminologies Database aliases, new terminologies and a migration to modules May 4, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🔭 Outside diff range comments (1)
tests/e2e/General/UsageTest.php (1)

45-63: 🛠️ Refactor suggestion

Remove duplicated trait-alias statements to avoid maintenance overhead

The same as-aliases are declared twice for several methods (e.g. listVariables, updateVariable, deleteDeployment, …).
These duplicates do not break PHP at runtime (the second alias is silently ignored) but make the class noisy and error-prone – future edits may accidentally touch only one of the two lines.

-        FunctionsBase::listVariables insteadof SitesBase;
-        FunctionsBase::listVariables insteadof SitesBase;
+        FunctionsBase::listVariables insteadof SitesBase;

…repeat for every pair.

Cleaning this block up keeps the trait-resolution readable and prevents confusion about which alias is actually in effect.

♻️ Duplicate comments (2)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (2)

467-470: size should accept zero – use strict null check instead of empty()

Using empty($size) treats 0 as "empty", so a caller cannot deliberately resize an attribute/column to 0. A null sentinel is already used to indicate "not provided".

-        if (!empty($size)) {
+        if ($size !== null) {
             $attribute->setAttribute('size', $size);
         }

445-451: ⚠️ Potential issue

Fix misplaced parentheses in getAttribute calls

There are syntax errors in the type and filter checks due to misplaced parentheses, which will cause unexpected behavior. Also, 'filter' should likely be 'format' based on other parts of the code.

-        if ($attribute->getAttribute(('type') !== $type)) {
+        if ($attribute->getAttribute('type') !== $type) {
             throw new Exception($this->getTypeInvalidException());
         }
 
-        if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) {
+        if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute('format') !== $filter) {
             throw new Exception($this->getTypeInvalidException());
         }
🧹 Nitpick comments (24)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php (1)

49-49: Documentation path needs updating for column terminology

The documentation link still references "attribute" instead of "column" which doesn't align with the new terminology being introduced in this PR.

-                description: '/docs/references/databases/create-ip-attribute.md',
+                description: '/docs/references/databases/create-ip-column.md',
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php (1)

51-51: Documentation path needs updating for column terminology

The documentation link still references "attribute" instead of "column" which doesn't align with the new terminology being introduced in this PR.

-                description: '/docs/references/databases/update-integer-attribute.md',
+                description: '/docs/references/databases/update-integer-column.md',
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php (1)

49-49: Documentation path needs updating for column terminology

The documentation link still references "attribute" instead of "column" which doesn't align with the new terminology being introduced in this PR.

-                description: '/docs/references/databases/create-integer-attribute.md',
+                description: '/docs/references/databases/create-integer-column.md',
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php (1)

55-55: Documentation path needs updating for row terminology

The documentation link still references "document" instead of "row" which doesn't align with the new terminology being introduced in this PR.

-                    description: '/docs/references/databases/create-document.md',
+                    description: '/docs/references/databases/create-row.md',
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php (2)

47-59: Doc-link still points to “attribute” instead of “column”

The SDK description references /docs/references/databases/update-email-attribute.md.
For the new Tables API we probably want a dedicated column page to avoid confusing users who migrate from collections → tables.

- description: '/docs/references/databases/update-email-attribute.md',
+ description: '/docs/references/databases/update-email-column.md',

(Or update the target file accordingly.)


61-67: Consider making the required & default mutual-exclusion explicit

The validator comment says “Cannot be set when column is required.”, but nothing enforces it locally; the restriction is hidden in the parent EmailUpdate.
A tiny runtime guard here would give earlier feedback if the parent signature ever changes:

if ($required === true && $default !== null) {
    throw new Exception('Cannot set default when column is required');
}
tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php (1)

235-257: Potentially brittle negative-path assertion

When the user lacks permissions the API could return 401, 403 or 404 depending
on auth vs resource visibility. Unlike testWriteDocuments, here we assert
only 401, which may cause flaky failures if the implementation changes.

-        $this->assertEquals(401, $rows['headers']['status-code']);
+        $this->assertContains($rows['headers']['status-code'], [401, 403, 404]);
tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php (1)

176-181: Negative read assertion could mask a 403/404

Similar to the member test, consider broadening the expected codes or using
assertNotEquals(200, …) to future-proof against backend changes.

tests/e2e/General/UsageTest.php (1)

422-460: DRY: factor out the repeated “create-then-optionally-delete” loop

The two loops building databases (Collections API vs Tables API) are byte-for-byte identical except for the endpoint. Extract the common logic into a private helper:

private function seedDatabases(string $endpoint, int &$total, int &$requests): string
{
    $databaseId = '';
    for ($i = 0; $i < self::CREATE; $i++) {
        $name = uniqid() . ' database';
        $response = $this->client->call(Client::METHOD_POST, $endpoint, /* … */);
        // assertions …

        $total++; $requests++; $databaseId = $response['body']['$id'];

        if ($i < (self::CREATE / 2)) {
            $this->client->call(Client::METHOD_DELETE, $endpoint.'/'.$databaseId, /* … */);
            $total--; $requests++;
        }
    }
    return $databaseId;
}

This will shave ~70 duplicated lines, shorten the test by ~250 LOC overall, and make future endpoint changes trivial.

Also applies to: 659-697

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (4)

1-8: Add declare(strict_types=1); for project-wide strict typing

All core Appwrite sources start with declare(strict_types=1);.
Keeping the directive consistent avoids silent type juggling and aligns with the coding-standard checks.

 <?php
+declare(strict_types=1);

13-14: Property should be non-nullable

$context is initialised with a string constant and is never allowed to be null (setter validates).
Mark it as string instead of ?string to better express invariants and remove a useless null-check for callers.

-    private ?string $context = DATABASE_INDEX_CONTEXT;
+    private string $context = DATABASE_INDEX_CONTEXT;

27-31: Prefix global constants with backslash to prevent namespace lookup

Inside a namespace PHP first looks for a namespaced constant named DATABASE_INDEX_CONTEXT; if none exists it falls back to the global one.
Prefixing with \ eliminates this two-step lookup and guards against future namespace-level constants shadowing the global ones:

-        if (!\in_array($context, [DATABASE_INDEX_CONTEXT, DATABASE_COLUMN_INDEX_CONTEXT], true)) {
+        if (!\in_array($context, [\DATABASE_INDEX_CONTEXT, \DATABASE_COLUMN_INDEX_CONTEXT], true)) {

Apply the same change to every occurrence of these constants.


89-94: Typo: getGrantParentNotFoundExceptiongetGrandParentNotFoundException

“Grandparent” is the correct term in the rest of the codebase (GRAND_PARENT_* constants).
Renaming avoids confusion and improves discoverability in IDE search.

tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (2)

186-194: Fix assertion parameter order

assertEquals($expected, $actual) – currently the arguments are swapped, so a failure message would misleadingly show the actual value as “expected”.

-        $this->assertEquals($response['body'], "");
+        $this->assertEquals("", $response['body']);

226-235: Avoid brittle “field-count” assertions

count($response['body']) === 15 will fail the moment a new field is legitimately added to the response.
Prefer checking for the specific keys you rely on:

$this->assertArrayHasKey('rowsTotal',   $response['body']);
$this->assertArrayHasKey('tablesTotal', $response['body']);

This keeps the test future-proof while still validating contract essentials.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php (1)

165-173: Event parameters mix “row” and “document” IDs

Both rowId and documentId are set to the same value, even when operating on the Collections API.

If consumers rely on one of these keys being absent for collections vs tables, this may break them or create redundancy. Please either:

  • drop rowId for collections, or
  • document the new behaviour in event-consumer code.
tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php (2)

20-72: Duplicate helper exists in other permissions suites

createCollection() largely duplicates logic from DatabasesPermissionsMemberTest and TeamTest. Extract this into the shared DatabasesPermissionsScope trait to avoid diverging behaviour and reduce maintenance.


148-150: Global auth role mutation could leak across tests

Authorization::cleanRoles() / manual restore relies on each test always reaching the foreach restore block—even when an assertion fails. Wrap the role-manipulating section in try/finally to guarantee cleanup.

tests/e2e/General/AbuseTest.php (1)

332-346: Missing success assertion for resource creation

$movies (table/collection) is created without verifying its HTTP status code or payload.
Adding an assertion makes failures easier to pinpoint and keeps the helper symmetrical with the earlier database assertion.

         $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . "/$endpoint", [
             ...
         ]);

+        $this->assertEquals(201, $movies['headers']['status-code']);
+        $this->assertEquals('Movies', $movies['body']['name']);
tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php (1)

126-353: High duplication between Collections- and Tables-API tests

testAttributesCollectionsAPI() and testAttributesTablesAPI() differ only by
– the root endpoint (collections vs tables),
– the term (attributes vs columns).

Duplicating ~200 lines makes maintenance hard; every bug-fix must be applied twice. A small data-provider or a private helper that takes {resourceType, attributeKey} parameters would DRY this out while preserving coverage.

No functional breakage, but refactoring will:
• cut test run-time,
• make future terminology migrations cheaper,
• reduce cognitive overhead.

tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php (1)

67-69: Avoid fixed sleep() calls in tests

Hard-coded sleeps (1-3 s) make the suite slower and flaky under load.
Polling the resource until its status becomes available|ready (or until a timeout) is both faster in the common case and more robust.

Consider extracting a helper like:

private function waitUntilReady(callable $probe, int $timeout = 15): void
{
    $start = time();
    while (time() - $start < $timeout) {
        if ($probe()) {
            return;
        }
        usleep(200_000); // 200 ms
    }
    $this->fail('Timed-out waiting for async worker');
}

and replace the bare sleep() statements.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (3)

421-422: Rename parameter for consistency

The parameter $filter is used to check against format throughout the code. Rename it to $format for better consistency and clarity.

-    final protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
+    final protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $format = null, string|bool|int|float $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document

Remember to update the parameter usage in line 449 as well.


421-422: Consider reducing method parameter count

The updateAttribute method has 15 parameters, making it difficult to maintain and understand. Consider using a structured parameter object or builder pattern.

A more maintainable approach would be to accept an options array or data transfer object:

-    final protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
+    final protected function updateAttribute(
+        string $databaseId,
+        string $collectionId,
+        string $key,
+        Database $dbForProject,
+        Event $queueForEvents,
+        string $type,
+        array $attributeOptions = []
+    ): Document

Then extract values from $attributeOptions with default values:

$size = $attributeOptions['size'] ?? null;
$format = $attributeOptions['format'] ?? null;
// etc.

607-616: Set context values consistently

When setting event context parameters, both attributeId and columnId are set to the same value, as are tableId and collectionId. Consider setting only the context-relevant parameters for better clarity and reduced payload size.

         $queueForEvents
             ->setContext('database', $db)
             ->setParam('databaseId', $databaseId)
-            ->setParam('attributeId', $attribute->getId())
-            ->setParam('columnId', $attribute->getId())
-            ->setParam('tableId', $collection->getId())
-            ->setParam('collectionId', $collection->getId())
+            ->setParam($this->isCollectionsAPI() ? 'attributeId' : 'columnId', $attribute->getId())
+            ->setParam($this->isCollectionsAPI() ? 'collectionId' : 'tableId', $collection->getId())
             ->setContext($this->isCollectionsAPI() ? 'collection' : 'table', $collection);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d74765 and c542ba0.

📒 Files selected for processing (67)
  • .github/workflows/tests.yml (2 hunks)
  • src/Appwrite/Event/Realtime.php (1 hunks)
  • src/Appwrite/Messaging/Adapter/Realtime.php (3 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Workers/Databases.php (17 hunks)
  • tests/e2e/General/AbuseTest.php (7 hunks)
  • tests/e2e/General/UsageTest.php (3 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/AuthTest.php (3 hunks)
  • tests/e2e/Services/GraphQL/Base.php (19 hunks)
  • tests/e2e/Services/GraphQL/DatabaseClientTest.php (6 hunks)
  • tests/e2e/Services/GraphQL/DatabaseServerTest.php (29 hunks)
  • tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php (11 hunks)
  • tests/e2e/Services/Realtime/RealtimeCustomClientTest.php (19 hunks)
✅ Files skipped from review due to trivial changes (3)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php
  • tests/e2e/Services/GraphQL/AuthTest.php
  • tests/e2e/Services/GraphQL/Base.php
🚧 Files skipped from review as they are similar to previous changes (47)
  • tests/e2e/Services/GraphQL/DatabaseClientTest.php
  • .github/workflows/tests.yml
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php
  • tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Update.php
  • src/Appwrite/Event/Realtime.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Get.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Datetime/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Delete.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/XList.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/XList.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Logs/XList.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Usage/Get.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php
  • tests/e2e/Services/GraphQL/DatabaseServerTest.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/XList.php
  • src/Appwrite/Messaging/Adapter/Realtime.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Delete.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/XList.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Relationship/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Get.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Get.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Indexes/Delete.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Enum/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Logs/XList.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php
  • src/Appwrite/Platform/Modules/Databases/Workers/Databases.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Get.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php (9)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php (4)
  • Create (16-69)
  • getName (20-23)
  • getResponseModel (25-28)
  • __construct (30-68)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php (4)
  • Create (17-70)
  • getName (21-24)
  • getResponseModel (26-29)
  • __construct (31-69)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Create.php (4)
  • Create (17-72)
  • getName (21-24)
  • getResponseModel (26-29)
  • __construct (31-71)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php (4)
  • Create (17-70)
  • getName (21-24)
  • getResponseModel (26-29)
  • __construct (31-69)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php (1)
  • Create (19-74)
src/Appwrite/Utopia/Response.php (1)
  • Response (151-963)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php (3)
  • getName (22-25)
  • getResponseModel (27-30)
  • __construct (32-70)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php (3)
  • getName (23-26)
  • getResponseModel (28-31)
  • __construct (33-73)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (3)
  • setContext (42-49)
  • getSdkNamespace (82-85)
  • getSdkGroup (74-77)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php (8)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Create.php (4)
  • Create (16-69)
  • getName (20-23)
  • getResponseModel (25-28)
  • __construct (30-68)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php (4)
  • Create (17-70)
  • getName (21-24)
  • getResponseModel (26-29)
  • __construct (31-69)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Create.php (1)
  • Create (19-74)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Create.php (1)
  • Create (17-70)
src/Appwrite/Utopia/Response.php (1)
  • Response (151-963)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Delete.php (3)
  • getName (20-23)
  • getResponseModel (26-29)
  • __construct (31-67)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Update.php (3)
  • getName (23-26)
  • getResponseModel (28-31)
  • __construct (33-71)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (3)
  • setContext (42-49)
  • getSdkNamespace (82-85)
  • getSdkGroup (74-77)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php (4)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (1)
  • Create (30-319)
src/Appwrite/SDK/Method.php (1)
  • Method (9-295)
src/Appwrite/Utopia/Response.php (1)
  • Response (151-963)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php (3)
  • setContext (25-32)
  • getSdkNamespace (65-68)
  • getSdkGroup (57-60)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Update.php (7)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Boolean/Update.php (4)
  • Update (18-71)
  • getName (22-25)
  • getResponseModel (27-30)
  • __construct (32-70)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Float/Update.php (4)
  • Update (19-74)
  • getName (23-26)
  • getResponseModel (28-31)
  • __construct (33-73)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php (4)
  • Update (19-74)
  • getName (23-26)
  • getResponseModel (28-31)
  • __construct (33-73)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/String/Update.php (1)
  • Update (21-75)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/URL/Update.php (1)
  • Update (19-72)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php (1)
  • Update (21-92)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Email/Create.php (3)
  • getName (21-24)
  • getResponseModel (26-29)
  • __construct (31-69)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php (9)
src/Appwrite/Platform/Modules/Databases/Workers/Databases.php (2)
  • Databases (21-630)
  • action (54-85)
src/Appwrite/Event/Event.php (1)
  • Event (10-600)
src/Appwrite/Extend/Exception.php (1)
  • Exception (7-439)
src/Appwrite/Utopia/Response.php (2)
  • Response (151-963)
  • output (716-798)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php (1)
  • Delete (19-105)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php (8)
  • Action (8-130)
  • getResponseModel (18-18)
  • getSdkNamespace (65-68)
  • getSdkGroup (57-60)
  • getParentNotFoundException (73-78)
  • getNotFoundException (83-88)
  • setContext (25-32)
  • isCollectionsAPI (45-50)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php (5)
  • Action (8-95)
  • getSdkGroup (61-64)
  • getNotFoundException (79-84)
  • setContext (25-32)
  • isCollectionsAPI (53-56)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php (2)
  • getResponseModel (39-42)
  • action (86-306)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php (1)
  • action (72-156)
tests/e2e/General/AbuseTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-317)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: E2E Service Test (Webhooks)
  • GitHub Check: E2E Service Test (Teams)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Databases/Collections)
  • GitHub Check: E2E Service Test (Databases/Tables)
  • GitHub Check: Benchmark
🔇 Additional comments (10)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/IP/Create.php (1)

1-70: Implementation looks good and follows established patterns

The Create class correctly extends IPCreate, sets the appropriate context, and configures the HTTP endpoint with all necessary parameters. The implementation is consistent with other column type classes.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Update.php (1)

1-74: Implementation looks good and follows established patterns

The Update class correctly extends IntegerUpdate, sets the appropriate context, and configures the HTTP endpoint with all necessary parameters. The implementation is consistent with other column type update classes.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Columns/Integer/Create.php (1)

1-72: Implementation looks good and follows established patterns

The Create class correctly extends IntegerCreate, sets the appropriate context, and configures the HTTP endpoint with all necessary parameters. The implementation is consistent with other column type classes.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php (2)

1-78: Overall implementation follows established patterns

The class correctly extends DocumentCreate and sets up an appropriate endpoint for row creation with proper parameters and injected dependencies.


76-77:

✅ Verification successful

Verify action implementation with correct parameters

A previous review comment mentioned issues with the parent::action call regarding missing the $data parameter and having a duplicated $databaseId parameter. Ensure that this has been fixed in your implementation.


🏁 Script executed:

#!/bin/bash
# Verify that the action method properly handles parameters

# Check for any action method implementations in the class or parent class that might handle these parameters
rg -A 10 "action\s*\(\s*[^)]*databaseId[^)]*" src/Appwrite/Platform/Modules/Databases/Http/Databases/

Length of output: 62830


🏁 Script executed:

#!/bin/bash
# Locate the Create action in Rows
FILE="src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php"

# Show signature of action() and any parent::action calls
rg -C3 "public function action" "$FILE"
rg -C3 "parent::action" "$FILE"

Length of output: 298


🏁 Script executed:

#!/bin/bash
# Locate the Rows/Create.php file and display its contents
find src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables -type f -name 'Create.php' -print
echo "===== File Content: First 200 lines ====="
find src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables -type f -name 'Create.php' -exec sed -n '1,200p' {} \;

Length of output: 41320


🏁 Script executed:

#!/bin/bash
# Show action implementation and any parent calls in Collections/Documents/Create
FILE="src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php"

# Display file location
echo "File: $FILE"

# Show lines around public function action
rg -C3 "public function action" "$FILE"

# Show any parent::action calls
rg -C3 "parent::action" "$FILE"

Length of output: 934


Verified correct action signature in createRow

The createRow handler in Tables/Rows/Create.php inherits action() from Collections/Documents/Create.php, whose method signature is now:

public function action(
    string $databaseId,
    string $documentId,
    string $collectionId,
    string|array $data,
    ?array $permissions,
    UtopiaResponse $response,
    Database $dbForProject,
    Document $user,
    Event $queueForEvents,
    StatsUsage $queueForStatsUsage
): void

No parent::action(...) calls remain with missing or duplicated parameters. The parameters are injected in the correct order, so this concern is resolved.

tests/e2e/General/UsageTest.php (1)

681-697: Potential dangling-ID bug

$databaseId is reused to store the last created database even when the delete branch runs first ($i < CREATE/2).
If self::CREATE is odd, the last preserved DB will be from the second half of the loop – fine.
But any later assertion that assumes $databaseId is also the one that owns $tableId (created in the next loop) silently relies on this coincidence.

Please assert that the $databaseId used for tables is really the same DB that survived the deletion pass, or store them separately ($tablesDbId).

tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (1)

19-29: 🛠️ Refactor suggestion

Method name misrepresents what is being tested

testCreateCollection handles tables (/tables endpoint), not collections.
Given the coexistence of both terminologies in the codebase, accurate naming is vital for maintainability and grep-ability.

Rename to testCreateTable (and analogously update the dependent methods).

-class DatabasesConsoleClientTest extends Scope
-{
-
-    public function testCreateCollection(): array
+class DatabasesConsoleClientTest extends Scope
+{
+
+    public function testCreateTable(): array

Likely an incorrect or invalid review comment.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php (1)

150-155: Metric constant looks mismatched with the comment

addMetric(str_replace('{databaseInternalId}', …, METRIC_DATABASE_ID_OPERATIONS_WRITES), 1);
The inline comment says “per collection”, but the placeholder only contains the database internal ID. Double-check the constant name and ensure you are not accidentally aggregating per-database when per-collection was intended.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php (1)

99-104: Missing context for worker queue may cause stale cache

After queuing the database job you purge the collection cache locally (purgeCachedDocument) but do not enqueue a follow-up event to purge edge caches (as done in the Collection Delete path). Confirm that the worker invoked via DATABASE_TYPE_DELETE_INDEX eventually invalidates caches; otherwise clients may still receive the removed index.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (1)

1-30: Good job implementing the dual-API architecture!

The abstract Action class provides a clean abstraction for handling both collections/attributes and tables/columns APIs through a single codebase. The context-aware exception handling and response models create an elegant solution for supporting both terminologies.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

♻️ Duplicate comments (9)
src/Appwrite/Utopia/Response/Model/Table.php (2)

114-136: Preserve legacy attributes for backward compatibility.

The current implementation removes legacy attributes after copying them to new keys. This approach might break existing API consumers who depend on the original attribute names. Consider preserving both old and new keys during a transition period to ensure backward compatibility.

        $document->setAttribute('columns', $columns);
+        // Preserve the legacy attribute for backward compatibility
+        $document->setAttribute('attributes', $columns);

        $related = $document->getAttribute('relatedCollection');
        if ($related !== null) {
            $document->setAttribute('relatedTable', $related);
+            // Keep the legacy attribute
+            // $document->setAttribute('relatedCollection', $related);
        }

        $documentSecurity = $document->getAttribute('documentSecurity');
        $document->setAttribute('rowSecurity', $documentSecurity);
+        // Keep the legacy attribute
+        $document->setAttribute('documentSecurity', $documentSecurity);

        // remove anyways as they are already copied above.
        $document
-            ->removeAttribute('attributes')
-            ->removeAttribute('documentSecurity')
-            ->removeAttribute('relatedCollection');

If you decide to remove legacy attributes, consider:

  1. Documenting this as a breaking change in your release notes
  2. Incrementing the major version number (following semantic versioning)
  3. Providing a migration guide for API consumers

144-148: Check if target key exists before remapping.

The remapping function should verify if the target key already exists before overwriting it. This prevents potential data loss if both keys were somehow present.

        foreach ($columns as $i => $column) {
            if (isset($column['relatedCollection'])) {
-                $columns[$i]['relatedTable'] = $column['relatedCollection'];
+                if (!isset($columns[$i]['relatedTable'])) {
+                    $columns[$i]['relatedTable'] = $column['relatedCollection'];
+                }
                unset($columns[$i]['relatedCollection']);
            }
        }
tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php (2)

65-66: Replace sleep() with a more reliable waiting mechanism

Hard-coded sleep(2) assumes the worker finishes within two seconds. In CI this is often either unnecessarily long (slowing the test suite) or not long enough (causing sporadic failures).

Consider polling for the column status until it becomes available, or use the existing helper waitForAttribute() that other tests employ.


260-260: 🛠️ Refactor suggestion

Replace sleep() with a more reliable waiting mechanism

Similar to the earlier sleep(2), this hard-coded delay is brittle and can cause inconsistent test behavior.

Consider polling for the column status until it becomes available, or use an existing helper method.

-        sleep(1);
+        // Wait for the column to be available
+        $this->waitForAttribute($dbForProject, 'attributes', function ($attribute) use ($moviesId) {
+            return $attribute->getAttribute('collectionId') === $moviesId
+                && $attribute->getAttribute('key') === 'title'
+                && $attribute->getAttribute('status') === 'available';
+        }, 10, 0.5);
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php (2)

252-283: Guard against cyclic relationships to prevent infinite recursion

$processDocument() is recursively called for every related document but there is no visited-set / depth limit. A cyclic relationship (A→B, B→A) will cause a stack overflow and take down the worker.

Consider keeping a SplObjectStorage / $visitedIds set and short-circuiting when a document has already been processed.


289-295: ⚠️ Potential issue

Wrong variable referenced – sensitive attributes list is always null

The callback passed to array_map references $document from the outer scope instead of the $row (attribute) passed into the closure. This produces an array filled with null, so setPayload() never flags any relationship keys as sensitive.

-$relationships = \array_map(
-    fn ($document) => $document->getAttribute('key'),
+$relationships = \array_map(
+    fn ($attribute) => $attribute->getAttribute('key'),
    \array_filter(
        $collection->getAttribute('attributes', []),
        fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
    )
);
tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php (1)

14-18: ⚠️ Potential issue

Trait usage error

The current trait usage will cause a fatal error. The DatabasesBase trait is referenced without proper namespace qualification or import.

-    use DatabasesBase;
+    use \Tests\E2E\Services\Databases\DatabasesBase;

Or add an import statement at the top:

use Tests\E2E\Services\Databases\DatabasesBase;
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (2)

475-477: empty($size) swallows legitimate zero – use strict null check
This issue was flagged in a previous review and is still present.

Using empty() treats 0 as “empty”, preventing clients from resizing to zero.
Check for null instead.


453-459: ⚠️ Potential issue

Broken conditional – parentheses turn the whole check into a boolean

Both lines accidentally wrap the field name in parentheses, so PHP first evaluates the comparison ('type' !== $type) and then passes true/false to getAttribute().
Consequently the validation is never executed as intended.

-        if ($attribute->getAttribute(('type') !== $type)) {
+        if ($attribute->getAttribute('type') !== $type) {
...
-        if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) {
+        if (
+            $attribute->getAttribute('type') === Database::VAR_STRING &&
+            $attribute->getAttribute('format') !== $filter   // probable field name
+        ) {
🧹 Nitpick comments (19)
tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php (1)

16-16: Consider renaming the test class to match the new terminology.

While the namespace has been updated to reflect the collections module, the class name DatabaseServerTest still uses the old terminology. Consider renaming it to CollectionsServerTest for better consistency with the new terminology and namespace structure.

-class DatabaseServerTest extends Scope
+class CollectionsServerTest extends Scope
tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php (1)

14-14: Consider renaming the test class to match the new terminology.

While the namespace has been updated to reflect the collections module, the class name DatabaseClientTest still uses the old terminology. Consider renaming it to CollectionsClientTest for better consistency with the new terminology and namespace structure.

-class DatabaseClientTest extends Scope
+class CollectionsClientTest extends Scope
tests/e2e/Services/GraphQL/Tables/AuthTest.php (2)

143-143: Investigate the need for sleep in the setup method.

The sleep(1) call at the end of the setup method suggests there might be a race condition or timing issue with the created resources. Consider if this delay is necessary or if there's a more deterministic way to ensure resources are ready, such as waiting for specific conditions or implementing a retry mechanism.


250-251: Consider a more specific assertion for deletion response.

The assertion assertIsNotArray is somewhat vague. Since you're testing a successful deletion response, consider using more specific assertions that directly validate the expected response structure or status code.

-        $this->assertIsNotArray($row['body']);
+        $this->assertEmpty($row['body']); // Or more specifically check for an expected structure
         $this->assertEquals(204, $row['headers']['status-code']);
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php (1)

51-52: Update documentation reference to reflect tables instead of collections.

The SDK description points to the collections documentation instead of tables-specific documentation. Update the documentation reference to point to the appropriate table documentation.

-                description: '/docs/references/databases/create-collection.md',
+                description: '/docs/references/databases/create-table.md',
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php (2)

50-51: Update documentation reference to reflect tables instead of collections.

The SDK description points to the collections documentation instead of tables-specific documentation. Update the documentation reference to point to the appropriate table documentation.

-                description: '/docs/references/databases/update-collection.md',
+                description: '/docs/references/databases/update-table.md',

62-63: Fix parameter description to reference table instead of collection.

The parameter description refers to "Collection name" instead of "Table name", which is inconsistent with the class's purpose.

-            ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.')
+            ->param('name', null, new Text(128), 'Table name. Max length: 128 chars.')
tests/e2e/Services/GraphQL/Tables/AbuseTest.php (2)

32-32: Method name inconsistent with Tables API terminology.

The method name createCollection is called here but creates tables with the new Tables API. This mismatch in terminology could cause confusion for developers maintaining these tests.

Consider renaming the helper method to createTable to align with the Tables API terminology:

- $data = $this->createCollection();
+ $data = $this->createTable();

178-178: Add a comment explaining the sleep.

The sleep is necessary for eventual consistency but lacks a comment explaining its purpose.

Add a comment to explain why this sleep is necessary:

-        sleep(2);
+        // Sleep to ensure the column is fully indexed and available for queries
+        sleep(2);
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1)

18-18: Add more detailed documentation for abstract method.

The abstract method getResponseModel() lacks detailed documentation about what implementing classes should return.

Enhance the documentation to provide more guidance for implementers:

/**
 * Get the response model used in the SDK and HTTP responses.
+ *
+ * @return string The fully qualified class name of the response model to use
+ * for this action. This model will be used in both SDK code generation
+ * and HTTP responses.
 */
abstract protected function getResponseModel(): string;
tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (2)

53-54: Assertion parameter order is inconsistent.

The parameter order in this assertion puts the actual value first, unlike other assertions in the codebase that put expected value first.

- $this->assertEquals($movies['body']['name'], 'Movies');
+ $this->assertEquals('Movies', $movies['body']['name']);

226-227: Remove extra blank lines.

Multiple consecutive blank lines where one would be sufficient.

        ]);



-
-
        $this->assertEquals(200, $response['headers']['status-code']);
+
+        $this->assertEquals(200, $response['headers']['status-code']);
tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php (2)

155-155: Add a comment explaining the sleep.

The sleep is necessary for eventual consistency but lacks a comment explaining its purpose.

-        sleep(1);
+        // Sleep to ensure columns are fully indexed and available for row creation
+        sleep(1);

278-278: Improve assertion robustness.

Using assertStringContainsString on JSON data is brittle as it depends on the exact string representation.

Consider decoding the JSON data and making a more precise assertion:

-        $this->assertStringContainsString('New Row Name', $row['data']);
+        $decodedData = json_decode($row['data'], true);
+        $this->assertEquals('New Row Name', $decodedData['name']);
tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php (3)

20-72: Consider renaming method to better reflect its purpose

The method createCollection() is actually creating tables, not collections. This naming mismatch might create confusion when the code is maintained or reviewed later.

Rename the method to createTable() for clarity, or update the internal variable names to be consistent with the Tables API terminology.


51-52: Use consistent terminology in variable names

The variables publicCollection and privateCollection store table IDs, not collection IDs. This inconsistent naming might lead to confusion.

-        $publicCollection = ['id' => $publicMovies['body']['$id']];
-        $privateCollection = ['id' => $privateMovies['body']['$id']];
+        $publicTable = ['id' => $publicMovies['body']['$id']];
+        $privateTable = ['id' => $privateMovies['body']['$id']];

And update all references to these variables accordingly.


68-70: Align return variable names with Tables API terminology

The return array uses publicCollectionId and privateCollectionId even though they refer to table IDs, not collection IDs.

-            'publicCollectionId' => $publicCollection['id'],
-            'privateCollectionId' => $privateCollection['id'],
+            'publicTableId' => $publicTable['id'],
+            'privateTableId' => $privateTable['id'],

And update all references to these variables accordingly.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php (1)

32-33: Remove or address the TODO comment

The comment "we should correctly & carefully set the context later" suggests incomplete implementation or a reminder for future work. Either implement the suggested change or remove the comment if it's no longer applicable.

tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php (1)

150-152: Avoid fixed sleep() – replace with polling for eventual consistency

Hard-coded sleeps (1–3 s) slow the suite and are flaky under load.
Prefer a helper that polls the API until the column status is available or a timeout occurs.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c542ba0 and ba26eae.

📒 Files selected for processing (27)
  • src/Appwrite/Event/Realtime.php (3 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php (1 hunks)
  • src/Appwrite/Utopia/Response/Model/Table.php (1 hunks)
  • tests/e2e/General/AbuseTest.php (7 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/Base.php (23 hunks)
  • tests/e2e/Services/GraphQL/Collections/AbuseTest.php (2 hunks)
  • tests/e2e/Services/GraphQL/Collections/AuthTest.php (4 hunks)
  • tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php (7 hunks)
  • tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php (30 hunks)
  • tests/e2e/Services/GraphQL/Tables/AbuseTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/Tables/AuthTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/Tables/DatabaseClientTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/Tables/DatabaseServerTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/Appwrite/Event/Realtime.php
  • tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php
  • tests/e2e/General/AbuseTest.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php
🧰 Additional context used
🧠 Learnings (1)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1)
Learnt from: ItzNotABug
PR: appwrite/appwrite#9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.012Z
Learning: The class `Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Action` defines a method `getGrantParentNotFoundException()` which is used to handle cases where a parent resource (like a database collection) is not found.
🧬 Code Graph Analysis (4)
tests/e2e/Services/GraphQL/Collections/AuthTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-317)
tests/e2e/Services/GraphQL/Tables/AbuseTest.php (4)
tests/e2e/Client.php (1)
  • Client (8-317)
tests/e2e/Services/GraphQL/Collections/AbuseTest.php (6)
  • AbuseTest (15-185)
  • setUp (21-28)
  • testRateLimitEnforced (30-63)
  • createCollection (117-184)
  • testComplexQueryBlocked (65-97)
  • testTooManyQueriesBlocked (99-115)
tests/e2e/Services/GraphQL/Base.php (1)
  • getQuery (482-2938)
src/Appwrite/Utopia/Request.php (1)
  • getHeaders (147-169)
src/Appwrite/Utopia/Response/Model/Table.php (13)
src/Appwrite/Utopia/Response.php (1)
  • Response (151-963)
src/Appwrite/Event/Database.php (1)
  • Database (9-186)
src/Appwrite/Utopia/Response/Model/Document.php (1)
  • Document (8-92)
src/Appwrite/Utopia/Response/Model/ColumnIndex.php (3)
  • getName (70-73)
  • getType (78-81)
  • filter (83-93)
src/Appwrite/Utopia/Response/Model/Index.php (2)
  • getName (69-72)
  • getType (77-80)
src/Appwrite/Utopia/Response/Model/ColumnRelationship.php (3)
  • getName (63-66)
  • getType (73-76)
  • filter (83-95)
src/Appwrite/Utopia/Response/Model/Column.php (2)
  • getName (71-74)
  • getType (81-84)
src/Appwrite/Utopia/Response/Model/ColumnBoolean.php (2)
  • getName (45-48)
  • getType (55-58)
src/Appwrite/Utopia/Response/Model/ColumnDatetime.php (2)
  • getName (53-56)
  • getType (63-66)
src/Appwrite/Utopia/Response/Model/ColumnEmail.php (1)
  • getType (62-65)
src/Appwrite/Utopia/Response/Model/ColumnEnum.php (1)
  • getType (69-72)
src/Appwrite/Utopia/Response/Model/ColumnFloat.php (1)
  • getType (69-72)
src/Appwrite/Utopia/Response/Model/ColumnIP.php (1)
  • getType (62-65)
tests/e2e/Services/Databases/Tables/DatabasesPermissionsGuestTest.php (4)
tests/e2e/Client.php (1)
  • Client (8-317)
tests/e2e/Services/Databases/Tables/DatabasesPermissionsScope.php (1)
  • getServerHeader (79-86)
tests/e2e/Services/Databases/Tables/DatabasesPermissionsMemberTest.php (2)
  • permissionsProvider (29-105)
  • testReadDocuments (199-270)
tests/e2e/Services/Databases/Tables/DatabasesPermissionsTeamTest.php (1)
  • testReadDocuments (167-181)
⏰ Context from checks skipped due to timeout of 90000ms (18)
  • GitHub Check: E2E Service Test (Webhooks)
  • GitHub Check: E2E Service Test (Proxy)
  • GitHub Check: E2E Service Test (Teams)
  • GitHub Check: E2E Service Test (Users)
  • GitHub Check: E2E Service Test (Messaging)
  • GitHub Check: E2E Service Test (Migrations)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (Storage)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (Databases)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E General Test
  • GitHub Check: Benchmark
🔇 Additional comments (30)
tests/e2e/Services/GraphQL/Collections/AbuseTest.php (3)

3-3: Namespace updated to reflect collections module structure.

The namespace has been updated from Tests\E2E\Services\GraphQL to Tests\E2E\Services\GraphQL\Collections, which aligns with the modularization of collections-related tests in the PR. This change improves code organization by properly categorizing tests by their domain.


9-9: Base trait properly imported for shared functionality.

Adding the import for Tests\E2E\Services\GraphQL\Base allows the test to reuse common GraphQL testing functionality shared between Collections and Tables tests, supporting the dual API structure introduced in this PR.


117-117: Type declaration added to improve code safety.

Adding the return type declaration : array to the createCollection method improves type safety and code readability, making it clear that this method returns an array containing the created database and collection IDs.

tests/e2e/Services/GraphQL/Collections/AuthTest.php (3)

3-3: Namespace updated to reflect collections module structure.

The namespace has been updated from Tests\E2E\Services\GraphQL to Tests\E2E\Services\GraphQL\Collections, which aligns with the modularization of collections-related tests in the PR.


9-9: Base trait properly imported for shared functionality.

Adding the import for Tests\E2E\Services\GraphQL\Base allows the test to reuse common GraphQL testing functionality shared between Collections and Tables tests.


182-182:

✅ Verification successful

GraphQL schema key updated from databases to collections terminology.

The response key references have been updated from a databases* prefix to a collections* prefix (collectionsCreateDocument, collectionsGetDocument), which aligns with the new terminology introduced in this PR. This ensures the tests correctly validate the response structure from the updated GraphQL schema.

Let's verify that this naming convention change is consistent with the GraphQL schema definition:

Also applies to: 191-191, 241-241


🏁 Script executed:

#!/bin/bash
# Check if the GraphQL schema uses the new collections terminology
rg -A 3 "type.*Collections|type.*Document" --type php

Length of output: 2317


🏁 Script executed:

#!/bin/bash
# Search for GraphQL schema keys for create/get document in collections or databases
rg -n "collectionsCreateDocument"
rg -n "databasesCreateDocument"
rg -n "collectionsGetDocument"
rg -n "databasesGetDocument"

# Search for GraphQL field definitions referencing these keys
rg -n "->field('collectionsCreateDocument'"
rg -n "->field('databasesCreateDocument'"
rg -n "->field('collectionsGetDocument'"
rg -n "->field('databasesGetDocument'"

# List GraphQL schema files by extension
fd -e graphql
fd -e gql

Length of output: 3368


🏁 Script executed:

#!/bin/bash
set -e
# Search for GraphQL usage in code files (excluding tests)
rg -n "GraphQL" --type php | grep -v "tests/e2e" || true

# List directories with GraphQL in name
fd -t d GraphQL || true

# Search for field definitions for createDocument & getDocument including database prefix
rg -n "CreateDocument" --type php | grep -E "databases|collections" || true
rg -n "GetDocument" --type php | grep -E "databases|collections" || true

Length of output: 10510


Approve GraphQL terminology update
All GraphQL operation names have been correctly renamed from databasesCreateDocument/databasesGetDocument to collectionsCreateDocument/collectionsGetDocument. There are no remaining databases* keys in tests or schema code.

Affected files:

  • tests/e2e/Services/GraphQL/Collections/AuthTest.php (lines 182, 191, 241)
  • tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php (lines 185, 245)
  • tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php (lines 878, 1179)
  • tests/e2e/Services/GraphQL/Base.php (lines 1075, 1096)
tests/e2e/Services/GraphQL/Collections/DatabaseServerTest.php (3)

3-3: Namespace updated to reflect collections module structure.

The namespace has been updated from Tests\E2E\Services\GraphQL to Tests\E2E\Services\GraphQL\Collections, which aligns with the modularization of collections-related tests in the PR.


10-10: Base trait properly imported for shared functionality.

Adding the import for Tests\E2E\Services\GraphQL\Base allows the test to reuse common GraphQL testing functionality shared between Collections and Tables tests.


139-140: GraphQL schema references updated from databases to collections terminology.

Multiple assertions throughout the file have been updated to use the collections-prefixed response keys instead of the databases-prefixed keys, aligning with the new terminology introduced in this PR. This comprehensive update ensures all test assertions correctly validate the responses from the updated GraphQL schema.

Also applies to: 172-174, 242-247, 310-312, 380-385, 449-451, 526-530, 594-596, 724-726, 791-793, 827-827, 832-832, 878-878, 1046-1046, 1073-1073, 1099-1099, 1126-1126, 1152-1152, 1179-1179, 1310-1310

tests/e2e/Services/GraphQL/Collections/DatabaseClientTest.php (3)

3-3: Namespace updated to reflect collections module structure.

The namespace has been updated from Tests\E2E\Services\GraphQL to Tests\E2E\Services\GraphQL\Collections, which aligns with the modularization of collections-related tests in the PR.


9-9: Base trait properly imported for shared functionality.

Adding the import for Tests\E2E\Services\GraphQL\Base allows the test to reuse common GraphQL testing functionality shared between Collections and Tables tests.


112-112: GraphQL schema references updated from databases to collections terminology.

Multiple assertions throughout the file have been updated to use the collections-prefixed response keys instead of the databases-prefixed keys, aligning with the new terminology introduced in this PR. This comprehensive update ensures all test assertions correctly validate the responses from the updated GraphQL schema.

Also applies to: 144-144, 185-185, 218-218, 245-245, 275-275

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php (1)

72-156: Code handles attribute deletion properly with comprehensive validation

The implementation correctly:

  • Validates database, collection, and attribute existence
  • Checks for index dependencies before allowing deletion
  • Handles relationship attributes and their two-way relationships
  • Updates attribute status and purges cached documents
  • Adapts database actions based on current API context (Collections or Tables)
  • Sends appropriate events with parameters for both context types

This is a robust implementation with good error handling.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php (1)

8-138: Well-designed abstraction for context-aware document operations

This abstract class provides a solid foundation for handling both Collections API and Tables API operations with the same codebase. The context-switching methods and exception mapping are clean and effective.

Key strengths:

  • Clear distinction between contexts with constants
  • Type-safe context setting with validation
  • Consistent exception mapping based on current context
  • Centralized SDK namespace and group handling
  • Protected helper methods that encapsulate context-specific logic

This approach will simplify maintenance as both APIs evolve.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php (1)

86-232: Document update implementation handles permissions and relationships properly

The implementation correctly:

  • Validates input data and permissions
  • Checks authorization for managing permissions
  • Maintains existing permissions when none are provided
  • Processes nested relationships recursively
  • Tracks operation count for metrics
  • Sets appropriate headers for debugging

The approach to handling nested document relationships is thorough and supports both single relationships and arrays of related documents.

tests/e2e/Services/GraphQL/Base.php (10)

58-84: Well-structured column constants parallel to attribute constants

The column-related constants are implemented as direct parallels to the existing attribute constants, facilitating the transition to the new Tables/Columns terminology while maintaining consistency with the existing Collections/Attributes API.


85-96: Clean separation of index operations for both APIs

The clear separation between attribute indexes and column indexes in the constants will make it easier for developers to understand which API they're working with, while maintaining consistent operation naming across both APIs.


104-110: Row operations mirror document operations

The new row operations constants match the existing document operations, providing a consistent API experience across both terminologies.


386-459: Column fragments mirror attribute fragments structure

The column fragments implementation provides the same structure and functionality as the attribute fragments, ensuring consistent GraphQL query capabilities across both APIs.


567-611: Tables GraphQL queries mirror Collections queries

The implementation of table queries follows the same pattern as collection queries, ensuring consistency across both APIs and making it easier for developers to transition between them.


788-963: Comprehensive column operations mirror attribute operations

The column operations for all data types (string, integer, float, etc.) are implemented to match their attribute counterparts, ensuring feature parity between the two APIs.


997-1029: Column index operations mirror attribute index operations

The column index operations follow the same pattern as attribute index operations, with appropriate naming changes to reflect the new terminology.


1051-1071: Tables column operations mirror collection attribute operations

The implementation of column list and get operations use the column fragments in the same way that attribute operations use attribute fragments, maintaining consistency between the APIs.


1102-1130: Row operations mirror document operations

The row operations (get, list, create) are implemented to mirror document operations, ensuring consistency between the two APIs and facilitating developer transition.


1204-1217: Row update and delete match document operations

The row update and delete operations match their document counterparts, completing the parallel implementation between the two APIs.

tests/e2e/Services/Databases/Tables/DatabasesCustomClientTest.php (5)

20-108: Comprehensive test for permission mapping

This test thoroughly validates how permissions are propagated from the table level to row level, confirming that:

  1. write permission at table level converts to create, update, and delete
  2. write permission at row level converts to only update and delete (not create)
  3. Trying to add create permission to a row returns an error

The test effectively verifies the security model is working as expected.


110-227: Thorough permission validation test

This test validates an important permission behavior: when a row has been created by a server-side operation, a client should be able to update it without needing to explicitly adjust permissions. The test:

  1. Creates database, table, and adds a column
  2. Creates a row with server credentials with specific permissions
  3. Verifies a client can update the row without having update permission
  4. Properly cleans up resources afterwards

The test includes appropriate sleep intervals to wait for asynchronous database workers to complete their tasks.


229-318: Two-way relationship test with onDelete behavior

This test confirms that a two-way relationship between tables can be properly established and updated, specifically verifying that the onDelete behavior can be changed from "cascade" to "restrict" and is correctly reflected in both related tables.


320-476: Comprehensive relationship constraints test

This test verifies important constraints for relationships between tables:

  1. Duplicate twoWayKey values are rejected with appropriate error messages
  2. Default twoWayKey generation works when omitted
  3. Multiple many-to-many relationships on the same table are correctly restricted

The test includes appropriate validation of response status codes and error messages.


478-892: Thorough relationship permission test

This extensive test verifies permission behavior for operations involving relationships:

  1. Tests updating nested relationships with and without proper permissions
  2. Validates that permission checks are properly applied to each level of related entities
  3. Tests linking of existing and newly created related entities
  4. Verifies proper error codes when permissions are missing

The test establishes a complex hierarchy of related tables with varying permission settings to thoroughly test all scenarios.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (2)

129-129: Variable name inconsistent with Tables API terminology.

The variable name uses "Collection" terminology but is used with table operations.

- $moviesCollectionId = $data['moviesId'];
+ $moviesTableId = $data['moviesId'];

154-154: Variable name inconsistent with Tables API terminology.

The variable name uses "Collection" terminology but is used with table operations.

- $moviesCollectionId = $data['moviesId'];
+ $moviesTableId = $data['moviesId'];
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (1)

122-133: Permission::aggregate() may receive null – call after the fallback is built

Permission::aggregate() is executed before the null‐check. If the helper expects an array (as it does in other Appwrite helpers) it will throw a type error when permissions is omitted from the request.

-        // Map aggregate permissions to into the set of individual permissions they represent.
-        $permissions = Permission::aggregate($permissions, $allowedPermissions);
-
-        // Add permissions for current the user if none were provided.
-        if (\is_null($permissions)) {
+        // Add permissions for the current user if none were provided.
+        if (\is_null($permissions)) {
             $permissions = [];
             if (!empty($user->getId())) {
                 foreach ($allowedPermissions as $permission) {
                     $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
                 }
             }
         }
+
+        // Map aggregate permissions into the individual permissions they represent.
+        $permissions = Permission::aggregate($permissions, $allowedPermissions);
tests/e2e/Services/GraphQL/Base.php (1)

28-29: Constants now use consistent singular form.

The UPDATE_TABLE and DELETE_TABLE constants are now correctly using the singular form ('update_table' and 'delete_table') which aligns with the other operation constants in the codebase.

🧹 Nitpick comments (11)
tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (4)

19-98: Method implementation looks good, but variable naming in return value could be improved.

The testCreateTable method effectively tests table creation under various conditions, including when the database is disabled. However, the return value on line 97 could be more consistent with the Tables API terminology.

- return ['moviesId' => $movies['body']['$id'], 'databaseId' => $databaseId, 'tvShowsId' => $tvShows['body']['$id']];
+ return ['tableId' => $movies['body']['$id'], 'databaseId' => $databaseId, 'tvShowsTableId' => $tvShows['body']['$id']];

This would require updating the variable names in dependent test methods.


159-159: Update method parameter to match variable naming.

For consistency, update the parameter name in the API call to match the variable name.

- $table = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $moviesCollectionId, array_merge([
+ $table = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/tables/' . $moviesTableId, array_merge([

257-257: Update variable name in the request endpoint.

The endpoint uses "randomCollectionId" which is inconsistent with the Tables API terminology.

- $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/randomCollectionId/usage', array_merge([
+ $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/randomTableId/usage', array_merge([

134-142: Use more descriptive variable name.

The method correctly tests retrieving a specific table when both database and table are disabled, but consider using a more descriptive variable name.

- $table = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $moviesCollectionId, array_merge([
+ $moviesTable = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $moviesTableId, array_merge([

Then update the assertions accordingly.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (2)

125-126: Fix grammar in comment

There's a grammatical error in the comment.

-        // Add permissions for current the user if none were provided.
+        // Add permissions for the current user if none were provided.

301-318: Clearly document the purpose of sensitive relationships marking

The code marks relationships as sensitive in the event payload, but doesn't provide a comment explaining why this is necessary or what the implications are.

Consider adding a clarifying comment before the event queuing code:

        $relationships = \array_map(
            fn ($document) => $document->getAttribute('key'),
            \array_filter(
                $collection->getAttribute('attributes', []),
                fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
            )
        );

+        // Mark relationship fields as sensitive to prevent exposing potentially private related document data
+        // in events and notifications
        $queueForEvents
            ->setParam('databaseId', $databaseId)
            ->setContext('database', $database)
            ->setParam('collectionId', $collection->getId())
            ->setParam('tableId', $collection->getId())
            ->setParam('documentId', $document->getId())
            ->setParam('rowId', $document->getId())
            ->setPayload($response->getPayload(), sensitive: $relationships)
            ->setContext($this->getCollectionsEventsContext(), $collection);
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php (2)

91-118: Add validation for empty cursorDocument array

The code could handle the case where $cursorDocument is an empty array more explicitly, though it does check this with empty($cursorDocument). A slightly more robust approach would be to check the array size before accessing index 0.

            $indexId = $cursor->getValue();
            $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [
                Query::equal('collectionInternalId', [$collection->getInternalId()]),
                Query::equal('databaseInternalId', [$database->getInternalId()]),
                Query::equal('key', [$indexId]),
                Query::limit(1)
            ]));

-            if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) {
+            if (empty($cursorDocument) || count($cursorDocument) === 0 || $cursorDocument[0]->isEmpty()) {
                throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Index '{$indexId}' for the 'cursor' value not found.");
            }

            $cursor->setValue($cursorDocument[0]);

126-127: Improve variable naming for collection/table-specific error message components

The variables used for constructing error messages are named with singular forms, but they represent plural concepts in the message. This could be a source of confusion when reading the code.

-            $documents = $this->isCollectionsAPI() ? 'documents' : 'rows';
-            $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column';
+            $documentsOrRows = $this->isCollectionsAPI() ? 'documents' : 'rows';
+            $attributeOrColumn = $this->isCollectionsAPI() ? 'attribute' : 'column';
-            $message = "The order $attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documents order $attribute values are non-null.";
+            $message = "The order $attributeOrColumn '{$e->getAttribute()}' had a null value. Cursor pagination requires all $documentsOrRows order $attributeOrColumn values are non-null.";
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (3)

27-29: Consider using constant values in error message

The error message in the setContext method uses string literals instead of the constant names themselves, which could lead to inconsistency if the constant values change.

        if (!\in_array($context, [DATABASE_INDEX_CONTEXT, DATABASE_COLUMN_INDEX_CONTEXT], true)) {
-            throw new \InvalidArgumentException("Invalid context '$context'. Must be either `DATABASE_COLUMN_INDEX_CONTEXT` or `DATABASE_INDEX_CONTEXT`.");
+            throw new \InvalidArgumentException("Invalid context '$context'. Must be either DATABASE_COLUMN_INDEX_CONTEXT or DATABASE_INDEX_CONTEXT.");
        }

10-14: Document default context value with a comment

The class initializes $context with DATABASE_INDEX_CONTEXT, but it's not immediately clear why this is the default. Adding a comment would clarify the design decision.

    /**
     * The current API context (either 'columnIndex' or 'index').
+     * 
+     * Defaults to DATABASE_INDEX_CONTEXT for compatibility with the collections API.
     */
    private ?string $context = DATABASE_INDEX_CONTEXT;

156-163: Rename method for clarity

The method getCollectionsEventsContext() might be confusing since it returns either "collection" or "table". A more accurate name would better reflect its purpose.

    /**
-     * Get the correct collections context for Events queue.
+     * Get the context string for Events queue (either "collection" or "table").
     */
-    final protected function getCollectionsEventsContext(): string
+    final protected function getEventContextString(): string
    {
        return $this->isCollectionsAPI() ? 'collection' : 'table';
    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba26eae and b0d9215.

📒 Files selected for processing (10)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php (1 hunks)
  • tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/Base.php (29 hunks)
  • tests/e2e/Services/GraphQL/Collections/AbuseTest.php (3 hunks)
  • tests/e2e/Services/GraphQL/Tables/AbuseTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/e2e/Services/GraphQL/Collections/AbuseTest.php
  • tests/e2e/Services/GraphQL/Tables/AbuseTest.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php
🧰 Additional context used
🧠 Learnings (1)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1)
Learnt from: ItzNotABug
PR: appwrite/appwrite#9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.012Z
Learning: The class `Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Action` defines a method `getGrantParentNotFoundException()` which is used to handle cases where a parent resource (like a database collection) is not found.
🧬 Code Graph Analysis (1)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (14)
src/Appwrite/Platform/Modules/Databases/Workers/Databases.php (2)
  • Databases (21-630)
  • action (54-85)
src/Appwrite/Event/Event.php (3)
  • Event (10-600)
  • reset (389-395)
  • setPayload (206-215)
src/Appwrite/Extend/Exception.php (1)
  • Exception (7-439)
src/Appwrite/SDK/Method.php (1)
  • Method (9-295)
src/Appwrite/Utopia/Response.php (2)
  • Response (151-963)
  • dynamic (671-704)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php (3)
  • Create (27-232)
  • getResponseModel (36-39)
  • action (80-231)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Create.php (1)
  • Create (19-72)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Rows/Create.php (1)
  • Create (19-78)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (4)
  • Action (8-163)
  • getResponseModel (18-18)
  • getContext (47-50)
  • setContext (25-32)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (4)
  • Action (25-627)
  • getResponseModel (35-35)
  • getContext (54-57)
  • setContext (42-49)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php (2)
  • getResponseModel (33-36)
  • action (74-125)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php (2)
  • getResponseModel (35-38)
  • action (80-175)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php (2)
  • getResponseModel (39-42)
  • action (86-306)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php (2)
  • getResponseModel (34-37)
  • action (72-156)
⏰ Context from checks skipped due to timeout of 90000ms (18)
  • GitHub Check: E2E Service Test (Migrations)
  • GitHub Check: E2E Service Test (VCS)
  • GitHub Check: E2E Service Test (Tokens)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (Webhooks)
  • GitHub Check: E2E Service Test (Storage)
  • GitHub Check: E2E Service Test (Teams)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (Health)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Projects)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: E2E Service Test (Databases)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: E2E General Test
🔇 Additional comments (16)
tests/e2e/Services/Databases/Tables/DatabasesConsoleClientTest.php (4)

105-119: Test method looks good.

The method correctly tests listing tables when the database is disabled and verifies the expected response.


196-235: Test method for database usage looks good.

The method correctly tests both success and failure cases for retrieving database usage statistics. It verifies invalid range parameters return 400 status code and valid parameters return 200 with appropriate response structure.


237-280: Test method for table usage looks good.

The method correctly tests both success and failure cases for retrieving table usage statistics, including non-existent table IDs. It verifies the response structure and data types.


282-335: Test method for table logs looks good.

The method effectively tests retrieving table logs with various query parameters, including limit and offset. It verifies the response structure and ensures proper pagination.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php (1)

54-54:

❌ Incorrect review comment

Audit label is inconsistent with the documents API

audits.event is set to document.create, but based on the context of this PR which introduces both collections/documents and tables/rows terminology, this might require dynamic context handling similar to other parts of the code.


🏁 Script executed:

#!/bin/bash
# Search for other audit event labels in the codebase to check for consistency
echo "Searching for audit event labels in Collections and Tables contexts..."
rg --no-filename "audits\.event.*document\." src/
echo "---------"
rg --no-filename "audits\.event.*row\." src/

Length of output: 591


Audit label matches controller context – no change needed

I verified that the Collections controllers consistently use document.* audit labels (document.create, document.update, document.delete) and the Tables controllers use row.* labels (row.create, row.update, row.delete). The existing approach already scopes labels by context, so no dynamic handling is required here. Please disregard this comment.

Likely an incorrect or invalid review comment.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php (1)

89-94: Method name correction implemented

The method name getGrandParentNotFoundException() has been properly implemented, addressing the previously identified typo where it was incorrectly named getGrantParentNotFoundException().

tests/e2e/Services/GraphQL/Base.php (10)

24-29: The Tables API constants match the Collections API pattern.

The new constants for Tables operations follow the same naming pattern as the existing Collections API constants, ensuring consistency in the codebase. This parallel structure will make it easier for developers to work with both APIs.


58-83: Column operations mirror attribute operations consistently.

The Columns API operation constants maintain the same structure and naming convention as their Attribute counterparts, providing a consistent parallel between the two APIs. Each column type (string, integer, float, boolean, URL, email, IP, enum, datetime, relationship) has corresponding create and update operations.


91-96: Column Index operations follow established pattern.

The Column Index constants follow the same pattern as Attribute Indexes, maintaining consistency throughout the codebase.


104-109: Row operations mirror document operations appropriately.

The Row operations constants follow the same pattern as their Document counterparts, establishing a clear parallel between the two APIs.


387-460: Column fragments mirror attribute fragments consistently.

The new column fragments have the same structure as their attribute counterparts, ensuring a consistent representation of column types in GraphQL responses. This parallel implementation will make it easier for clients to adapt to the new Tables API.


568-612: Table queries maintain consistent structure with Collection queries.

The GraphQL queries for Table operations mirror their Collection counterparts in structure and naming conventions, ensuring a consistent API experience.


789-964: Column operation queries follow the same pattern as attributes.

The GraphQL queries for Column operations maintain the same structure and behavior as their Attribute counterparts, with appropriate renaming from "collection" to "table" and "attribute" to "column".


2704-2704: Added databaseId field to Collection response matches Tables implementation.

The addition of the databaseId field to the Collection queries ensures consistency with the new Tables implementation, which also includes this field.


2936-3178: Complex query implementation for tables mirrors collections.

The complex query for tables follows the same pattern as the collections version, creating a complete testing environment with database, table, columns, and users. This provides comprehensive test coverage for the new Tables API.


310-310: Split complex query operations for collections and tables.

The complex query operation has been appropriately split into two variants: one for collections and one for tables, allowing for specific testing of each API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy