Conversation
📝 WalkthroughWalkthroughAdds a fallback in app/controllers/api/databases.php: after createOrUpdateDocuments, if Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
Security Scan Results for PRDocker Image Scan Results
Source Code Scan Results🎉 No vulnerabilities found! |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/controllers/api/databases.php (1)
281-286: Bug: Misplaced parentheses break attribute validation in updateAttribute()Both conditionals use
getAttribute(( 'type') !== $type)andgetAttribute(( 'filter') !== $filter), passing a boolean instead of the attribute key. This will always read the wrong attribute and makes validation incorrect.Fix:
- if ($attribute->getAttribute(('type') !== $type)) { + if ($attribute->getAttribute('type') !== $type) { throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); } - if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute(('filter') !== $filter)) { + if ($attribute->getAttribute('type') === Database::VAR_STRING && $attribute->getAttribute('filter') !== $filter) { throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID); }This is unrelated to the current PR, but it’s a correctness bug that will surface during attribute updates.
🧹 Nitpick comments (2)
app/controllers/api/databases.php (1)
4724-4729: Avoid undefined index notice when checking $permissions in bulk update
if ($data['$permissions'])may trigger an “undefined index” notice. Use a safe check.- if ($data['$permissions']) { + if (isset($data['$permissions']) && $data['$permissions']) { $validator = new Permissions(); if (!$validator->isValid($data['$permissions'])) { throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); } }Low impact, but keeps logs clean.
tests/e2e/Services/Databases/DatabasesBase.php (1)
1733-1765: Strengthen "no-change" upsert verification by asserting $updatedAt is unchanged.To verify the controller fallback truly avoids a write when no attributes change, also assert the document’s $updatedAt didn’t mutate.
Apply this diff:
- /** + /** * Resend same document for no change * Had to add all attributes because of partial comparison $old->getAttributes() == $document->getAttributes() * If nothing is upserted there will be a regular call to getDocument */ + // Capture timestamp to ensure idempotent upsert doesn't mutate metadata + $updatedAtBefore = $document['body']['$updatedAt']; $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'data' => [ 'title' => 'Thor: Ragnarok', 'releaseYear' => 2000, 'integers' => [], 'birthDay' => null, 'duration' => null, 'starringActors' => [], 'actors' => [], 'tagline' => '', 'description' => '', ], 'permissions' => [ Permission::read(Role::users()), Permission::update(Role::users()), Permission::delete(Role::users()), ], ]); $this->assertEquals(200, $document['headers']['status-code']); $this->assertEquals('Thor: Ragnarok', $document['body']['title']); $this->assertCount(3, $document['body']['$permissions']); + // No-op upsert should not bump system timestamp + $this->assertEquals($updatedAtBefore, $document['body']['$updatedAt']);
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
app/controllers/api/databases.php(1 hunks)tests/e2e/Services/Databases/DatabasesBase.php(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/e2e/Services/Databases/DatabasesBase.php (1)
tests/e2e/Client.php (1)
Client(8-328)
⏰ 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 (Projects)
- GitHub Check: E2E Service Test (Proxy)
- GitHub Check: E2E Service Test (Locale)
- GitHub Check: E2E Service Test (Users)
- GitHub Check: E2E Service Test (Tokens)
- GitHub Check: E2E Service Test (Webhooks)
- GitHub Check: E2E Service Test (Realtime)
- GitHub Check: E2E Service Test (Console)
- GitHub Check: E2E Service Test (Databases)
- GitHub Check: E2E Service Test (FunctionsSchedule)
- GitHub Check: E2E Service Test (GraphQL)
- GitHub Check: E2E Service Test (Avatars)
- GitHub Check: E2E Service Test (Health)
- GitHub Check: E2E Service Test (Account)
- 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: Setup & Build Appwrite Image
- GitHub Check: scan
🔇 Additional comments (2)
tests/e2e/Services/Databases/DatabasesBase.php (2)
1707-1707: Minor formatting is fine.The added blank line after documentId improves readability.
1723-1725: LGTM: baseline upsert assertions.Status and permissions count checks after the initial upsert look correct.
| throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); | ||
| } | ||
|
|
||
| if (empty($upserted[0])) { |
There was a problem hiding this comment.
I think we should return a header for $modified
that was returned from
$modified = $dbForProject->createOrUpdateDocuments
Otherwise will not know if upserted was skipped.
Like now in this test had to add all attributes
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
tests/e2e/Services/Databases/DatabasesBase.php (1)
1761-1764: Previous failing placeholder assertion is gone — resolved.The hard-coded failing assertion reported earlier no longer exists in this block. Good cleanup.
🧹 Nitpick comments (3)
tests/e2e/Services/Databases/DatabasesBase.php (3)
1733-1738: Reword to describe observable behavior, not internals.The comment references internal implementation ($old->getAttributes() == $document->getAttributes()) which can drift. Prefer stating the behavioral contract we’re validating (idempotent upsert returns the current document without mutating timestamps).
Apply this comment tweak:
- /** - * Resend same document for no change - * Had to add all attributes because of partial comparison $old->getAttributes() == $document->getAttributes() - * If nothing is upserted there will be a regular call to getDocument - */ + /** + * Resend the same fields to verify a no-op upsert: + * server should return the existing document (no mutation) and not bump $updatedAt. + * When no changes are detected, API should behave like a GET of the current document. + */
1739-1759: Ensure a true “no-change” upsert: reuse fetched data to avoid null/[]/'' drift.Hard-coding defaults (e.g., empty arrays/strings) can differ from stored values (often null), accidentally turning this into an update instead of a no-op. Build the payload from the previously fetched document body and strip system fields so the PUT is guaranteed identical.
Apply this diff to construct an idempotent payload and reuse permissions:
- $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([ + // Build idempotent payload from the current document snapshot + $sameData = $document['body']; + foreach (['$id', '$databaseId', '$collectionId', '$permissions', '$createdAt', '$updatedAt', '$sequence', '$tenant'] as $sys) { + unset($sameData[$sys]); + } + $samePermissions = $document['body']['$permissions'] ?? [ + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()), + ]; + + $document = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'data' => [ - 'title' => 'Thor: Ragnarok', - 'releaseYear' => 2000, - 'integers' => [], - 'birthDay' => null, - 'duration' => null, - 'starringActors' => [], - 'actors' => [], - 'tagline' => '', - 'description' => '', - ], - 'permissions' => [ - Permission::read(Role::users()), - Permission::update(Role::users()), - Permission::delete(Role::users()), - ], + 'data' => $sameData, + 'permissions' => $samePermissions, ]);Optionally also assert that $updatedAt didn’t change (see next comment) and that the response body still matches the snapshot for key user fields.
1761-1764: Make idempotency observable: assert $updatedAt is unchanged.If the upsert is a true no-op, $updatedAt should remain the same. Capture it before the second PUT and compare after.
Patch this block to include the timestamp assertion:
$this->assertEquals(200, $document['headers']['status-code']); $this->assertEquals('Thor: Ragnarok', $document['body']['title']); $this->assertCount(3, $document['body']['$permissions']); + $this->assertEquals($prevUpdatedAt, $document['body']['$updatedAt'], 'No-op upsert should not bump $updatedAt');And record the baseline right after the first GET (shown here for clarity; add near Lines 1726–1731):
// After the initial GET of the upserted doc $prevUpdatedAt = $document['body']['$updatedAt'] ?? null; $this->assertNotNull($prevUpdatedAt, 'Precondition: $updatedAt should be present');
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
tests/e2e/Services/Databases/DatabasesBase.php(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/e2e/Services/Databases/DatabasesBase.php (1)
tests/e2e/Client.php (1)
Client(8-328)
⏰ 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 (Messaging)
- GitHub Check: E2E Service Test (Users)
- GitHub Check: E2E Service Test (Migrations)
- GitHub Check: E2E Service Test (Proxy)
- GitHub Check: E2E Service Test (GraphQL)
- GitHub Check: E2E Service Test (Realtime)
- GitHub Check: E2E Service Test (Webhooks)
- GitHub Check: E2E Service Test (VCS)
- GitHub Check: E2E Service Test (FunctionsSchedule)
- GitHub Check: E2E Service Test (Account)
- GitHub Check: E2E Service Test (Locale)
- GitHub Check: E2E Service Test (Teams)
- GitHub Check: E2E Service Test (Health)
- GitHub Check: E2E Service Test (Site Screenshots)
- GitHub Check: E2E Service Test (Databases)
- GitHub Check: E2E Service Test (Dev Keys)
- GitHub Check: E2E General Test
- GitHub Check: Unit Test
- GitHub Check: Benchmark
- GitHub Check: scan
🔇 Additional comments (1)
tests/e2e/Services/Databases/DatabasesBase.php (1)
1707-1707: No-op whitespace change — fine to keep.Non-functional blank line; no action needed.
✨ Benchmark results
⚡ Benchmark Comparison
|
What does this PR do?
Fixes #10326
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
Checklist