mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Add missing state handlers
This commit is contained in:
@@ -423,6 +423,33 @@ class TransactionState
|
||||
];
|
||||
break;
|
||||
|
||||
case 'increment':
|
||||
case 'decrement':
|
||||
$attribute = $data['attribute'] ?? null;
|
||||
$value = $data['value'] ?? 1;
|
||||
|
||||
if ($attribute) {
|
||||
if (isset($state[$collectionId][$documentId])) {
|
||||
$existingDocument = $state[$collectionId][$documentId]['document'];
|
||||
$currentValue = $existingDocument->getAttribute($attribute, 0);
|
||||
$newValue = $action === 'increment' ? $currentValue + $value : $currentValue - $value;
|
||||
$existingDocument->setAttribute($attribute, $newValue);
|
||||
|
||||
$currentAction = $state[$collectionId][$documentId]['action'];
|
||||
if ($currentAction !== 'create' && $currentAction !== 'upsert') {
|
||||
$state[$collectionId][$documentId]['action'] = 'update';
|
||||
}
|
||||
} else {
|
||||
$newValue = $action === 'increment' ? $value : -$value;
|
||||
$state[$collectionId][$documentId] = [
|
||||
'action' => 'update',
|
||||
'document' => new Document([$attribute => $newValue]),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkCreate':
|
||||
if (\is_array($data)) {
|
||||
foreach ($data as $doc) {
|
||||
@@ -437,6 +464,84 @@ class TransactionState
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkUpdate':
|
||||
if (isset($data['queries']) && isset($data['data'])) {
|
||||
$queries = Query::parseQueries($data['queries'] ?? []);
|
||||
$updateData = $data['data'];
|
||||
|
||||
foreach ($state[$collectionId] ?? [] as $docId => $entry) {
|
||||
if (!$entry['exists']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$document = $entry['document'];
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
if ($this->documentMatchesFilters($document, $filters)) {
|
||||
foreach ($updateData as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$document->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$currentAction = $state[$collectionId][$docId]['action'];
|
||||
if ($currentAction !== 'create' && $currentAction !== 'upsert') {
|
||||
$state[$collectionId][$docId]['action'] = 'update';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkUpsert':
|
||||
if (\is_array($data)) {
|
||||
foreach ($data as $doc) {
|
||||
if ($doc instanceof Document) {
|
||||
$doc = $doc->getArrayCopy();
|
||||
}
|
||||
|
||||
$docId = $doc['$id'] ?? null;
|
||||
if (!$docId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($state[$collectionId][$docId])) {
|
||||
$existingDocument = $state[$collectionId][$docId]['document'];
|
||||
foreach ($doc as $key => $value) {
|
||||
$existingDocument->setAttribute($key, $value);
|
||||
}
|
||||
} else {
|
||||
$state[$collectionId][$docId] = [
|
||||
'action' => 'upsert',
|
||||
'document' => new Document($doc),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkDelete':
|
||||
if (isset($data['queries'])) {
|
||||
$queries = Query::parseQueries($data['queries'] ?? []);
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
foreach ($state[$collectionId] ?? [] as $docId => $entry) {
|
||||
if (!$entry['exists']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$document = $entry['document'];
|
||||
if ($this->documentMatchesFilters($document, $filters)) {
|
||||
$state[$collectionId][$docId] = [
|
||||
'action' => 'delete',
|
||||
'exists' => false
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4613,4 +4613,127 @@ trait TransactionsBase
|
||||
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that bulkUpdate can match documents created in the same transaction
|
||||
* This tests the fix for the bug where applyBulkUpdateToState was treating
|
||||
* state entries as Documents instead of arrays with 'document' keys
|
||||
*/
|
||||
public function testBulkUpdateMatchesCreatedDocsInSameTransaction(): void
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'BulkUpdateStateDB'
|
||||
]);
|
||||
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestTable',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'status',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'flag',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
sleep(3);
|
||||
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Create 3 documents with status='pending' in transaction
|
||||
$docIds = [];
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'rowId' => 'test_' . $i,
|
||||
'data' => [
|
||||
'status' => 'pending'
|
||||
],
|
||||
'transactionId' => $transactionId
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$docIds[] = $response['body']['$id'];
|
||||
}
|
||||
|
||||
// Bulk update all documents with status='pending' to add flag='processed'
|
||||
// This should match all 3 documents created above in the same transaction
|
||||
$response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'data' => [
|
||||
'flag' => 'processed'
|
||||
],
|
||||
'queries' => [Query::equal('status', ['pending'])->toString()],
|
||||
'transactionId' => $transactionId
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Commit transaction
|
||||
$response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId}", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'commit' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Verify all 3 documents have the flag set
|
||||
foreach ($docIds as $docId) {
|
||||
$response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/{$docId}", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('pending', $response['body']['status']);
|
||||
$this->assertEquals('processed', $response['body']['flag'], 'Bulk update should have matched document created in same transaction');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user