mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Support legacy
This commit is contained in:
@@ -169,7 +169,6 @@ jobs:
|
||||
Console,
|
||||
Databases/Legacy,
|
||||
Databases/TablesDB,
|
||||
Databases/Transactions,
|
||||
Functions,
|
||||
FunctionsSchedule,
|
||||
GraphQL,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Transactions;
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions;
|
||||
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Transactions;
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Transactions;
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Transactions\Operations;
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
@@ -53,7 +53,7 @@ class Create extends Action
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('transactionId', '', new UID(), 'Transaction ID.')
|
||||
->param('operations', [], new ArrayList(new Operation()), 'Array of staged operations.', true)
|
||||
->param('operations', [], new ArrayList(new Operation(type: 'legacy')), 'Array of staged operations.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('plan')
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Transactions;
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions;
|
||||
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Extend\Exception;
|
||||
@@ -73,7 +73,7 @@ class Update extends Action
|
||||
* @param bool $rollback
|
||||
* @param UtopiaResponse $response
|
||||
* @param Database $dbForProject
|
||||
* @param Delete $queueForDeletes
|
||||
* @param \Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Delete $queueForDeletes
|
||||
* @return void
|
||||
* @throws ConflictException
|
||||
* @throws Exception
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Transactions;
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Create as TransactionsCreate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Range;
|
||||
|
||||
class Create extends TransactionsCreate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createTransaction';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_TRANSACTION;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tablesdb/transactions')
|
||||
->desc('Create transaction')
|
||||
->groups(['api', 'database', 'transactions'])
|
||||
->label('scope', 'transactions.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'tablesDB',
|
||||
group: 'transactions',
|
||||
name: 'createTransaction',
|
||||
description: '/docs/references/tablesdb/create-transaction.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_CREATED,
|
||||
model: UtopiaResponse::MODEL_TRANSACTION,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('ttl', APP_DATABASE_TXN_TTL_DEFAULT, new Range(min: APP_DATABASE_TXN_TTL_MIN, max: APP_DATABASE_TXN_TTL_MAX), 'Seconds before the transaction expires.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Delete as TransactionsDelete;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
|
||||
class Delete extends TransactionsDelete
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'deleteTransaction';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_NONE;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE)
|
||||
->setHttpPath('/v1/tablesdb/transactions/:transactionId')
|
||||
->desc('Delete transaction')
|
||||
->groups(['api', 'database', 'transactions'])
|
||||
->label('scope', 'transactions.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'tablesDB',
|
||||
group: 'transactions',
|
||||
name: 'deleteTransaction',
|
||||
description: '/docs/references/tablesdb/delete-transaction.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_NOCONTENT,
|
||||
model: UtopiaResponse::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::NONE
|
||||
))
|
||||
->param('transactionId', '', new UID(), 'Transaction ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Get as TransactionsGet;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
|
||||
class Get extends TransactionsGet
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'getTransaction';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_TRANSACTION;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/tablesdb/transactions/:transactionId')
|
||||
->desc('Get transaction')
|
||||
->groups(['api', 'database', 'transactions'])
|
||||
->label('scope', 'transactions.read')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'tablesDB',
|
||||
group: 'transactions',
|
||||
name: 'getTransaction',
|
||||
description: '/docs/references/tablesdb/get-transaction.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: UtopiaResponse::MODEL_TRANSACTION,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('transactionId', '', new UID(), 'Transaction ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\Operations;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations\Create as OperationsCreate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Operation;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\ArrayList;
|
||||
|
||||
class Create extends OperationsCreate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'createOperations';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_TRANSACTION;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/tablesdb/transactions/:transactionId/operations')
|
||||
->desc('Add operations to transaction')
|
||||
->groups(['api', 'database', 'transactions'])
|
||||
->label('scope', 'transactions.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'tablesDB',
|
||||
group: 'transactions',
|
||||
name: 'createOperations',
|
||||
description: '/docs/references/tablesdb/create-operations.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_CREATED,
|
||||
model: UtopiaResponse::MODEL_TRANSACTION,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('transactionId', '', new UID(), 'Transaction ID.')
|
||||
->param('operations', [], new ArrayList(new Operation(type: 'tablesdb')), 'Array of staged operations.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('plan')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Update as TransactionsUpdate;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
class Update extends TransactionsUpdate
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'updateTransaction';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_TRANSACTION;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/tablesdb/transactions/:transactionId')
|
||||
->desc('Update transaction')
|
||||
->groups(['api', 'database', 'transactions'])
|
||||
->label('scope', 'transactions.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'tablesDB',
|
||||
group: 'transactions',
|
||||
name: 'updateTransaction',
|
||||
description: '/docs/references/tablesdb/update-transaction.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: UtopiaResponse::MODEL_TRANSACTION,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('transactionId', '', new UID(), 'Transaction ID.')
|
||||
->param('commit', false, new Boolean(), 'Commit transaction?', true)
|
||||
->param('rollback', false, new Boolean(), 'Rollback transaction?', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForDeletes')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\XList as TransactionsList;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Transactions;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
|
||||
class XList extends TransactionsList
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'listTransactions';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_TRANSACTION_LIST;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/tablesdb/transactions')
|
||||
->desc('List transactions')
|
||||
->groups(['api', 'database', 'transactions'])
|
||||
->label('scope', 'transactions.read')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'tablesDB',
|
||||
group: 'transactions',
|
||||
name: 'listTransactions',
|
||||
description: '/docs/references/tablesdb/list-transactions.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_OK,
|
||||
model: UtopiaResponse::MODEL_TRANSACTION_LIST,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('queries', [], new Transactions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries).', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ class Http extends Service
|
||||
foreach ([
|
||||
LegacyRegistry::class,
|
||||
TablesDBRegistry::class,
|
||||
TransactionsRegistry::class,
|
||||
] as $registrar) {
|
||||
new $registrar($this);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Create as CreateDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Delete as DeleteDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Get as GetDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Logs\XList as ListDatabaseLogs;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Create as CreateTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Delete as DeleteTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Get as GetTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations\Create as CreateOperations;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Update as UpdateTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\XList as ListTransactions;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Update as UpdateDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Usage\Get as GetDatabaseUsage;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Usage\XList as ListDatabaseUsage;
|
||||
@@ -82,6 +88,7 @@ class Legacy extends Base
|
||||
$this->registerDocumentActions($service);
|
||||
$this->registerAttributeActions($service);
|
||||
$this->registerIndexActions($service);
|
||||
$this->registerTransactionActions($service);
|
||||
}
|
||||
|
||||
public function registerDatabaseActions(Service $service): void
|
||||
@@ -191,4 +198,14 @@ class Legacy extends Base
|
||||
$service->addAction(DeleteIndex::getName(), new DeleteIndex());
|
||||
$service->addAction(ListIndexes::getName(), new ListIndexes());
|
||||
}
|
||||
|
||||
private function registerTransactionActions(Service $service): void
|
||||
{
|
||||
$service->addAction(CreateTransaction::getName(), new CreateTransaction());
|
||||
$service->addAction(GetTransaction::getName(), new GetTransaction());
|
||||
$service->addAction(UpdateTransaction::getName(), new UpdateTransaction());
|
||||
$service->addAction(DeleteTransaction::getName(), new DeleteTransaction());
|
||||
$service->addAction(ListTransactions::getName(), new ListTransactions());
|
||||
$service->addAction(CreateOperations::getName(), new CreateOperations());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,12 @@ use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Rows\XList as ListR
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Update as UpdateTable;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Usage\Get as GetTableUsage;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\XList as ListTables;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\Create as CreateTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\Delete as DeleteTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\Get as GetTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\Operations\Create as CreateOperations;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\Update as UpdateTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Transactions\XList as ListTransactions;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Update as UpdateTablesDatabase;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Usage\Get as GetTablesDatabaseUsage;
|
||||
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Usage\XList as ListTablesDatabaseUsage;
|
||||
@@ -81,6 +87,7 @@ class TablesDB extends Base
|
||||
$this->registerColumnActions($service);
|
||||
$this->registerIndexActions($service);
|
||||
$this->registerRowActions($service);
|
||||
$this->registerTransactionActions($service);
|
||||
}
|
||||
|
||||
private function registerDatabaseActions(Service $service): void
|
||||
@@ -188,4 +195,14 @@ class TablesDB extends Base
|
||||
$service->addAction(IncrementRowColumn::getName(), new IncrementRowColumn());
|
||||
$service->addAction(DecrementRowColumn::getName(), new DecrementRowColumn());
|
||||
}
|
||||
|
||||
private function registerTransactionActions(Service $service): void
|
||||
{
|
||||
$service->addAction(CreateTransaction::getName(), new CreateTransaction());
|
||||
$service->addAction(GetTransaction::getName(), new GetTransaction());
|
||||
$service->addAction(UpdateTransaction::getName(), new UpdateTransaction());
|
||||
$service->addAction(DeleteTransaction::getName(), new DeleteTransaction());
|
||||
$service->addAction(ListTransactions::getName(), new ListTransactions());
|
||||
$service->addAction(CreateOperations::getName(), new CreateOperations());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Services\Registry;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Http\Transactions\Create as CreateTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Transactions\Delete as DeleteTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Transactions\Get as GetTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Transactions\Operations\Create as CreateOperations;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Transactions\Update as UpdateTransaction;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Transactions\XList as ListTransactions;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
/**
|
||||
* Registers all HTTP actions related to transactions in the module.
|
||||
*/
|
||||
class Transactions extends Base
|
||||
{
|
||||
public function register(Service $service): void
|
||||
{
|
||||
$service->addAction(CreateTransaction::getName(), new CreateTransaction());
|
||||
$service->addAction(GetTransaction::getName(), new GetTransaction());
|
||||
$service->addAction(UpdateTransaction::getName(), new UpdateTransaction());
|
||||
$service->addAction(DeleteTransaction::getName(), new DeleteTransaction());
|
||||
$service->addAction(ListTransactions::getName(), new ListTransactions());
|
||||
$service->addAction(CreateOperations::getName(), new CreateOperations());
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ class Operation extends Validator
|
||||
/** @var array<string> */
|
||||
private array $required = [
|
||||
'databaseId',
|
||||
'collectionId',
|
||||
'action',
|
||||
];
|
||||
|
||||
@@ -39,6 +38,27 @@ class Operation extends Validator
|
||||
'bulkDelete' => true,
|
||||
];
|
||||
|
||||
private string $collectionIdName = '';
|
||||
private string $documentIdName = '';
|
||||
|
||||
public function __construct(private readonly string $type)
|
||||
{
|
||||
switch ($this->type) {
|
||||
case 'legacy':
|
||||
$this->collectionIdName = 'collectionId';
|
||||
$this->documentIdName = 'documentId';
|
||||
break;
|
||||
case 'tablesdb':
|
||||
$this->collectionIdName = 'tableId';
|
||||
$this->documentIdName = 'rowId';
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid type provided.');
|
||||
}
|
||||
|
||||
$this->required[] = $this->collectionIdName;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
@@ -85,9 +105,9 @@ class Operation extends Validator
|
||||
// If action requires documentId, it must be present
|
||||
if (
|
||||
isset($this->requiresDocumentId[$value['action']]) &&
|
||||
!\array_key_exists('documentId', $value)
|
||||
!\array_key_exists($this->documentIdName, $value)
|
||||
) {
|
||||
$this->description = "Key 'documentId' is required for action '{$value['action']}'";
|
||||
$this->description = "Key '$this->documentIdName' is required for action '{$value['action']}'";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Legacy;
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Legacy;
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\TablesDB;
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Legacy;
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
+1
-2
@@ -1,13 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Transactions;
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Transactions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Transactions;
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Transactions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\TablesDB;
|
||||
namespace Tests\E2E\Services\Databases\TablesDB\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\TablesDB;
|
||||
namespace Tests\E2E\Services\Databases\TablesDB\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Legacy;
|
||||
namespace Tests\E2E\Services\Databases\TablesDB\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\TablesDB;
|
||||
namespace Tests\E2E\Services\Databases\TablesDB\Permissions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
@@ -0,0 +1,625 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Services\Databases\Legacy\Transactions;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
||||
class ACIDTest extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
use SideClient;
|
||||
|
||||
/**
|
||||
* Test atomicity - all operations succeed or all fail
|
||||
*/
|
||||
public function testAtomicity(): void
|
||||
{
|
||||
// Create database
|
||||
$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' => 'AtomicityTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection with unique constraint
|
||||
$collection = $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']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'AtomicityTest',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Add unique column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'email',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
// Add unique index
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'unique_email',
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'columns' => ['email']
|
||||
]);
|
||||
|
||||
sleep(3);
|
||||
|
||||
// Create first document outside transaction
|
||||
$doc1 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'email' => 'existing@example.com'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $doc1['headers']['status-code']);
|
||||
|
||||
// Create transaction
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(201, $transaction['headers']['status-code'], 'Transaction creation should succeed. Response: ' . json_encode($transaction));
|
||||
$this->assertArrayHasKey('$id', $transaction['body'], 'Transaction response should have $id. Response body: ' . json_encode($transaction['body']));
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Add operations - second one will fail due to unique constraint
|
||||
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'operations' => [
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'email' => 'newuser@example.com' // This should succeed
|
||||
]
|
||||
],
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'email' => 'existing@example.com' // This will fail - duplicate
|
||||
]
|
||||
],
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'email' => 'anotheruser@example.com' // This should not be created due to atomicity
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code'], 'Add operations failed. Response: ' . json_encode($response['body']));
|
||||
|
||||
// Attempt to commit - should fail due to unique constraint violation
|
||||
$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
|
||||
]);
|
||||
|
||||
if ($response['headers']['status-code'] === 200) {
|
||||
// If transaction succeeded, all documents should be created
|
||||
$documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
// Should have 4 documents total (1 original + 3 from transaction)
|
||||
// But since we have a unique constraint violation, this might fail
|
||||
$this->assertGreaterThanOrEqual(1, $documents['body']['total']);
|
||||
} else {
|
||||
$this->assertEquals(409, $response['headers']['status-code']); // Conflict error
|
||||
|
||||
// Verify NO new documents were created (atomicity)
|
||||
$documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(1, $documents['body']['total']); // Only the original document
|
||||
$this->assertEquals('existing@example.com', $documents['body']['documents'][0]['email']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test consistency - schema validation and constraints
|
||||
*/
|
||||
public function testConsistency(): void
|
||||
{
|
||||
// Create database
|
||||
$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' => 'ConsistencyTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection with required fields and constraints
|
||||
$collection = $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']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'ConsistencyTest',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Add required string column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'required_field',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
// Add integer column with min/max constraints
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/integer', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'age',
|
||||
'required' => true,
|
||||
'min' => 18,
|
||||
'max' => 100
|
||||
]);
|
||||
|
||||
sleep(3);
|
||||
|
||||
// Create transaction
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Add operations with both valid and invalid data
|
||||
$response = $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'operations' => [
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'required_field' => 'Valid User',
|
||||
'age' => 25 // Valid age
|
||||
]
|
||||
],
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'required_field' => 'Too Young User',
|
||||
'age' => 10 // Below minimum - will fail constraint
|
||||
]
|
||||
],
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'required_field' => 'Another Valid User',
|
||||
'age' => 30 // Valid but should not be created due to transaction failure
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Attempt to commit - should fail due to constraint violation
|
||||
$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->assertContains($response['headers']['status-code'], [400, 500], 'Transaction commit should fail due to validation. Response: ' . json_encode($response['body']));
|
||||
|
||||
// Verify no documents were created
|
||||
$documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(0, $documents['body']['total']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test isolation - concurrent transactions on same data
|
||||
*/
|
||||
public function testIsolation(): void
|
||||
{
|
||||
// Create database
|
||||
$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' => 'IsolationTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection
|
||||
$collection = $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']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'IsolationTest',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Add counter column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/integer', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'counter',
|
||||
'required' => true,
|
||||
'min' => 0,
|
||||
'max' => 1000000
|
||||
]);
|
||||
|
||||
sleep(2);
|
||||
|
||||
// Create initial document with counter
|
||||
$doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'shared_counter',
|
||||
'data' => [
|
||||
'counter' => 0
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $doc['headers']['status-code']);
|
||||
|
||||
// Create first transaction
|
||||
$transaction1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(201, $transaction1['headers']['status-code'], 'Transaction 1 creation should succeed');
|
||||
$this->assertArrayHasKey('$id', $transaction1['body'], 'Transaction 1 response should have $id');
|
||||
$transactionId1 = $transaction1['body']['$id'];
|
||||
|
||||
// Create second transaction
|
||||
$transaction2 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(201, $transaction2['headers']['status-code'], 'Transaction 2 creation should succeed');
|
||||
$this->assertArrayHasKey('$id', $transaction2['body'], 'Transaction 2 response should have $id');
|
||||
$transactionId2 = $transaction2['body']['$id'];
|
||||
|
||||
// Transaction 1: Increment counter by 10
|
||||
$this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId1}/operations", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'operations' => [
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'documentId' => 'shared_counter',
|
||||
'action' => 'increment',
|
||||
'data' => [
|
||||
'column' => 'counter',
|
||||
'value' => 10
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
// Transaction 2: Increment counter by 5
|
||||
$this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId2}/operations", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'operations' => [
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'documentId' => 'shared_counter',
|
||||
'action' => 'increment',
|
||||
'data' => [
|
||||
'column' => 'counter',
|
||||
'value' => 5
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
// Commit first transaction
|
||||
$response1 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId1}", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'commit' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response1['headers']['status-code']);
|
||||
|
||||
// Commit second transaction
|
||||
$response2 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId2}", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'commit' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response2['headers']['status-code']);
|
||||
|
||||
// Check final value - both increments should be applied
|
||||
$document = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/shared_counter", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
// Both increments should be applied: 0 + 10 + 5 = 15
|
||||
$this->assertEquals(15, $document['body']['counter']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test durability - committed data persists
|
||||
*/
|
||||
public function testDurability(): void
|
||||
{
|
||||
// Create database
|
||||
$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' => 'DurabilityTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection
|
||||
$collection = $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']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'DurabilityTest',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Add column
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'data',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
sleep(2);
|
||||
|
||||
// Create and commit transaction with multiple operations
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(201, $transaction['headers']['status-code'], 'Transaction creation should succeed');
|
||||
$this->assertArrayHasKey('$id', $transaction['body'], 'Transaction response should have $id');
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Add multiple operations
|
||||
$this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'operations' => [
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => 'durable_doc_1',
|
||||
'data' => [
|
||||
'data' => 'Important data 1'
|
||||
]
|
||||
],
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'create',
|
||||
'documentId' => 'durable_doc_2',
|
||||
'data' => [
|
||||
'data' => 'Important data 2'
|
||||
]
|
||||
],
|
||||
[
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collectionId,
|
||||
'action' => 'update',
|
||||
'documentId' => 'durable_doc_1',
|
||||
'data' => [
|
||||
'data' => 'Updated important data 1'
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
// 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'], 'Commit should succeed. Response: ' . json_encode($response['body']));
|
||||
$this->assertEquals('committed', $response['body']['status']);
|
||||
|
||||
// List all documents to see what was created
|
||||
$allDocs = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertGreaterThan(0, $allDocs['body']['total'], 'Should have created documents. Found: ' . json_encode($allDocs['body']));
|
||||
|
||||
// Verify documents exist and have correct data
|
||||
$document1 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/durable_doc_1", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $document1['headers']['status-code']);
|
||||
$this->assertEquals('Updated important data 1', $document1['body']['data']);
|
||||
|
||||
$document2 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/durable_doc_2", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $document2['headers']['status-code']);
|
||||
$this->assertEquals('Important data 2', $document2['body']['data']);
|
||||
|
||||
// Further update outside transaction to ensure persistence
|
||||
$update = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/durable_doc_1", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'data' => 'Modified outside transaction'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $update['headers']['status-code']);
|
||||
|
||||
// Verify the update persisted
|
||||
$document1 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/durable_doc_1", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals('Modified outside transaction', $document1['body']['data']);
|
||||
|
||||
// List all documents to verify total count
|
||||
$documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(2, $documents['body']['total']);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user