Skip to content

Feat multi create#10894

Merged
abnegate merged 16 commits into1.8.xfrom
feat-multi-create
Dec 8, 2025
Merged

Feat multi create#10894
abnegate merged 16 commits into1.8.xfrom
feat-multi-create

Conversation

@abnegate
Copy link
Copy Markdown
Member

@abnegate abnegate commented Dec 3, 2025

What does this PR do?

Allow passing attributes and indexes when creating a collection, to create the entire schema synchronously

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Screenshots may also be helpful.)

Related PRs and Issues

  • (Related PR or issue)

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?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 3, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (12)
  • app/config/specs/open-api3-1.8.x-client.json is excluded by !app/config/specs/**
  • app/config/specs/open-api3-1.8.x-console.json is excluded by !app/config/specs/**
  • app/config/specs/open-api3-1.8.x-server.json is excluded by !app/config/specs/**
  • app/config/specs/open-api3-latest-client.json is excluded by !app/config/specs/**
  • app/config/specs/open-api3-latest-console.json is excluded by !app/config/specs/**
  • app/config/specs/open-api3-latest-server.json is excluded by !app/config/specs/**
  • app/config/specs/swagger2-1.8.x-client.json is excluded by !app/config/specs/**
  • app/config/specs/swagger2-1.8.x-console.json is excluded by !app/config/specs/**
  • app/config/specs/swagger2-1.8.x-server.json is excluded by !app/config/specs/**
  • app/config/specs/swagger2-latest-client.json is excluded by !app/config/specs/**
  • app/config/specs/swagger2-latest-console.json is excluded by !app/config/specs/**
  • app/config/specs/swagger2-latest-server.json is excluded by !app/config/specs/**

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This PR adds inline schema validation for collection/table creation and introduces two new validators: Appwrite\Utopia\Database\Validator\Attributes and Appwrite\Utopia\Database\Validator\Indexes. The Collections Create flow now accepts attributes and indexes, validates them, builds attribute/index documents, checks index viability, persists them after collection creation, and performs cleanup on failures. Six context-aware exception helper methods were added to the Collections Action class. The Tables DB Create endpoint gained columns/indexes request parameter declarations. Extensive end-to-end tests for attributes, indexes, enums, datetimes, floats, relationships, spatial types, and cleanup were added.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

  • Review Attributes validator: many branches for types, formats, enums, defaults, ranges, and spatial support.
  • Review Indexes validator: attributes/lengths/orders alignment, supported index types, and duplicate key checks.
  • Inspect Collections Create flow: new parameters, validation ordering, index viability checks, buildAttributeDocument/buildIndexDocument logic, persistence sequencing, and cleanup/error handling paths.
  • Verify new exception helper methods in Action are correct and consistently used.
  • Review added endpoint parameter declarations in Tables DB Create for correctness and compatibility with Collections behavior.
  • Spot-check the extensive e2e tests for duplication and ensure they match validator and create behaviors.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title "Feat multi create" is vague and doesn't clearly convey what the feature accomplishes, using non-specific phrasing that obscures the main change from scanning history. Revise title to be more descriptive, e.g., "Add support for creating collection attributes and indexes during collection creation" or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly states the change intent: allowing attributes and indexes to be passed when creating a collection for synchronous schema creation, which directly relates to the changeset.

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 3, 2025

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
imagemagick 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-c++ 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-dev 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-heic 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-jpeg 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-jxl 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-libs 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-openexr 7.1.2.3-r0 CVE-2025-62171 HIGH
imagemagick-webp 7.1.2.3-r0 CVE-2025-62171 HIGH
libecpg 17.6-r0 CVE-2025-12818 HIGH
libecpg-dev 17.6-r0 CVE-2025-12818 HIGH
libpng 1.6.47-r0 CVE-2025-64720 HIGH
libpng 1.6.47-r0 CVE-2025-65018 HIGH
libpng-dev 1.6.47-r0 CVE-2025-64720 HIGH
libpng-dev 1.6.47-r0 CVE-2025-65018 HIGH
libpq 17.6-r0 CVE-2025-12818 HIGH
libpq-dev 17.6-r0 CVE-2025-12818 HIGH
libxml2 2.13.8-r0 CVE-2025-49794 CRITICAL
libxml2 2.13.8-r0 CVE-2025-49796 CRITICAL
libxml2 2.13.8-r0 CVE-2025-49795 HIGH
libxml2 2.13.8-r0 CVE-2025-6021 HIGH
postgresql17-dev 17.6-r0 CVE-2025-12818 HIGH
github.com/containerd/containerd/v2 v2.0.2 CVE-2024-25621 HIGH
golang.org/x/crypto v0.31.0 CVE-2025-22869 HIGH
golang.org/x/oauth2 v0.24.0 CVE-2025-22868 HIGH
stdlib 1.22.10 CVE-2025-47907 HIGH
stdlib 1.22.10 CVE-2025-58183 HIGH
stdlib 1.22.10 CVE-2025-58186 HIGH
stdlib 1.22.10 CVE-2025-58187 HIGH
stdlib 1.22.10 CVE-2025-61729 HIGH

Source Code Scan Results

🎉 No vulnerabilities found!

Copy link
Copy Markdown
Contributor

@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: 2

🧹 Nitpick comments (8)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php (1)

129-137: getParentNotFoundException() duplicates getNotFoundException().

Both methods return identical values (COLLECTION_NOT_FOUND / TABLE_NOT_FOUND). Consider reusing getNotFoundException() or removing one method to avoid confusion and maintenance overhead.

-    /**
-     * Get the appropriate parent level not found exception.
-     */
-    protected function getParentNotFoundException(): string
-    {
-        return $this->isCollectionsAPI()
-            ? Exception::COLLECTION_NOT_FOUND
-            : Exception::TABLE_NOT_FOUND;
-    }
+    /**
+     * Get the appropriate parent level not found exception.
+     */
+    protected function getParentNotFoundException(): string
+    {
+        return $this->getNotFoundException();
+    }
src/Appwrite/Utopia/Database/Validator/Attributes.php (2)

163-169: Consider validating enum elements are unique strings.

The enum validation checks for a non-empty elements array but doesn't verify that each element is a string or that elements are unique, which could lead to issues when creating the attribute in the database.

             // Validate enum elements if format is enum
             if (isset($attribute['format']) && $attribute['format'] === APP_DATABASE_ATTRIBUTE_ENUM) {
                 if (!isset($attribute['elements']) || !is_array($attribute['elements']) || empty($attribute['elements'])) {
                     $this->message = "Attribute '" . $attribute['key'] . "' with enum format must have 'elements' array";
                     return false;
                 }
+                foreach ($attribute['elements'] as $element) {
+                    if (!is_string($element)) {
+                        $this->message = "Attribute '" . $attribute['key'] . "' enum elements must be strings";
+                        return false;
+                    }
+                }
+                if (count($attribute['elements']) !== count(array_unique($attribute['elements']))) {
+                    $this->message = "Attribute '" . $attribute['key'] . "' has duplicate enum elements";
+                    return false;
+                }
             }

151-161: Default value type is not validated against the attribute type.

The validator checks for conflicts between default and required/array, but does not verify that the default value's type matches the attribute's declared type. For example, { type: 'boolean', default: 'hello' } would pass validation.

Consider adding type validation for default values, or verify that downstream processing handles type mismatches gracefully.

tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (3)

6783-6959: Confirm async behavior assumptions for attributes/indexes created via collection create

These assertions assume that attributes and indexes are already in 'available' status immediately after the collection create (no sleep / assertEventually like the rest of this file uses for attribute/index creation). If the new multi-create flow still schedules schema work asynchronously, these expectations may be flaky on slower CI or under load.

If the implementation really does block until attributes and indexes are fully built, it would be good to confirm that and maybe add a brief comment here. Otherwise, consider wrapping the /attributes and /indexes checks in assertEventually to poll until all expected keys reach 'available' before asserting their metadata.


6960-7111: Nice negative coverage; consider asserting error types and one or two more edge cases

This error suite is useful and focused: invalid type, index referencing unknown attribute, required+default, duplicate keys, and a system-attribute index happy-path. Two optional polish ideas:

  • For at least one or two of these cases, also assert the error type (e.g. AppwriteException::GENERAL_ARGUMENT_INVALID / ATTRIBUTE_DEFAULT_UNSUPPORTED) to lock in the contract beyond just 400.
  • Optionally add one more index-schema check (e.g. mismatched attributes/orders length or an invalid type constant) to exercise the new Indexes validator a bit more.

Not blocking, but would strengthen regression detection around the new validators.


7113-7202: Enum attribute happy-path looks good; optional: add one creation-time failure case

The enum creation flow and the valid/invalid document checks look correct and align with the new "type" = string + "format" = enum + "elements" contract. If you want to harden this area further, you could add a single collection-create failure case here (e.g. enum with empty elements, or format = 'enum' but missing elements) so creation-time enum validation is exercised as well, not only via the update tests above.

Purely optional; current coverage is already solid.

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

122-136: Consider clarifying the variable naming for consistency.

The variable $collectionAttributes is later passed to buildIndexDocument as $attributeDocuments (line 133 → line 295). While functionally correct, the naming inconsistency can cause confusion during maintenance.

Consider renaming for clarity:

-        $collectionAttributes = [];
-        $attributeDocuments = [];
+        $attributesForCollection = [];
+        $attributeMetadataDocuments = [];
         foreach ($attributes as $attributeDef) {
             $attrDoc = $this->buildAttributeDocument($database, $collection, $attributeDef, $dbForProject);
-            $collectionAttributes[] = $attrDoc['collection'];
-            $attributeDocuments[] = $attrDoc['document'];
+            $attributesForCollection[] = $attrDoc['collection'];
+            $attributeMetadataDocuments[] = $attrDoc['document'];
         }

Then update the corresponding usages on lines 133, 141, and 157.


154-167: Consider aligning collection creation with the transactional pattern used for document operations.

The codebase uses withTransaction() for multi-step document operations (Upsert, Update, Delete, Create), but collection-level operations (Create, Update, Delete) do not. Since collection creation involves multiple dependent steps—creating the collection document, building the schema, and creating attribute/index metadata documents—consider whether wrapping these operations in a transaction would improve consistency safety, consistent with the pattern established for document operations.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 379b85b and c09c284.

📒 Files selected for processing (6)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php (1 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (4 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php (2 hunks)
  • src/Appwrite/Utopia/Database/Validator/Attributes.php (1 hunks)
  • src/Appwrite/Utopia/Database/Validator/Indexes.php (1 hunks)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-19T09:20:03.312Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php:57-59
Timestamp: 2025-06-19T09:20:03.312Z
Learning: In table-related endpoints (such as `src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php`), parameter descriptions should use "table" and "row" terminology instead of "collection" and "document" for clarity and consistency.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
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.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php
🧬 Code graph analysis (3)
src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)
  • Attributes (9-211)
src/Appwrite/Utopia/Database/Validator/Indexes.php (1)
  • Indexes (9-190)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php (2)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php (5)
  • getFormatUnsupportedException (165-170)
  • isCollectionsAPI (57-62)
  • getDefaultUnsupportedException (155-160)
  • getParentNotFoundException (85-90)
  • getStructureException (125-130)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php (3)
  • isCollectionsAPI (67-72)
  • getParentNotFoundException (103-108)
  • getStructureException (163-168)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-342)
🪛 GitHub Actions: Linter
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php

[error] 1-1: PSR-12: no_unused_imports violation detected by Pint. Remove unused imports.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: scan
🔇 Additional comments (10)
src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php (1)

10-12: LGTM!

The new columns and indexes parameters are properly defined with appropriate validators and optional defaults. The descriptions correctly use "column" terminology consistent with the Tables API context. Based on learnings, this aligns with the expected table/row terminology for TablesDB endpoints.

Also applies to: 65-66

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

108-127: LGTM!

The new exception helper methods follow the established pattern from sibling Action classes and provide consistent context-aware exception mapping between Collections and Tables APIs.

Also applies to: 139-167

src/Appwrite/Utopia/Database/Validator/Attributes.php (1)

1-46: LGTM!

The validator structure is well-designed with appropriate max limits, clear error messages, and proper implementation of the Validator interface. The isArray() returning false while getType() returns TYPE_ARRAY is the correct pattern for this framework.

Also applies to: 66-77, 197-210

src/Appwrite/Utopia/Database/Validator/Indexes.php (3)

1-38: LGTM!

The class setup, imports, and constructor are well-structured. The supported types and orders are correctly defined based on the Database constants.


58-168: Solid validation implementation.

The isValid method is thorough and covers all necessary validations:

  • Array type check
  • Max count enforcement
  • Required field presence
  • Key validation and duplicate detection
  • Type validation against supported types
  • Attribute validation including special system attributes
  • Optional orders and lengths validation

The error messages are descriptive and include positional context for debugging.


176-189: This is not an inconsistency. The isArray() and getType() methods serve different purposes. getType() returns the expected PHP data type (TYPE_ARRAY), while isArray() indicates framework-level validator behavior. The Operation validator (which returns isArray() = true with getType() = TYPE_OBJECT) confirms these are independent concerns. The Indexes validator correctly implements both methods according to the framework contract.

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

46-88: LGTM!

The constructor properly sets up the new attributes and indexes parameters with appropriate validators and documentation.


184-288: LGTM!

The buildAttributeDocument method is well-structured and handles various attribute types including strings, enums, ranges, and relationships. The validation for required/array defaults and format checks are appropriate.


295-364: LGTM!

The buildIndexDocument method correctly validates that indexed attributes exist, prevents indexing on relationship attributes, and handles array attribute length/order overrides appropriately.


148-149: The Exception constructor signature is verified as correct. The Appwrite\Extend\Exception class has the signature:

public function __construct(
    string $type = Exception::GENERAL_UNKNOWN,
    string $message = null,
    int|string $code = null,
    \Throwable $previous = null,
    ?string $view = null
)

The code at lines 148-149 correctly passes the error type as the first parameter and the message from the IndexException as the second parameter, which aligns with the constructor's expected signature. The message propagates properly through the constructor to the parent Exception class.

Comment thread src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php Outdated
Comment thread src/Appwrite/Utopia/Database/Validator/Attributes.php
@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 3, 2025

✨ Benchmark results

  • Requests per second: 1,182
  • Requests with 200 status code: 212,830
  • P99 latency: 0.163182765

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,182 1,264
200 212,830 227,631
P99 0.163182765 0.163505794

Copy link
Copy Markdown
Contributor

@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 (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)

136-142: Format validation should be restricted to string types.

String-specific formats (email, enum, ip, url) are validated for all attribute types. This allows invalid combinations like { type: 'integer', format: 'email' } to pass validation.

             // Validate format if provided
-            if (isset($attribute['format']) && $attribute['format'] !== '') {
+            if ($attribute['type'] === Database::VAR_STRING && isset($attribute['format']) && $attribute['format'] !== '') {
                 if (!in_array($attribute['format'], $this->supportedFormats)) {
                     $this->message = "Invalid format for attribute '" . $attribute['key'] . "': " . $attribute['format'];
                     return false;
                 }
             }
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (1)

22-22: Remove unused import causing pipeline failure.

StructureException is imported but never used in this file.

-use Utopia\Database\Exception\Structure as StructureException;
🧹 Nitpick comments (4)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)

6960-7590: Comprehensive negative‑path validation for attributes, enums, and indexes; consider data‑driven consolidation

The new tests around invalid attribute types/defaults, reserved keys, min/max constraints, enum element/default rules, index key/type/attributes/orders/lengths, and cleanup on failure are thorough and align with the behavior exercised by the per‑attribute tests earlier in this class. From a maintainability standpoint, there’s a fair bit of repetition in the “create DB → create collection with 1 attribute/index → assert 400/201” pattern; over time, you might want to move these cases to a data provider or small helper to keep this already‑large test class from growing harder to navigate. Functionally, the expectations look consistent.

src/Appwrite/Utopia/Database/Validator/Indexes.php (1)

183-201: Inconsistency between isArray() and getType() return values.

isArray() returns false while getType() returns self::TYPE_ARRAY. This is inconsistent—if the validator expects array input (which it does, per line 60), isArray() should return true.

     public function isArray(): bool
     {
-        return false;
+        return true;
     }
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)

355-368: Inconsistency between isArray() and getType() return values.

Same issue as in Indexes.php: isArray() returns false while getType() returns TYPE_ARRAY. The validator expects array input, so isArray() should return true.

     public function isArray(): bool
     {
-        return false;
+        return true;
     }
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (1)

331-339: Consider renaming parameter for clarity.

The $attributeDocuments parameter receives $collectionAttributes from the caller (line 149), which are the "collection" documents from buildAttributeDocument. The naming is confusing since the codebase uses "attributeDocuments" for storage documents and "collectionAttributes" for schema documents.

-    protected function buildIndexDocument(Document $database, Document $collection, array $indexDef, array $attributeDocuments, Database $dbForProject): array
+    protected function buildIndexDocument(Document $database, Document $collection, array $indexDef, array $collectionAttributes, Database $dbForProject): array
     {
         $key = $indexDef['key'];
         $type = $indexDef['type'];
         $indexAttributes = $indexDef['attributes'];
         $orders = $indexDef['orders'] ?? [];
         $lengths = $indexDef['lengths'] ?? [];
 
-        $attrKeys = array_map(fn ($a) => $a->getAttribute('key'), $attributeDocuments);
+        $attrKeys = array_map(fn ($a) => $a->getAttribute('key'), $collectionAttributes);

And update remaining references to use $collectionAttributes.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c09c284 and dce4c6e.

📒 Files selected for processing (4)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (4 hunks)
  • src/Appwrite/Utopia/Database/Validator/Attributes.php (1 hunks)
  • src/Appwrite/Utopia/Database/Validator/Indexes.php (1 hunks)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
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.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
📚 Learning: 2025-06-19T09:20:03.312Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php:57-59
Timestamp: 2025-06-19T09:20:03.312Z
Learning: In table-related endpoints (such as `src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php`), parameter descriptions should use "table" and "row" terminology instead of "collection" and "document" for clarity and consistency.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
🧬 Code graph analysis (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)
src/Appwrite/Utopia/Database/Validator/Indexes.php (5)
  • __construct (35-38)
  • getDescription (47-50)
  • isValid (58-181)
  • isArray (188-191)
  • getType (198-201)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (3)
  • Attributes (13-369)
  • isValid (70-348)
  • getDescription (59-62)
src/Appwrite/Utopia/Database/Validator/Indexes.php (3)
  • Indexes (9-202)
  • isValid (58-181)
  • getDescription (47-50)
🪛 GitHub Actions: Linter
src/Appwrite/Utopia/Database/Validator/Attributes.php

[error] 1-1: Pint: no_unused_imports violation detected.

src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php

[error] 1-1: Pint: no_unused_imports violation detected.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: E2E Service Test (VCS)
  • GitHub Check: E2E Service Test (Users)
  • GitHub Check: E2E Service Test (Tokens)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (Health)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Projects)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: Unit Test
  • GitHub Check: scan
🔇 Additional comments (8)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (2)

6783-6958: Strong happy‑path coverage for collection multi‑create; verify attribute/index availability semantics

The testCreateCollectionWithAttributesAndIndexes test nicely exercises end‑to‑end behavior: declarative attributes + indexes on create, metadata assertions, and document CRUD including defaults. One thing to double‑check: this test assumes that when the create call returns, all attributes and indexes are already in available status (no sleeps or assertEventually). If the implementation still relies on async workers for physical schema/index creation, consider switching those availability assertions to an eventual check to avoid flakiness in slower environments.


7810-7993: Relationship validation tests look correct; ensure payload shape matches the create API contract

The relationship validation cases (missing relatedCollection, missing/invalid relationType, invalid onDelete, non‑boolean twoWay, array = true, and the final valid many‑to‑one two‑way example) give good coverage for the declarative relationship attribute shape. Just make sure the field names (type = Database::VAR_RELATIONSHIP, relatedCollection, relationType, twoWay, twoWayKey, onDelete) are exactly what the collections create endpoint expects, as they differ from the /attributes/relationship payload used elsewhere; if that contract ever changes, these tests will be the first to break.

src/Appwrite/Utopia/Database/Validator/Indexes.php (1)

58-181: Comprehensive validation logic looks correct.

The isValid method properly validates required fields, key format, duplicates, type constraints, attributes array, orders, and lengths. Error messages are descriptive and include context (index key/position).

src/Appwrite/Utopia/Database/Validator/Attributes.php (1)

70-347: Thorough attribute validation logic.

The validation comprehensively covers all attribute types, field constraints, type-specific options (size for strings, min/max for numerics, enum elements, relationship metadata), and default value consistency. Error messages are clear and actionable.

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

214-324: Well-structured attribute document builder.

The method properly handles type-specific logic (string size defaults, enum elements, numeric ranges, relationship options) and validates constraints (format availability, default value conflicts, spatial support, related collection existence).


420-436: Cleanup logic handles failures gracefully.

The method correctly attempts both deletions independently, ignoring errors to ensure best-effort cleanup. This prevents cascading failures during error recovery.


196-197: Cache invalidation placed correctly after successful creation.

The cache purge for both the document and collection occurs after all database operations succeed, ensuring stale cache entries are cleared.


140-143: This review comment is incorrect. The Exception constructor properly supports two arguments: Exception($type, $message). Line 142 correctly passes the error type constant from getLimitException() as the first argument and a custom message as the second. The constructor signature (lines 387-393 in src/Appwrite/Extend/Exception.php) explicitly defines string $message = null as the second parameter, allowing callers to override the default error description.

Likely an incorrect or invalid review comment.

Comment thread src/Appwrite/Utopia/Database/Validator/Attributes.php Outdated
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (7)
src/Appwrite/Utopia/Database/Validator/Attributes.php (2)

197-271: Defaults for relationship and spatial attributes are neither validated nor explicitly rejected.

The default-value switch only covers string, integer, float, boolean, and datetime types. For relationship and spatial types (point, linestring, polygon), any provided default:

  • Passes isValid() without type checking.
  • Is forwarded into the attribute documents unmodified.
  • Relies on the adapter to error out (or silently ignore), which can lead to confusing server-side failures instead of a clear 4xx validation error.

It would be safer to make the behavior explicit for these types:

  • Either disallow non-null defaults (and fail fast with a clear message), or
  • Add type-specific validation branches (e.g., shape of a spatial object, nullable semantics for relationships).

For example, if defaults should not be supported:

         // Validate default value matches attribute type
         if (isset($attribute['default'])) {
             switch ($attribute['type']) {
@@
                 case Database::VAR_DATETIME:
                     // existing logic...
                     break;
+
+                case Database::VAR_RELATIONSHIP:
+                case Database::VAR_POINT:
+                case Database::VAR_LINESTRING:
+                case Database::VAR_POLYGON:
+                    $this->message = "Default value is not supported for attribute type '" . $attribute['type'] . "'";
+                    return false;
             }
         }

Aligning this with the actual adapter capabilities will prevent hard-to-debug runtime errors.


127-133: Enum elements are not checked against the attribute size, which can yield unsatisfiable enums.

You validate:

  • String size bounds for the attribute (lines 127-133).
  • Each enum element is a non-empty string and ≤ Database::LENGTH_KEY (lines 281-289).

However, you do not ensure that each enum element’s length is ≤ the attribute’s configured size. This allows definitions where some enum values literally cannot be stored in the column (e.g. size = 4, element "apple").

To avoid creating impossible schemas, consider adding a size check inside the enum loop:

-                foreach ($attribute['elements'] as $elementIndex => $element) {
+                $attrSize = $attribute['size'] ?? APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH;
+                foreach ($attribute['elements'] as $elementIndex => $element) {
                     if (!is_string($element) || empty($element)) {
                         $this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' must be a non-empty string";
                         return false;
                     }
                     if (strlen($element) > Database::LENGTH_KEY) {
                         $this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' exceeds maximum length of " . Database::LENGTH_KEY . " characters";
                         return false;
                     }
+                    if (strlen($element) > $attrSize) {
+                        $this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' exceeds attribute size of $attrSize characters";
+                        return false;
+                    }
                 }

This keeps enum definitions consistent with the underlying column size.

Also applies to: 273-299

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

82-83: Attributes param description is slightly out of sync with supported attribute types.

AttributesValidator supports additional types like spatial (point, linestring, polygon), but the parameter description only mentions string, integer, float, boolean, datetime, relationship.

To avoid confusion for SDK users and API consumers, consider updating the description to reflect the full set of supported types (or link to a single source of truth in the docs):

-            ->param('attributes', [], new AttributesValidator(), 'Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.', true)
+            ->param('attributes', [], new AttributesValidator(), 'Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship, point, linestring, polygon), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.', true)

This keeps the public API contract aligned with the validator.


122-143: Unify rollback behavior by using cleanup() instead of scattered deleteDocument() calls.

Failure handling around collection creation is mostly correct, but inconsistent:

  • In several places (lines 133-136, 139-142, 152-155, 166-176), you call $dbForProject->deleteDocument($databaseKey, $collection->getId()); directly.
  • The cleanup() helper (lines 419-435) encapsulates safe rollback of both the DB collection and the metadata document, swallowing any cleanup errors so they don’t mask the original exception.
  • Currently, if deleteDocument() itself throws (e.g. connection issue), that secondary error can override the original validation/adapter failure.

To make rollback behavior consistent and less error-prone, consider replacing those direct deleteDocument() calls with cleanup() wherever you’ve already computed $databaseKey and $collectionKey:

-        } catch (\Throwable $e) {
-            $dbForProject->deleteDocument($databaseKey, $collection->getId());
-            throw $e;
-        }
+        } catch (\Throwable $e) {
+            $this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId());
+            throw $e;
+        }

Apply the same pattern for:

  • Attribute-building failure (lines 133-136).
  • Index limit check (lines 139-142).
  • Index-building failure (lines 152-155).
  • createCollection() failures (lines 166-176).

cleanup() already guards deleteCollection()/deleteDocument() in try/catch, so calling it before the DB collection exists is safe (the delete will just be ignored), and you avoid leaking cleanup errors to API consumers.

Also applies to: 147-155, 166-176, 181-193, 419-435

tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (3)

7183-7606: Enum and general attribute edge-case coverage is comprehensive but densely packed

The new tests around enum attributes and general attribute validation edge cases (reserved keys, type/default mismatches, min/max inconsistencies, array+default, invalid format, etc.) provide very thorough coverage of the validator. The only nit is that testCreateCollectionAttributeValidationEdgeCases and testCreateCollectionEnumValidationEdgeCases bundle many unrelated assertions into single methods, which can make failures harder to localize when something breaks. If this grows further, consider splitting into smaller tests or using data providers to keep individual scenarios more isolated, while reusing setup.


7608-8011: Index and relationship validation tests are solid; double-check naming consistency for relationship fields

The index validation tests hit the important edge cases (duplicate keys, invalid types, empty attributes, orders/lengths mismatch, invalid order values, and a valid compound index) and look correct. Relationship validation also covers missing relatedCollection, missing/invalid relationType, invalid onDelete, array misuse, bad twoWay, and a valid many-to-one two-way relationship.

One design point to verify: in these multi-create payloads, relationship attributes use relatedCollection + relationType alongside type => Database::VAR_RELATIONSHIP, whereas the dedicated relationship-attribute endpoints elsewhere in the suite use relatedCollectionId and type for the relation kind. If the public API is meant to accept both styles or intentionally diverge here, all good; otherwise, consider aligning field names or documenting the difference clearly to avoid confusion for SDK/CLI users.


8013-8280: Datetime/float/missing-field validation tests match existing rules; consider asserting error types for stronger guarantees

The new tests for datetime defaults, float min/max + defaults, and missing required fields in attribute/index definitions correctly mirror the validation rules exercised earlier in the file (e.g., date format constraints, numeric range checks, and mandatory keys in definitions). The HTTP status expectations (400 vs 201) look appropriate and should guard against regressions in the new validators.

If you want these tests to catch more subtle changes in error semantics later, you might also assert the error type (e.g., specific Appwrite exception codes) in a few representative negative cases, similar to other tests in this class. That’s optional but can help ensure the validator fails for the right reasons, not just with a generic 400.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dce4c6e and 252a4e1.

📒 Files selected for processing (3)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5 hunks)
  • src/Appwrite/Utopia/Database/Validator/Attributes.php (1 hunks)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)
src/Appwrite/Utopia/Database/Validator/Indexes.php (5)
  • __construct (35-38)
  • getDescription (47-50)
  • isValid (58-181)
  • isArray (188-191)
  • getType (198-201)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-342)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Users)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (Projects)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: E2E General Test
  • GitHub Check: Unit Test
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: scan
🔇 Additional comments (2)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (2)

6783-6958: Multi-create happy-path test is strong; verify assumptions about synchronous attribute/index availability

The coverage for creating a collection plus attributes and indexes in one call looks good and validates defaults and index wiring end-to-end. The test assumes that right after collection creation, /attributes and /indexes will expose all entries with status === 'available'. If some database adapters keep attributes/indexes in a transient processing state (like the standalone attribute/index APIs), consider wrapping the attribute/index assertions in an assertEventually loop to avoid flakiness; if the new multi-create path guarantees synchronous availability by design, this is fine as-is.


6960-7181: Error-path and cleanup tests for multi-create look correct

The negative tests (invalid attribute type, invalid index target, bad required+default combo, duplicate attribute keys, system-attribute indexing) plus testCreateCollectionCleanupOnFailure together exercise the key validation and cleanup behaviors for the new multi-create flow. HTTP status expectations are consistent with the rest of the suite, and the cleanup check (reusing collectionId after a failed create) is a good safeguard. No functional issues spotted here.

Comment thread src/Appwrite/Utopia/Database/Validator/Attributes.php
Copy link
Copy Markdown
Contributor

@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: 0

🧹 Nitpick comments (2)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (2)

6960-7110: Consider asserting error type payloads in addition to 400 status

The negative tests for invalid attribute/index/relationship definitions currently only assert status-code === 400. That will catch validation vs. success, but won’t fail if the implementation starts returning a different error classification (e.g., wrong type code or a more generic error).

Given these tests are encoding the contract of the new validators, you may want to also assert the specific body['type'] (similar to earlier tests in this class that use AppwriteException constants) for at least a representative subset of these cases (e.g., invalid type, reserved key, min/max violations, enum/relationship/index edge cases). This would tighten regression coverage without much extra noise.

Also applies to: 7182-7605, 7607-7825, 8012-8150, 8152-8279


7273-7500: Large “kitchen‑sink” edge‑case tests might benefit from some structuring

testCreateCollectionAttributeValidationEdgeCases, testCreateCollectionEnumValidationEdgeCases, and testCreateCollectionIndexValidationEdgeCases pack many unrelated invalid‑input scenarios into single methods. Functionally this works, but the methods are long and a bit hard to scan, and a single early failure prevents later cases from running.

Consider either:

  • Splitting by concern (e.g., reserved keys vs. numeric ranges vs. defaults vs. array/format, and separate enum/index suites), or
  • Grouping related payloads into small data‑driven helpers to keep the individual test bodies shorter.

Not urgent, but it would improve readability and future maintenance of this suite.

Also applies to: 7502-7605, 7607-7825

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 252a4e1 and e00dacf.

📒 Files selected for processing (1)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-342)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: E2E Service Test (Health)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (Teams)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (VCS)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (Messaging)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Projects)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: Unit Test
  • GitHub Check: E2E General Test
  • GitHub Check: Benchmark
  • GitHub Check: scan
🔇 Additional comments (1)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)

6784-6958: Confirm synchronous availability assumptions for multi‑create attributes/indexes

These assertions rely on attributes and indexes being immediately in status === 'available' right after the collection create call and before any sleep()/assertEventually, unlike the per‑attribute/index endpoints earlier in this file which are explicitly async. If Collections::create still offloads DDL work to background workers, this test may be timing‑sensitive.

Please confirm the new multi‑create path is guaranteed to return only after attributes/indexes are fully built, or consider wrapping the attribute/index fetches in an assertEventually similar to other tests here to avoid flakiness.

Copy link
Copy Markdown
Contributor

@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: 0

♻️ Duplicate comments (1)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)

184-196: Verify single-bound min/max handling aligns with persistence layer.

The validator now correctly accepts single-bound constraints (only min or only max). Ensure buildAttributeDocument in Create.php persists whichever bound(s) are provided.

🧹 Nitpick comments (3)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)

114-119: Consider defining reserved keys as a class constant.

Reserved keys are hardcoded inline. Extracting to a class constant would improve maintainability and reusability.

+    /**
+     * @var array<string> Reserved attribute keys that cannot be used
+     */
+    protected const RESERVED_KEYS = ['$id', '$createdAt', '$updatedAt', '$permissions', '$collection'];
+
     ...
-            $reservedKeys = ['$id', '$createdAt', '$updatedAt', '$permissions', '$collection'];
-            if (in_array($attribute['key'], $reservedKeys)) {
+            if (in_array($attribute['key'], self::RESERVED_KEYS)) {
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (2)

227-231: Consider documenting the default string size.

The default size of 256 for strings is applied silently when not specified. Consider documenting this default in the parameter description or throwing an error since the validator requires size for string types.

The validator at lines 128-132 in Attributes.php requires size to be set for string types, so this fallback may be unreachable if validation runs first.


419-435: Consider adding error logging in cleanup.

Silently swallowing cleanup errors is appropriate to prevent cascading failures, but logging these errors would aid debugging in production.

     protected function cleanup(Database $dbForProject, string $databaseKey, string $collectionKey, string $collectionId): void
     {
         try {
             $dbForProject->deleteCollection($collectionKey);
-        } catch (\Throwable) {
-            // Ignore cleanup errors for collection deletion
+        } catch (\Throwable $e) {
+            // Log but don't throw cleanup errors
+            \error_log("Cleanup failed for collection $collectionKey: " . $e->getMessage());
         }

         try {
             $dbForProject->deleteDocument($databaseKey, $collectionId);
-        } catch (\Throwable) {
-            // Ignore cleanup errors for document deletion
+        } catch (\Throwable $e) {
+            // Log but don't throw cleanup errors
+            \error_log("Cleanup failed for document $collectionId: " . $e->getMessage());
         }
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e00dacf and 331e294.

📒 Files selected for processing (2)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5 hunks)
  • src/Appwrite/Utopia/Database/Validator/Attributes.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)
src/Appwrite/Utopia/Database/Validator/Indexes.php (4)
  • getDescription (47-50)
  • isValid (58-181)
  • isArray (188-191)
  • getType (198-201)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: E2E Service Test (Teams)
  • GitHub Check: E2E Service Test (Migrations)
  • GitHub Check: E2E Service Test (Health)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (Storage)
  • GitHub Check: E2E Service Test (Messaging)
  • GitHub Check: E2E Service Test (Tokens)
  • GitHub Check: E2E Service Test (VCS)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Projects)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: Unit Test
  • GitHub Check: scan
🔇 Additional comments (12)
src/Appwrite/Utopia/Database/Validator/Attributes.php (5)

1-49: LGTM on class structure and setup.

The imports, class properties, and constructor are well-organized. The supported types and formats are clearly defined.


127-146: LGTM on string and format validation.

String size validation is thorough, and format validation is correctly restricted to string types only.


274-300: LGTM on enum validation.

Comprehensive validation for enum elements including type, emptiness, and length checks. Default value validation ensures consistency with declared elements.


302-349: LGTM on relationship validation.

All relationship constraints are properly validated: array restriction, required fields, relation type, two-way configuration, and onDelete behavior.


355-373: LGTM on utility methods.

The isArray() and getType() implementations follow the same pattern as the Indexes validator.

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

12-27: LGTM on imports.

All imported validators and utilities are used appropriately throughout the file.


122-136: LGTM on attribute document building with cleanup.

Proper error handling ensures the collection document is cleaned up if attribute building fails.


179-193: LGTM on document persistence with proper cleanup.

Attribute and index documents are created after successful collection creation, with appropriate cleanup on failure.


237-243: Single-bound min/max now persisted with fallback defaults.

The implementation uses fallback values (PHP_INT_MIN/PHP_INT_MAX for integers, ±PHP_FLOAT_MAX for floats) when only one bound is specified. This ensures the constraint is enforced at runtime.


395-411: LGTM on IndexValidator integration.

The IndexValidator is properly instantiated with all necessary adapter capabilities and validates index viability before creation.


340-370: Add $permissions to system attributes list.

The $permissions field is a defined system attribute (see Document.php) but not included in $systemAttrs for index validation. Add it to prevent "Unknown attribute" errors when indexing on $permissions.

$systemAttrs = ['$id', '$createdAt', '$updatedAt', '$permissions'];

138-142: Review comment is incorrect - Exception constructor signature verified.

The Appwrite Exception class constructor signature is __construct(string $message, ?int $code = null, ?\Throwable $previous = null), where the first parameter accepts a string message, not an exception type. The code at line 141 correctly passes a message as the first argument and a custom error message as the second parameter (which becomes the $code or is handled appropriately). The proposed diff would remove necessary error context and should not be applied.

Likely an incorrect or invalid review comment.

Copy link
Copy Markdown
Contributor

@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: 0

🧹 Nitpick comments (5)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5)

81-82: Consider aligning attributes/indexes param docs with validator capabilities

The attributes and indexes params are correctly wired to AttributesValidator / IndexesValidator, but the descriptions are narrower than what those validators accept (e.g., spatial types, relationship options like relatedCollection, relationType, twoWay, onDelete, numeric min/max). Consider expanding the docstrings so the HTTP/API surface matches the validator feature set and avoids documentation drift.


124-135: Relationship & spatial attribute handling: verify two‑way behavior and support flags

The attribute path is generally well-structured: you build collection/attribute documents up front, use buildAttributeDocument() to centralize type/format/default/options logic, validate relationship parents, and bail out early on errors while rolling back the collection document.

Two areas are worth a closer look:

  1. For type === Database::VAR_RELATIONSHIP with twoWay enabled, this code only materializes the parent‑side attribute and immediately marks it status => 'available'. If there’s existing logic that backfills the inverse attribute based on new relationship attributes, please confirm it still triggers for attributes created synchronously here (and doesn’t rely on a 'processing' status). Otherwise, two‑way relationships defined at collection‑create time might never get their opposite side.

  2. Spatial attributes are currently rejected whenever getAdapter()->getSupportForSpatialIndex() is false. If an adapter can store spatial attribute types but merely lacks spatial index support, this could be overly strict for attribute creation. It’s worth confirming that this flag is intended to gate spatial attribute support, not just spatial index support, or consider separating those concerns.

The isCollectionsAPI()‑based parent-not-found messaging is a nice touch to keep “collection” vs “table” terminology consistent across endpoints. Based on learnings, this aligns well with prior guidance.

Also applies to: 212-309


137-155: Index construction & validation are robust; double‑check limit semantics

The index pipeline looks good overall:

  • You enforce a hard cap via getLimitForIndexes() before doing any heavier work.
  • buildIndexDocument() ensures each index attribute either matches a declared attribute or one of the allowed system fields, rejects relationship attributes, and normalizes lengths / orders (esp. for array attributes via MAX_ARRAY_INDEX_LENGTH).
  • You then delegate to IndexValidator with adapter-specific capabilities (getMaxIndexLength(), getInternalIndexesKeys(), spatial/vector support flags, etc.) and surface a contextual invalid-index exception.

Two follow-ups to verify:

  • The limit check is based on count($indexes) only. If getLimitForIndexes() is intended to represent the total number of indexes per collection (user + internal), you may need to subtract the internal index count (from getInternalIndexesKeys()) to avoid rejecting valid configurations for adapters with many built‑in indexes.

  • IndexValidator is instantiated per index with an empty indexes list ([]), so cross‑index conflicts (duplicate keys, incompatible combinations) depend entirely on IndexesValidator at the request layer. That’s fine if those two validators stay in lockstep, but if their rules ever diverge you might want to pass the already built $collectionIndexes into IndexValidator so it can enforce cross‑index constraints as well.

Also applies to: 316-397


132-135: Consider centralizing collection-document rollback logic

On most early error paths (attribute build failure, index count over limit, index build failure, createCollection failures), you manually call deleteDocument($databaseKey, $collection->getId()), while later failures use the dedicated cleanup() helper. To reduce duplication and the risk of future inconsistencies, consider routing these paths through a common helper (possibly cleanup() with an option to skip deleteCollection) so that all rollback logic lives in one place.

Also applies to: 139-141, 152-153, 165-175


178-193: Cleanup currently skips attributes/indexes docs; verify createDocuments atomicity

In the post‑creation phase you bulk insert attribute and index documents, and on failure call cleanup() to drop the underlying DB collection and the parent collection document. cleanup() deliberately ignores errors from deleteCollection / deleteDocument, which is reasonable for best‑effort rollback.

However, if createDocuments('attributes' | 'indexes', $docs) can partially succeed before throwing (e.g., per‑document inserts with a mid‑batch duplicate), this sequence will:

  • Leave any already‑inserted attribute/index docs in place,
  • Remove the parent collection document and drop the physical collection, and
  • On retry, likely hit DuplicateExceptions on those orphan docs while the actual collection has been rolled back.

If createDocuments is guaranteed to be all‑or‑nothing at the adapter level, this is safe. Otherwise, consider extending the rollback here to also remove any attribute/index docs that were just created, for example by iterating $attributeDocuments / $indexDocuments and calling deleteDocument('attributes' | 'indexes', $doc->getId()) inside best‑effort try/catch blocks, in addition to the existing cleanup() call.

Also applies to: 405-425

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 331e294 and c6ccbfa.

📒 Files selected for processing (1)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
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.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
📚 Learning: 2025-06-19T09:20:03.312Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php:57-59
Timestamp: 2025-06-19T09:20:03.312Z
Learning: In table-related endpoints (such as `src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php`), parameter descriptions should use "table" and "row" terminology instead of "collection" and "document" for clarity and consistency.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
🧬 Code graph analysis (1)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (2)
  • Attributes (12-374)
  • getDescription (58-61)
src/Appwrite/Utopia/Database/Validator/Indexes.php (2)
  • Indexes (9-202)
  • getDescription (47-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: scan
🔇 Additional comments (3)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (3)

12-15: Validator imports and aliases look consistent

The new imports cleanly separate request-level validators (AttributesValidator, IndexesValidator) from the low-level IndexValidator via aliases; no issues spotted here.

Also applies to: 25-25


89-123: Collection metadata creation & exception mapping look solid

Extending action() to take attributes/indexes while keeping the existing flow—database lookup, unique() handling for collectionId, permission aggregation, then createDocument('database_'.$database->getSequence(), …) with Duplicate/Limit/NotFound mapped through the existing helpers—preserves prior behavior and sets up the new schema work cleanly. The derived $collectionKey / $databaseKey values match the established naming conventions for underlying collections.


194-205: Cache purge and event dispatch remain consistent with existing behavior

Purging the cached collection/document using the same database_{seq} / database_{seq}_collection_{seq} keys you derived earlier, then queuing the event with the collection ID and database context, preserves the previous behavior while extending the flow to cover schema creation. No issues here.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)

7294-8300: Extensive attribute/index/relationship validation matrix looks correct but is quite verbose

The new tests for attribute, enum, index, relationship, datetime, float, and “missing required fields” validation cover a wide range of edge cases and failure modes and match the stated goals of the PR. From a behaviour standpoint they look consistent and valuable.

If this area keeps growing, consider extracting small helpers (e.g. “createTestDatabase”, “postCollectionWithPayload”) or data-driven subtests to reduce repetition and make future additions easier to reason about, but this can be deferred.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6ccbfa and df94765.

📒 Files selected for processing (4)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5 hunks)
  • src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php (2 hunks)
  • src/Appwrite/Utopia/Database/Validator/Attributes.php (1 hunks)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-19T09:20:03.312Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php:57-59
Timestamp: 2025-06-19T09:20:03.312Z
Learning: In table-related endpoints (such as `src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php`), parameter descriptions should use "table" and "row" terminology instead of "collection" and "document" for clarity and consistency.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
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.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
🧬 Code graph analysis (3)
src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php (1)
src/Appwrite/GraphQL/Types/Mapper.php (1)
  • param (255-363)
src/Appwrite/Utopia/Database/Validator/Attributes.php (1)
src/Appwrite/Auth/Key.php (1)
  • Key (14-201)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-342)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Benchmark
  • GitHub Check: scan
🔇 Additional comments (2)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (2)

6783-6958: Solid coverage for synchronous multi-create happy path

This test exercises the intended behaviour well: single-call collection creation with attributes, indexes, and subsequent document CRUD + defaults, and it asserts both attribute/index metadata and data-path usage. Nothing blocking here from a test-design standpoint.


7104-7123: The test at lines 7104-7123 cannot be verified as contradictory because the tests referenced in your comment—specifically testCreateCollectionRelationshipValidation and exercises using Database::VAR_RELATIONSHIP with successful relationship attribute creation—do not exist in the codebase.

A thorough search reveals:

  • No testCreateCollectionRelationshipValidation test method exists
  • No Database::VAR_RELATIONSHIP constant is defined anywhere
  • Only one use of relationType exists in the entire test file: the rejected inline case at lines 7104-7123
  • No successful inline relationship attribute creation tests are found

The test at lines 7104-7123 correctly demonstrates that the 'type' => 'relationship' attribute is not supported as an inline attribute and is properly rejected with a 400 status and "Invalid type" message. This behavior is consistent with the API design—no contradictory specification exists.

Likely an incorrect or invalid review comment.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (9)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (7)

82-90: Attributes/indexes params and action signature alignment

The new attributes/indexes params, validators, and the extended action() signature line up correctly with the constructor’s param/inject order, so wiring looks sound. One small UX improvement: the attributes description lists relationship as an allowed type, but inline relationship attributes are rejected later in the method. Consider updating the description to call this out explicitly to avoid surprises.

-            ->param('attributes', [], new ArrayList(new JSON(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.', true)
+            ->param('attributes', [], new ArrayList(new JSON(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. Relationship attributes cannot be created inline; use the create relationship endpoint instead.', true)

122-188: Attribute/index validation and document-building flow looks robust; minor duplication in cleanup

The pre-createCollection pipeline (computing keys, validating attributes, rejecting relationships, building attribute/index docs, and validating indexes against adapter capabilities) is coherent and ensures we bail out before touching the physical collection when definitions are bad. Repeated calls to deleteDocument($databaseKey, $collection->getId()) across several failure branches are correct but a bit duplicated; if this grows further, you might consider a small helper for “delete collection document only” to complement cleanup().


190-210: Consider using cleanup() for some createCollection failures to avoid orphaned collections

Passing $collectionKey, $collectionAttributes, and $collectionIndexes into createCollection() looks correct, and you now consistently drop the collection document on all exception paths. If any adapter can partially create the underlying collection (or indexes) before throwing IndexException, LimitException, or a generic \Throwable, you might end up with an orphaned physical collection since cleanup() isn’t invoked here.

If Database::createCollection() is guaranteed to be atomic and leave no collection behind on failure, this is fine. Otherwise, consider using cleanup() for the non-duplicate branches:

-        } catch (DuplicateException) {
-            $dbForProject->deleteDocument($databaseKey, $collection->getId());
-            throw new Exception($this->getDuplicateException());
-        } catch (IndexException $e) {
-            $dbForProject->deleteDocument($databaseKey, $collection->getId());
-            throw new Exception($this->getInvalidIndexException(), $e->getMessage());
-        } catch (LimitException) {
-            $dbForProject->deleteDocument($databaseKey, $collection->getId());
-            throw new Exception($this->getLimitException());
-        } catch (\Throwable $e) {
-            $dbForProject->deleteDocument($databaseKey, $collection->getId());
-            throw $e;
-        }
+        } catch (DuplicateException) {
+            $dbForProject->deleteDocument($databaseKey, $collection->getId());
+            throw new Exception($this->getDuplicateException());
+        } catch (IndexException $e) {
+            $this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId());
+            throw new Exception($this->getInvalidIndexException(), $e->getMessage());
+        } catch (LimitException) {
+            $this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId());
+            throw new Exception($this->getLimitException());
+        } catch (\Throwable $e) {
+            $this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId());
+            throw $e;
+        }

212-226: Bulk creation of attribute/index documents with centralized cleanup is a good improvement; verify deleteCollection cascades as expected

The two createDocuments calls, wrapped in a single try with cleanup() on any failure, give you a clear “all-or-nothing” behavior for the schema metadata. This looks solid. One thing to verify: that Database::deleteCollection($collectionKey) reliably removes any attributes/indexes state for this collection (or that such state can’t partially survive a failed bulk insert). If it doesn’t, you might still accumulate orphaned attribute/index docs even after cleanup.


228-230: Cache purge is correct; optional reuse of computed keys

Using purgeCachedDocument and purgeCachedCollection with the computed database/collection sequences matches how you build $databaseKey/$collectionKey earlier. For readability, you could reuse those variables here instead of re-concatenating the strings, but that’s purely cosmetic.


321-380: buildIndexDocument correctly normalizes array-attribute indexes; consider minor naming/efficiency polish

Index documents are built with normalized lengths and orders, and array attributes are given Database::MAX_ARRAY_INDEX_LENGTH with null order, which lines up with how array indexes are typically treated. Passing the same attribute Document instances that you later feed into IndexValidator keeps things consistent.

Two optional nits:

  • The parameter name $attributeDocuments can be slightly confusing since you’re actually passing $collectionAttributes; renaming either the param or the local variable could improve readability.
  • If you ever expect many attributes, you could pre-index them by key (assoc array) instead of doing array_search inside the loop, though this is likely premature optimization for most collections.

382-402: cleanup() helper is straightforward; parameter naming could be clearer

The cleanup helper safely attempts to drop the underlying DB collection and then the collection document, swallowing any errors. That’s appropriate for a best-effort rollback. The only minor nit is naming: $databaseId and $collectionId here are internal collection names ($databaseKey/$collectionKey), not the logical database/collection IDs. Renaming them (e.g., to $databaseCollectionId / $dbCollectionName) in a follow-up could make call sites and the docblock slightly clearer.

tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (2)

7294-7521: Split testCreateCollectionAttributeValidationEdgeCases into smaller, focused tests

This method bundles many unrelated attribute edge cases (reserved keys, wrong default types, min/max issues, string length, signed on non‑numeric, arrays, invalid format, and a success case) into one large test. A single failure here will be hard to localize and may short‑circuit checks of later scenarios. Consider splitting into smaller tests (e.g., “ReservedKeys”, “DefaultTypeMismatch”, “RangeValidation”, “ArrayDefaults”, “FormatValidation”) so failures are isolated and easier to debug.


7628-7846: Index edge‑case test is comprehensive but monolithic

testCreateCollectionIndexValidationEdgeCases very usefully covers duplicate keys, invalid types, empty attributes, mismatched orders/lengths, invalid order values, and a valid compound index. For maintainability and clearer failure signals, it would be better to break this into multiple tests (e.g., one per invalid pattern plus one success test) rather than a single long method. This also makes it easier to extend with new edge cases later.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df94765 and 121f946.

📒 Files selected for processing (2)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5 hunks)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-19T09:20:03.312Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php:57-59
Timestamp: 2025-06-19T09:20:03.312Z
Learning: In table-related endpoints (such as `src/Appwrite/Platform/Modules/Databases/Http/Databases/Tables/Update.php`), parameter descriptions should use "table" and "row" terminology instead of "collection" and "document" for clarity and consistency.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
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.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
🧬 Code graph analysis (1)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-342)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Benchmark
  • GitHub Check: scan
🔇 Additional comments (6)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (2)

12-30: New validators and helpers are correctly imported and used

The added imports for AttributesValidator, IndexValidator, ArrayList, and JSON are all used in the constructor/action and look consistent with the new validation flow.


241-314: buildAttributeDocument structure matches expected attribute metadata

The attribute document builder correctly pulls out key/type/size/required/signed/array/default/filters, handles enum elements via formatOptions, and switches to the appropriate int/float range format when min/max are present. The IDs and metadata (database/collection internal IDs vs external IDs) look consistent with how collection and attribute documents are identified elsewhere, so this helper should integrate cleanly with both createCollection and the bulk createDocuments call.

tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (4)

6784-6958: Solid coverage for happy‑path multi‑create collection behavior

This test exercises inline attributes, defaults, indexes, and subsequent document creation in a single flow, which is exactly what this feature needs. The assumptions about attributes/indexes being immediately available look consistent with the new synchronous schema‑creation semantics.


7848-8031: Nice targeted coverage for inline relationship validation

This test suite around testCreateCollectionRelationshipValidation exercises missing relatedCollection, missing relationType, invalid relationType, invalid onDelete, invalid twoWay type, and a fully valid relationship definition. That’s a good, focused complement to the more generic attribute tests and clearly documents the intended contract for inline relationships.


8033-8171: Datetime/float validation tests align with expected validator behavior

The datetime and float tests cleanly separate invalid cases (non‑ISO default, min>max, default out of range) from valid ones, and they don’t over‑assert on internal details. These should give good coverage for the new Attributes validator without being brittle.


8173-8300: Missing‑field tests look correct and consistent with the schema model

The cases for attributes missing key/type and indexes missing key/type/attributes match how the public API is documented, and asserting 400 in each case seems appropriate. No changes needed here.

Comment thread tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (3)
src/Appwrite/Utopia/Database/Validator/Indexes.php (1)

122-133: Consider extracting system attributes to a shared constant.

The system attributes ($id, $createdAt, $updatedAt) are defined inline. If these need to be referenced elsewhere or if new system attributes are added, a shared constant would improve maintainability.

+    // Consider defining at class level or using a shared constant
+    protected array $systemAttributes = ['$id', '$createdAt', '$updatedAt'];

Then reference it in the validation:

-            $systemAttrs = ['$id', '$createdAt', '$updatedAt'];
+            $systemAttrs = $this->systemAttributes;
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (1)

165-167: Parameter naming mismatch.

buildIndexDocument is called with $collectionAttributes, but the method parameter is named $attributeDocuments. Both are arrays of Document objects, so it works functionally, but the naming is confusing.

Consider renaming the parameter for clarity:

-    protected function buildIndexDocument(Document $database, Document $collection, array $indexDef, array $attributeDocuments): array
+    protected function buildIndexDocument(Document $database, Document $collection, array $indexDef, array $collectionAttributes): array
     {
         ...
-        $attrKeys = array_map(fn ($a) => $a->getAttribute('key'), $attributeDocuments);
+        $attrKeys = array_map(fn ($a) => $a->getAttribute('key'), $collectionAttributes);

And update references in lines 343-344.

tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)

6960-7131: Consider asserting error “type” codes for negative multi-create cases

The error-path scenarios in testCreateCollectionWithAttributesAndIndexesErrors are valuable, but they currently only assert HTTP 400 (and, for the relationship case, a substring of body['message']). To make these tests more resilient to copy updates and to better document expected failure modes, consider also asserting the structured error code in body['type'] for at least the key cases (invalid attribute type, bad index attributes, relationship payload).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 121f946 and 4803851.

📒 Files selected for processing (3)
  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (5 hunks)
  • src/Appwrite/Utopia/Database/Validator/Indexes.php (1 hunks)
  • tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-10T04:12:13.037Z
Learnt from: ItzNotABug
Repo: appwrite/appwrite PR: 9693
File: src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php:85-86
Timestamp: 2025-05-10T04:12:13.037Z
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.

Applied to files:

  • src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php
🧬 Code graph analysis (2)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php (2)
src/Appwrite/Utopia/Database/Validator/Attributes.php (3)
  • Attributes (15-358)
  • isValid (74-337)
  • getDescription (63-66)
src/Appwrite/Utopia/Database/Validator/Indexes.php (3)
  • Indexes (9-200)
  • isValid (55-179)
  • getDescription (44-47)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Client.php (1)
  • Client (8-342)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: E2E Service Test (Migrations)
  • GitHub Check: E2E Service Test (Messaging)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (GraphQL)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: E2E General Test
  • GitHub Check: Unit Test
  • GitHub Check: scan
🔇 Additional comments (15)
src/Appwrite/Utopia/Database/Validator/Indexes.php (2)

1-37: LGTM! Well-structured validator setup.

The class properly extends Validator, defines supported index types and orders, and has a clean constructor with the configurable maxIndexes limit.


186-198: LGTM!

The isArray() and getType() implementations are consistent with the Attributes validator pattern, where isArray() returns false (the validator handles the array internally) while getType() returns TYPE_ARRAY (the expected input type).

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

12-31: LGTM! Imports are well-organized and all appear to be used.


83-84: LGTM!

The new parameters use ArrayList(new JSON(), ...) which correctly parses each element as JSON, with the deep structure validation delegated to AttributesValidator and IndexesValidator later in the flow.


136-141: Good validation: relationship attributes correctly rejected inline.

Clear error message directing users to the appropriate endpoint for relationship creation.


176-196: LGTM! Comprehensive index validation against DB adapter capabilities.

Properly validates each index against adapter constraints (max index length, spatial support, fulltext support, etc.) before attempting creation.


220-234: LGTM! Proper transactional semantics with cleanup on failure.

The code correctly handles the case where the underlying collection was created but persisting metadata documents failed, ensuring the cleanup method removes both the DB collection and the collection document to maintain consistency.


390-410: LGTM! Appropriate cleanup handling.

The silent error handling in the cleanup method is appropriate - if cleanup fails, there's nothing more that can be done at this point, and the original error should be propagated to the caller rather than masking it with a cleanup error.


274-283: No issue found: min/max handling does not conflict with user-provided format.

The code correctly overrides format to APP_DATABASE_ATTRIBUTE_INT_RANGE or APP_DATABASE_ATTRIBUTE_FLOAT_RANGE when min or max is provided. This poses no conflict because AttributesValidator enforces strict mutual exclusivity: format is restricted to string types (line 148), while min/max are restricted to integer and float types (line 160). Since these constraints apply to different types, the override is safe and intentional.


350-353: Array attribute indexes intentionally use null order by design.

For array attributes, the order is forcibly set to null because arrays use index-based access (e.g., arrayInsert(index, value)) rather than sort ordering. This behavior is tested and validated—the test comment explicitly confirms: "set a length for an array attribute, it should get overriden with Database::MAX_ARRAY_INDEX_LENGTH." The validator also explicitly allows null as a valid order value for indexes, confirming this is by design across all supported adapters.

tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (5)

6784-6958: Inline multi-create happy-path coverage looks solid

The testCreateCollectionWithAttributesAndIndexes flow exercises attributes, defaults, index definitions (including orders), and document creation end‑to‑end, which is exactly what this feature needs. No issues from my side here.


7133-7201: Cleanup-on-failure test captures the core invariant well

testCreateCollectionCleanupOnFailure nicely proves that a failed multi-create with an invalid index leaves no lingering collection, by reusing the same collectionId successfully on the second request. This is an important safety guarantee for callers relying on idempotency.


7203-7521: Enum + attribute validation edge-case tests are thorough

The new tests for enum attributes and general attribute validation edge cases (reserved keys, wrong default types, range violations, array+default, invalid format, etc.) give very good coverage of the new validator behavior at collection-create time and mirror the existing per-attribute update tests. No changes requested.


7523-7846: Index validation edge-case suite aligns well with expected invariants

testCreateCollectionIndexValidationEdgeCases exercises duplicate index keys, invalid types, empty attribute lists, orders/lengths cardinality mismatches, and invalid order values, plus a positive compound-index case. This is a good complement to the attribute tests; I don’t see any functional issues here.


7906-8115: Float and “missing required fields” validation tests look correct

The float range/default tests and the missing‑field cases for attributes/indexes (key, type, attributes) behave as expected for the new multi-create schema contract. The mix of failing and one successful case at the end is a nice pattern; nothing blocking here.

Comment thread tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Dec 5, 2025

Open in StackBlitz

npm i https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@10894

commit: 3c9548b

@abnegate abnegate merged commit 65bd9a4 into 1.8.x Dec 8, 2025
53 of 109 checks passed
@abnegate abnegate deleted the feat-multi-create branch December 8, 2025 05:58
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.

2 participants