From fc8326417b469ba9f58323aad99b622b0ebefedf Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 23 May 2025 19:20:26 +0530 Subject: [PATCH 001/274] update: z-index to be the highest! --- src/Appwrite/Transformation/Adapter/Preview.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Transformation/Adapter/Preview.php b/src/Appwrite/Transformation/Adapter/Preview.php index 70af19a188..7c08777413 100644 --- a/src/Appwrite/Transformation/Adapter/Preview.php +++ b/src/Appwrite/Transformation/Adapter/Preview.php @@ -47,7 +47,7 @@ class Preview extends Adapter position: fixed; right: 16px; bottom: 16px; - z-index: 1; + z-index: calc(infinity); border-radius: var(--border-radius-S, 8px); border: var(--border-width-S, 1px) solid var(--color-border-neutral, #EDEDF0); background: var(--color-bgColor-neutral-primary, #FFF); From b556846be3441e1daa3bba3a4c0cf438a31924d7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:01:39 -0400 Subject: [PATCH 002/274] Add txn roles --- app/config/roles.php | 130 ++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/app/config/roles.php b/app/config/roles.php index bccc2837f5..bbe970f05d 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -3,105 +3,107 @@ use Appwrite\Auth\Auth; $member = [ - 'global', - 'public', - 'home', - 'console', - 'graphql', - 'sessions.write', 'account', - 'teams.read', - 'teams.write', + 'assistant.read', + 'avatars.read', + 'console', 'documents.read', 'documents.write', - 'files.read', - 'files.write', - 'projects.read', - 'locale.read', - 'avatars.read', 'execution.read', 'execution.write', + 'files.read', + 'files.write', + 'global', + 'graphql', + 'home', + 'locale.read', + 'projects.read', + 'public', + 'rules.read', + 'sessions.write', + 'subscribers.read', + 'subscribers.write', 'targets.read', 'targets.write', - 'subscribers.write', - 'subscribers.read', - 'assistant.read', - 'rules.read' + 'teams.read', + 'teams.write', ]; $admins = [ - 'global', - 'graphql', - 'sessions.write', - 'teams.read', - 'teams.write', - 'documents.read', - 'documents.write', - 'files.read', - 'files.write', + 'avatars.read', 'buckets.read', 'buckets.write', - 'users.read', - 'users.write', - 'databases.read', - 'databases.write', 'collections.read', 'collections.write', + 'databases.read', + 'databases.write', + 'documents.read', + 'documents.write', + 'execution.read', + 'execution.write', + 'files.read', + 'files.write', + 'functions.read', + 'functions.write', + 'global', + 'graphql', + 'health.read', + 'keys.read', + 'keys.write', + 'locale.read', + 'log.read', + 'log.write', + 'messages.read', + 'messages.write', + 'migrations.read', + 'migrations.write', 'platforms.read', 'platforms.write', 'projects.write', - 'keys.read', - 'keys.write', - 'webhooks.read', - 'webhooks.write', - 'locale.read', - 'avatars.read', - 'health.read', - 'functions.read', - 'functions.write', - 'sites.read', - 'sites.write', - 'log.read', - 'log.write', - 'execution.read', - 'execution.write', + 'providers.read', + 'providers.write', 'rules.read', 'rules.write', - 'migrations.read', - 'migrations.write', - 'vcs.read', - 'vcs.write', + 'sessions.write', + 'sites.read', + 'sites.write', + 'subscribers.read', + 'subscribers.write', 'targets.read', 'targets.write', - 'providers.write', - 'providers.read', - 'messages.write', - 'messages.read', - 'topics.write', - 'topics.read', - 'subscribers.write', - 'subscribers.read', + 'teams.read', + 'teams.write', 'tokens.read', 'tokens.write', + 'topics.read', + 'topics.write', + 'transactions.read', + 'transactions.write', + 'users.read', + 'users.write', + 'vcs.read', + 'vcs.write', + 'webhooks.read', + 'webhooks.write', ]; return [ Auth::USER_ROLE_GUESTS => [ 'label' => 'Guests', 'scopes' => [ - 'global', - 'public', - 'home', + 'avatars.read', 'console', - 'graphql', - 'sessions.write', 'documents.read', 'documents.write', + 'execution.write', 'files.read', 'files.write', + 'global', + 'graphql', + 'home', 'locale.read', - 'avatars.read', - 'execution.write', + 'public', + 'sessions.write', ], ], Auth::USER_ROLE_USERS => [ From fc4b82d017a85730b37e4c17266598c96725c38a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:01:45 -0400 Subject: [PATCH 003/274] Add txn scopes --- app/config/scopes.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/config/scopes.php b/app/config/scopes.php index 7dea7b1cd5..69cc94a009 100644 --- a/app/config/scopes.php +++ b/app/config/scopes.php @@ -40,6 +40,12 @@ return [ // List of publicly visible scopes 'indexes.write' => [ 'description' => 'Access to create, update, and delete your project\'s database collection\'s indexes', ], + 'transactions.read' => [ + 'description' => 'Access to read your project\'s database transactions', + ], + 'transactions.write' => [ + 'description' => 'Access to create, update, and delete your project\'s database transactions', + ], 'documents.read' => [ 'description' => 'Access to read your project\'s database documents', ], From 11ce3941811f97521b6000390b3d70ef648e797c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:01:56 -0400 Subject: [PATCH 004/274] Add txn errors --- app/config/errors.php | 37 +++++++++++++++++++++++++++++++ src/Appwrite/Extend/Exception.php | 10 +++++++++ 2 files changed, 47 insertions(+) diff --git a/app/config/errors.php b/app/config/errors.php index 8365e8c705..7a48e7f46f 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -810,6 +810,43 @@ return [ 'code' => 409, ], + /** Transactions */ + Exception::TRANSACTION_NOT_FOUND => [ + 'name' => Exception::TRANSACTION_NOT_FOUND, + 'description' => 'Transaction with the requested ID could not be found.', + 'code' => 404, + ], + Exception::TRANSACTION_ALREADY_EXISTS => [ + 'name' => Exception::TRANSACTION_ALREADY_EXISTS, + 'description' => 'Transaction with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + 'code' => 409, + ], + Exception::TRANSACTION_INVALID => [ + 'name' => Exception::TRANSACTION_INVALID, + 'description' => 'The transaction is invalid. Please check the transaction data and try again.', + 'code' => 400, + ], + Exception::TRANSACTION_EXPIRED => [ + 'name' => Exception::TRANSACTION_EXPIRED, + 'description' => 'The transaction has expired. Please create a new transaction and try again.', + 'code' => 410, + ], + Exception::TRANSACTION_CONFLICT => [ + 'name' => Exception::TRANSACTION_CONFLICT, + 'description' => 'The transaction has a conflict. Please resolve the conflict and try again.', + 'code' => 409, + ], + Exception::TRANSACTION_LIMIT_EXCEEDED => [ + 'name' => Exception::TRANSACTION_LIMIT_EXCEEDED, + 'description' => 'The maximum number of transactions has been reached.', + 'code' => 400, + ], + Exception::TRANSACTION_NOT_READY => [ + 'name' => Exception::TRANSACTION_NOT_READY, + 'description' => 'The transaction is not ready yet. Please try again later.', + 'code' => 400, + ], + /** Project Errors */ Exception::PROJECT_NOT_FOUND => [ 'name' => Exception::PROJECT_NOT_FOUND, diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 3af6d9962c..2e9911683c 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -229,6 +229,16 @@ class Exception extends \Exception public const INDEX_INVALID = 'index_invalid'; public const INDEX_DEPENDENCY = 'index_dependency'; + /** Transactions */ + public const TRANSACTION_NOT_FOUND = 'transaction_not_found'; + public const TRANSACTION_ALREADY_EXISTS = 'transaction_already_exists'; + public const TRANSACTION_INVALID = 'transaction_invalid'; + public const TRANSACTION_EXPIRED = 'transaction_expired'; + public const TRANSACTION_CONFLICT = 'transaction_conflict'; + public const TRANSACTION_LIMIT_EXCEEDED = 'transaction_limit_exceeded'; + public const TRANSACTION_NOT_READY = 'transaction_not_ready'; + + /** Projects */ public const PROJECT_NOT_FOUND = 'project_not_found'; public const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled'; From c11fd38f0fa3500c46b72c12596add82ea673547 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:02:17 -0400 Subject: [PATCH 005/274] Add txn models --- src/Appwrite/SDK/Response.php | 3 +- .../Utopia/Database/Validator/Operation.php | 76 +++++ src/Appwrite/Utopia/Response.php | 285 +++++++++--------- .../Utopia/Response/Model/Transaction.php | 54 ++++ 4 files changed, 271 insertions(+), 147 deletions(-) create mode 100644 src/Appwrite/Utopia/Database/Validator/Operation.php create mode 100644 src/Appwrite/Utopia/Response/Model/Transaction.php diff --git a/src/Appwrite/SDK/Response.php b/src/Appwrite/SDK/Response.php index e87813024b..2b034691a8 100644 --- a/src/Appwrite/SDK/Response.php +++ b/src/Appwrite/SDK/Response.php @@ -2,12 +2,11 @@ namespace Appwrite\SDK; -class Response +readonly class Response { /** * @param int $code * @param string|array $model - * @param string $description */ public function __construct( private int $code, diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php new file mode 100644 index 0000000000..3f9a15673a --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -0,0 +1,76 @@ +description; + } + + public function isArray(): bool + { + return true; + } + + /** + * @param mixed $value + */ + public function isValid($value): bool + { + // Must be array‑like + if (!\is_array($value)) { + $this->description = 'Value must be an array'; + return false; + } + + // Mandatory keys + $required = ['databaseId', 'collectionId', 'action', 'payload']; + foreach ($required as $key) { + if (!\array_key_exists($key, $value)) { + $this->description = "Missing required key: {$key}"; + return false; + } + } + + // databaseId / collectionId / action must be non‑empty strings + foreach (['databaseId', 'collectionId', 'action'] as $key) { + if (!\is_string($value[$key]) || \trim($value[$key]) === '') { + $this->description = "Key '{$key}' must be a non‑empty string"; + return false; + } + } + + // Validate action + if (!\in_array($value['action'], $this->actions, true)) { + $this->description = "Key 'action' must be one of: " . \implode(', ', $this->actions); + return false; + } + + // Payload must be array (can be empty) + if (!\is_array($value['payload'])) { + $this->description = "Key 'payload' must be an array"; + return false; + } + + return true; + } + + public function getType(): string + { + return self::TYPE_OBJECT; + } +} diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3d69ac1291..a376dd9cb6 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -144,7 +144,6 @@ class Response extends SwooleResponse public const MODEL_METRIC_LIST = 'metricList'; public const MODEL_METRIC_BREAKDOWN = 'metricBreakdown'; public const MODEL_ERROR_DEV = 'errorDev'; - public const MODEL_BASE_LIST = 'baseList'; public const MODEL_USAGE_DATABASES = 'usageDatabases'; public const MODEL_USAGE_DATABASE = 'usageDatabase'; public const MODEL_USAGE_COLLECTION = 'usageCollection'; @@ -166,6 +165,8 @@ class Response extends SwooleResponse public const MODEL_INDEX_LIST = 'indexList'; public const MODEL_DOCUMENT = 'document'; public const MODEL_DOCUMENT_LIST = 'documentList'; + public const MODEL_TRANSACTION = 'transaction'; + public const MODEL_TRANSACTION_LIST = 'transactionList'; // Database Attributes public const MODEL_ATTRIBUTE = 'attribute'; @@ -337,13 +338,6 @@ class Response extends SwooleResponse // Console public const MODEL_CONSOLE_VARIABLES = 'consoleVariables'; - // Deprecated - public const MODEL_PERMISSIONS = 'permissions'; - public const MODEL_RULE = 'rule'; - public const MODEL_TASK = 'task'; - public const MODEL_DOMAIN = 'domain'; - public const MODEL_DOMAIN_LIST = 'domainList'; - // Tests (keep last) public const MODEL_MOCK = 'mock'; @@ -365,7 +359,7 @@ class Response extends SwooleResponse /** * Response constructor. * - * @param float $time + * @param SwooleHTTPResponse $response */ public function __construct(SwooleHTTPResponse $response) { @@ -376,165 +370,166 @@ class Response extends SwooleResponse ->setModel(new Error()) ->setModel(new ErrorDev()) // Lists - ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) - ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) - ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) - ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) - ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) - ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) - ->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY)) - ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG)) - ->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE)) - ->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET)) - ->setModel(new BaseList('Resource Tokens List', self::MODEL_RESOURCE_TOKEN_LIST, 'tokens', self::MODEL_RESOURCE_TOKEN)) - ->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM)) - ->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP)) - ->setModel(new BaseList('Sites List', self::MODEL_SITE_LIST, 'sites', self::MODEL_SITE)) - ->setModel(new BaseList('Site Templates List', self::MODEL_TEMPLATE_SITE_LIST, 'templates', self::MODEL_TEMPLATE_SITE)) - ->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION)) - ->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION)) - ->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION)) - ->setModel(new BaseList('Framework Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', self::MODEL_PROVIDER_REPOSITORY_FRAMEWORK)) - ->setModel(new BaseList('Runtime Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', self::MODEL_PROVIDER_REPOSITORY_RUNTIME)) - ->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH)) - ->setModel(new BaseList('Frameworks List', self::MODEL_FRAMEWORK_LIST, 'frameworks', self::MODEL_FRAMEWORK)) - ->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME)) - ->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT)) - ->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION)) - ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) - ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) ->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false)) - ->setModel(new BaseList('Dev Keys List', self::MODEL_DEV_KEY_LIST, 'devKeys', self::MODEL_DEV_KEY, true, false)) ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) - ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) - ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) + ->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH)) + ->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET)) + ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) ->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT)) - ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) + ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) - ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) - ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) - ->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE)) - ->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS)) - ->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE)) + ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) + ->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT)) + ->setModel(new BaseList('Dev Keys List', self::MODEL_DEV_KEY_LIST, 'devKeys', self::MODEL_DEV_KEY, true, false)) + ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) + ->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION)) + ->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE)) + ->setModel(new BaseList('Framework Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', self::MODEL_PROVIDER_REPOSITORY_FRAMEWORK)) + ->setModel(new BaseList('Frameworks List', self::MODEL_FRAMEWORK_LIST, 'frameworks', self::MODEL_FRAMEWORK)) + ->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION)) + ->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION)) + ->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY)) + ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) + ->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION)) + ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) ->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE)) - ->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER)) - ->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE)) - ->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC)) + ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG)) + ->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP)) + ->setModel(new BaseList('Message List', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE)) + ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) + ->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT)) + ->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION)) + ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) + ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) + ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) + ->setModel(new BaseList('Provider List', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER)) + ->setModel(new BaseList('Resource Tokens List', self::MODEL_RESOURCE_TOKEN_LIST, 'tokens', self::MODEL_RESOURCE_TOKEN)) + ->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE)) + ->setModel(new BaseList('Runtime Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', self::MODEL_PROVIDER_REPOSITORY_RUNTIME)) + ->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME)) + ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) + ->setModel(new BaseList('Site Templates List', self::MODEL_TEMPLATE_SITE_LIST, 'templates', self::MODEL_TEMPLATE_SITE)) + ->setModel(new BaseList('Sites List', self::MODEL_SITE_LIST, 'sites', self::MODEL_SITE)) + ->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION)) + ->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS)) ->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER)) ->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET)) - ->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION)) - ->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT)) - ->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION)) + ->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM)) + ->setModel(new BaseList('Topic List', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC)) + ->setModel(new BaseList('Transaction List', self::MODEL_TRANSACTION_LIST, 'transactions', self::MODEL_TRANSACTION)) + ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) ->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT)) + ->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE)) + ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) // Entities - ->setModel(new Database()) - ->setModel(new Collection()) - ->setModel(new Attribute()) - ->setModel(new AttributeList()) - ->setModel(new AttributeString()) - ->setModel(new AttributeInteger()) - ->setModel(new AttributeFloat()) - ->setModel(new AttributeBoolean()) - ->setModel(new AttributeEmail()) - ->setModel(new AttributeEnum()) - ->setModel(new AttributeIP()) - ->setModel(new AttributeURL()) - ->setModel(new AttributeDatetime()) - ->setModel(new AttributeRelationship()) - ->setModel(new Index()) - ->setModel(new ModelDocument()) - ->setModel(new Log()) - ->setModel(new User()) - ->setModel(new AlgoMd5()) - ->setModel(new AlgoSha()) - ->setModel(new AlgoPhpass()) + ->setModel(new Account()) + ->setModel(new AlgoArgon2()) ->setModel(new AlgoBcrypt()) + ->setModel(new AlgoMd5()) + ->setModel(new AlgoPhpass()) ->setModel(new AlgoScrypt()) ->setModel(new AlgoScryptModified()) - ->setModel(new AlgoArgon2()) - ->setModel(new Account()) - ->setModel(new Preferences()) - ->setModel(new Session()) + ->setModel(new AlgoSha()) + ->setModel(new Attribute()) + ->setModel(new AttributeBoolean()) + ->setModel(new AttributeDatetime()) + ->setModel(new AttributeEmail()) + ->setModel(new AttributeEnum()) + ->setModel(new AttributeFloat()) + ->setModel(new AttributeIP()) + ->setModel(new AttributeInteger()) + ->setModel(new AttributeList()) + ->setModel(new AttributeRelationship()) + ->setModel(new AttributeString()) + ->setModel(new AttributeURL()) + ->setModel(new AuthProvider()) + ->setModel(new Branch()) + ->setModel(new Bucket()) + ->setModel(new Collection()) + ->setModel(new ConsoleVariables()) + ->setModel(new Continent()) + ->setModel(new Country()) + ->setModel(new Currency()) + ->setModel(new Database()) + ->setModel(new Deployment()) + ->setModel(new DetectionFramework()) + ->setModel(new DetectionRuntime()) + ->setModel(new DevKey()) + ->setModel(new Execution()) + ->setModel(new File()) + ->setModel(new Framework()) + ->setModel(new FrameworkAdapter()) + ->setModel(new Func()) + ->setModel(new Headers()) + ->setModel(new HealthAntivirus()) + ->setModel(new HealthCertificate()) + ->setModel(new HealthQueue()) + ->setModel(new HealthStatus()) + ->setModel(new HealthTime()) + ->setModel(new HealthVersion()) ->setModel(new Identity()) - ->setModel(new Token()) + ->setModel(new Index()) + ->setModel(new Installation()) ->setModel(new JWT()) + ->setModel(new Key()) + ->setModel(new Language()) ->setModel(new Locale()) ->setModel(new LocaleCode()) - ->setModel(new File()) - ->setModel(new Bucket()) - ->setModel(new ResourceToken()) - ->setModel(new Team()) + ->setModel(new Log()) + ->setModel(new MFAChallenge()) + ->setModel(new MFAFactors()) + ->setModel(new MFARecoveryCodes()) + ->setModel(new MFAType()) ->setModel(new Membership()) - ->setModel(new Site()) - ->setModel(new TemplateSite()) - ->setModel(new TemplateFramework()) - ->setModel(new Func()) - ->setModel(new TemplateFunction()) - ->setModel(new TemplateRuntime()) - ->setModel(new TemplateVariable()) - ->setModel(new Installation()) + ->setModel(new Message()) + ->setModel(new Metric()) + ->setModel(new MetricBreakdown()) + ->setModel(new Migration()) + ->setModel(new MigrationFirebaseProject()) + ->setModel(new MigrationReport()) + ->setModel(new MockNumber()) + ->setModel(new ModelDocument()) + ->setModel(new Phone()) + ->setModel(new Platform()) + ->setModel(new Preferences()) + ->setModel(new Project()) + ->setModel(new Provider()) ->setModel(new ProviderRepository()) ->setModel(new ProviderRepositoryFramework()) ->setModel(new ProviderRepositoryRuntime()) - ->setModel(new DetectionFramework()) - ->setModel(new DetectionRuntime()) - ->setModel(new VcsContent()) - ->setModel(new Branch()) - ->setModel(new Runtime()) - ->setModel(new Framework()) - ->setModel(new FrameworkAdapter()) - ->setModel(new Deployment()) - ->setModel(new Execution()) - ->setModel(new Project()) - ->setModel(new Webhook()) - ->setModel(new Key()) - ->setModel(new DevKey()) - ->setModel(new MockNumber()) - ->setModel(new AuthProvider()) - ->setModel(new Platform()) - ->setModel(new Variable()) - ->setModel(new Country()) - ->setModel(new Continent()) - ->setModel(new Language()) - ->setModel(new Currency()) - ->setModel(new Phone()) - ->setModel(new HealthAntivirus()) - ->setModel(new HealthQueue()) - ->setModel(new HealthStatus()) - ->setModel(new HealthCertificate()) - ->setModel(new HealthTime()) - ->setModel(new HealthVersion()) - ->setModel(new Metric()) - ->setModel(new MetricBreakdown()) - ->setModel(new UsageDatabases()) - ->setModel(new UsageDatabase()) - ->setModel(new UsageCollection()) - ->setModel(new UsageUsers()) - ->setModel(new UsageStorage()) - ->setModel(new UsageBuckets()) - ->setModel(new UsageFunctions()) - ->setModel(new UsageFunction()) - ->setModel(new UsageSites()) - ->setModel(new UsageSite()) - ->setModel(new UsageProject()) - ->setModel(new Headers()) - ->setModel(new Specification()) + ->setModel(new ResourceToken()) ->setModel(new Rule()) - ->setModel(new TemplateSMS()) - ->setModel(new TemplateEmail()) - ->setModel(new ConsoleVariables()) - ->setModel(new MFAChallenge()) - ->setModel(new MFARecoveryCodes()) - ->setModel(new MFAType()) - ->setModel(new MFAFactors()) - ->setModel(new Provider()) - ->setModel(new Message()) - ->setModel(new Topic()) + ->setModel(new Runtime()) + ->setModel(new Session()) + ->setModel(new Site()) + ->setModel(new Specification()) ->setModel(new Subscriber()) ->setModel(new Target()) - ->setModel(new Migration()) - ->setModel(new MigrationReport()) - ->setModel(new MigrationFirebaseProject()) + ->setModel(new Team()) + ->setModel(new TemplateEmail()) + ->setModel(new TemplateFramework()) + ->setModel(new TemplateFunction()) + ->setModel(new TemplateRuntime()) + ->setModel(new TemplateSMS()) + ->setModel(new TemplateSite()) + ->setModel(new TemplateVariable()) + ->setModel(new Token()) + ->setModel(new Topic()) + ->setModel(new UsageBuckets()) + ->setModel(new UsageCollection()) + ->setModel(new UsageDatabase()) + ->setModel(new UsageDatabases()) + ->setModel(new UsageFunction()) + ->setModel(new UsageFunctions()) + ->setModel(new UsageProject()) + ->setModel(new UsageSite()) + ->setModel(new UsageSites()) + ->setModel(new UsageStorage()) + ->setModel(new UsageUsers()) + ->setModel(new User()) + ->setModel(new Variable()) + ->setModel(new VcsContent()) + ->setModel(new Webhook()) // Tests (keep last) ->setModel(new Mock()); diff --git a/src/Appwrite/Utopia/Response/Model/Transaction.php b/src/Appwrite/Utopia/Response/Model/Transaction.php new file mode 100644 index 0000000000..c6eafd18b9 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Transaction.php @@ -0,0 +1,54 @@ +addRule('$id', [ + 'type' => self::TYPE_STRING, + 'description' => 'Transaction ID.', + 'default' => '', + 'example' => '259125845563242502', + ]) + ->addRule('$createdAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Transaction creation time in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('$updatedAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Transaction update date in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.', + 'default' => 'pending', + 'example' => 'pending', + ]) + ->addRule('expiresAt', [ + 'type' => self::TYPE_DATETIME, + 'description' => 'Expiration time in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE, + ]); + } + + public function getName(): string + { + return 'Transaction'; + } + + public function getType(): string + { + return Response::MODEL_TRANSACTION; + } +} From cfc6d5fe2b23f80a19ff194616ed5d886a086b06 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:02:32 -0400 Subject: [PATCH 006/274] Add txn collections --- app/config/collections/projects.php | 148 ++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index 48a0938a1c..5e0b1b17b5 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2510,4 +2510,152 @@ return [ ], ], ], + + 'transactions' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('transactions'), + 'name' => 'Transactions', + 'attributes' => [ + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_STRING, + 'size' => 16, // pending | committing | committed | rolled_back | failed + 'signed' => true, + 'required' => false, + 'default' => 'pending', + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('expiresAt'), + 'type' => Database::VAR_DATETIME, + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('committedAt'), + 'type' => Database::VAR_DATETIME, + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('rolledBackAt'), + 'type' => Database::VAR_DATETIME, + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_status'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['status'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_expires'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['expiresAt'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + ], + ], + + 'transactionLogs' => [ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('transactionLogs'), + 'name' => 'Transaction Logs', + 'attributes' => [ + [ + '$id' => ID::custom('transactionInternalId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('databaseInternalId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('collectionInternalId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('documentId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('action'), + 'type' => Database::VAR_STRING, + 'size' => 32, // create | update | upsert | increment | decrement | delete + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('data'), + 'type' => Database::VAR_STRING, + 'size' => 65535, + 'signed' => false, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => ['json'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_transaction'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['transactionInternalId'], + 'lengths' => [], + 'orders' => [], + ], + [ + '$id' => ID::custom('_key_db_coll'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['databaseId', 'collectionId'], + 'lengths' => [], + 'orders' => [], + ], + ], + ], ]; From 4e2727292eb35333a1051f98f7cf6ba5d6dd135e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:03:33 -0400 Subject: [PATCH 007/274] Add txn routes --- app/controllers/api/databases.php | 233 ++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 0e85171772..31b0585a7e 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1536,6 +1536,239 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); }); +App::post('/v1/databases/transactions') + ->desc('Create transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'createTransaction', + description: '/docs/references/databases/create-transaction.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_TRANSACTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('ttl', 300, new Integer(), 'Seconds before the transaction expires.', true) + ->inject('response') + ->inject('dbForProject') + ->action(function (int $ttl, Response $response, Database $dbForProject) { + $transaction = $dbForProject->createDocument('transactions', new Document([ + '$id' => ID::unique(), + 'status' => 'pending', + 'expiresAt' => DateTime::addSeconds(new \DateTime(), $ttl), + ])); + + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->dynamic($transaction, Response::MODEL_TRANSACTION); + }); + +App::post('/v1/databases/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: 'databases', + group: 'transactions', + name: 'createOperations', + description: '/docs/references/databases/create-operations.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_TRANSACTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('transactionId', '', new UID(), 'Transaction ID.') + ->param('operations', [], new ArrayList(new Operation()), 'Array of staged operations.', true) + ->inject('response') + ->inject('dbForProject') + ->action(function (string $transactionId, array $operations, Response $response, Database $dbForProject) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + + if ($transaction->isEmpty() || $transaction['status'] !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + $staged = []; + foreach ($operations as $op) { + $staged[] = new Document([ + '$id' => ID::unique(), + 'transactionId' => $transactionId, + 'databaseId' => $op['databaseId'] ?? null, + 'collectionId' => $op['collectionId'] ?? null, + 'documentId' => $op['documentId'] ?? null, + 'action' => $op['action'], + 'data' => $op['data'] ?? [], + ]); + } + + $dbForProject->createDocuments('transactionLogs', $staged); + + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->dynamic($transaction, Response::MODEL_TRANSACTION); + }); + +App::get('/v1/databases/transactions/:transactionId') + ->desc('Get transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'getTransaction', + description: '/docs/references/databases/get-transaction.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_TRANSACTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('transactionId', '', new UID(), 'Transaction ID.') + ->inject('response') + ->inject('dbForProject') + ->action(function (string $transactionId, Response $response, Database $dbForProject) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + + $response + ->setStatusCode(Response::STATUS_CODE_OK) + ->dynamic($transaction, Response::MODEL_TRANSACTION); + }); + +App::patch('/v1/databases/transactions/:transactionId') + ->desc('Update transaction (commit / rollback)') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'updateTransaction', + description: '/docs/references/databases/update-transaction.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_TRANSACTION, + ) + ], + contentType: ContentType::JSON + )) + ->param('transactionId', '', new UID(), 'Transaction ID.') + ->param('action', '', new WhiteList(['commit','rollback']), 'Action to take, commit or rollback.') + ->inject('response') + ->inject('dbForProject') + ->inject('project') + ->action(function (string $transactionId, string $action, ?string $reason, Response $response, Database $dbForProject, Document $project) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); + } + + switch ($action) { + case 'commit': + // Get staged operations + $operations = $dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc('$sequence'), + ]); + + $creates = $updates = $deletes = []; + + foreach ($operations as $operation) { + $databaseId = $operation['databaseInternalId']; + $collectionId = $operation['collectionInternalId']; + $documentId = $operation['documentInternalId']; + + switch ($operation['action']) { + case 'create': + $creates[$databaseId][$collectionId][] = new Document([ + '$id' => $documentId ?? ID::unique(), + ...$operation['data'] + ]); + break; + case 'update': + case 'upsert': + $updates[$databaseId][$collectionId][] = new Document([ + '$id' => $documentId, + ...$operation['data'], + ]); + break; + case 'delete': + $deletes[$databaseId][$collectionId][] = $documentId; + break; + } + } + + unset($databaseId, $collectionId); + + $dbForProject->withTransaction(function () use ($dbForProject, $creates, $updates, $deletes) { + foreach ($creates as $databaseId => $collectionDocs) { + foreach ($collectionDocs as $collectionId => $docs) { + $dbForProject->createDocuments("database_{$databaseId}_collection_{$collectionId}", $docs); + } + } + foreach ($updates as $databaseId => $collectionDocs) { + foreach ($collectionDocs as $collectionId => $docs) { + $dbForProject->updateDocuments("database_{$databaseId}_collection_{$collectionId}", $docs); + } + } + foreach ($deletes as $databaseId => $collectionDocs) { + foreach ($collectionDocs as $collectionId => $docs) { + $dbForProject->deleteDocuments("database_{$databaseId}_collection_{$collectionId}", $docs); + } + } + }); + + $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committed', + ])); + + break; + + case 'rollback': + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc('$sequence'), + ]); + + $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'rolled_back', + 'rolledBackAt' => DateTime::now(), + 'reason' => $reason ?? 'user_request', + ])); + break; + } + + $response + ->setStatusCode(Response::STATUS_CODE_OK) + ->dynamic($transaction, Response::MODEL_TRANSACTION); + }); + App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->alias('/v1/database/collections/:collectionId/attributes/url') ->desc('Create URL attribute') From 705e5dd45bbf8c3292ca284abab2e7a19f3391f3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:03:58 -0400 Subject: [PATCH 008/274] Add txn support to existing routes --- app/controllers/api/databases.php | 601 +++++++++++++++++++++--------- 1 file changed, 422 insertions(+), 179 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 31b0585a7e..269018033a 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -13,6 +13,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Parameter; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; +use Appwrite\Utopia\Database\Validator\Operation; use Appwrite\Utopia\Database\Validator\Queries\Attributes; use Appwrite\Utopia\Database\Validator\Queries\Collections; use Appwrite\Utopia\Database\Validator\Queries\Databases; @@ -3492,17 +3493,18 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ] ) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') + ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('data', [], new JSON(), 'Document data as JSON object.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('response') ->inject('dbForProject') ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, ?string $documentId, string $collectionId, string|array|null $data, ?array $permissions, ?array $documents, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $collectionId, ?string $documentId, string|array|null $data, ?array $permissions, ?array $documents, ?string $transactionId, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = \is_string($data) ? \json_decode($data, true) : $data; @@ -3565,6 +3567,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for collections with relationship attributes'); } + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + } + $setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk) { $allowedPermissions = [ Database::PERMISSION_READ, @@ -3727,26 +3739,46 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') return $document; }, $documents); - try { - $dbForProject->createDocuments( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - $documents - ); - } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); - } catch (NotFoundException) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } catch (RelationshipException $e) { - throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); - } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); - } + if (!empty($transactionId)) { + $operations = []; + foreach ($documents as $document) { + $operations[] = new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'create', + 'documentId' => $document->getId(), + 'data' => $document->getArrayCopy(), + ]); + } - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setContext('collection', $collection) - ->setContext('database', $database); + try { + $dbForProject->createDocuments('transactionLogs', $operations); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (NotFoundException) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } + } else { + try { + $dbForProject->createDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documents + ); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (NotFoundException) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } + } // Add $collectionId and $databaseId for all documents $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { @@ -3786,12 +3818,20 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $processDocument($collection, $document); } + $response->setStatusCode(Response::STATUS_CODE_CREATED); + + if (empty($transactionId)) { + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setContext('collection', $collection) + ->setContext('database', $database); + } + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); // per collection - $response->setStatusCode(Response::STATUS_CODE_CREATED); - if ($isBulk) { $response->dynamic(new Document([ 'total' => count($documents), @@ -3801,9 +3841,11 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') return; } - $queueForEvents - ->setParam('documentId', $documents[0]->getId()) - ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); + if (empty($transactionId)) { + $queueForEvents + ->setParam('documentId', $documents[0]->getId()) + ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create'); + } $response->dynamic( $documents[0], @@ -4244,12 +4286,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->param('documentId', '', new UID(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && \is_null($permissions)) { @@ -4269,6 +4312,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::COLLECTION_NOT_FOUND); } + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + } + // Read permission should not be required for update $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($document->isEmpty()) { @@ -4388,20 +4441,31 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); - try { - $document = $dbForProject->updateDocument( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - $document->getId(), - $newDocument - ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); - } catch (RelationshipException $e) { - throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); - } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + if (!empty($transactionId)) { + $dbForProject->createDocument('transactionLog', new Document([ + 'transactionInternalId' => $transaction->getSequence(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'documentId' => $document->getId(), + 'data' => $newDocument->getArrayCopy(), + 'action' => 'update', + ])); + } else { + try { + $document = $dbForProject->updateDocument( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $document->getId(), + $newDocument + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } } // Add $collectionId and $databaseId for all documents @@ -4447,13 +4511,15 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ) ); - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setContext('collection', $collection) - ->setContext('database', $database) - ->setPayload($response->getPayload(), sensitive: $relationships); + if (empty($transactionId)) { + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setContext('collection', $collection) + ->setContext('database', $database) + ->setPayload($response->getPayload(), sensitive: $relationships); + } $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -4488,12 +4554,13 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->param('documentId', '', new CustomId(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include all required attributes of the document to be created or updated.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && \is_null($permissions)) { @@ -4513,6 +4580,16 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::COLLECTION_NOT_FOUND); } + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + } + // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions, [ Database::PERMISSION_READ, @@ -4622,26 +4699,36 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); - $upserted = []; - try { - $modified = $dbForProject->createOrUpdateDocuments( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - [$newDocument], - onNext: function (Document $document) use (&$upserted) { - $upserted[] = $document; - }, - ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); - } catch (RelationshipException $e) { - throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); - } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + if (!empty($transactionId)) { + $dbForProject->createDocument('transactionLog', new Document([ + 'transactionInternalId' => $transaction->getSequence(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'action' => 'update', + 'data' => $newDocument->getArrayCopy(), + ])); + + $upserted = $newDocument; + } else { + try { + $dbForProject->createOrUpdateDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + [$newDocument], + onNext: function (Document $document) use (&$upserted) { + $upserted = $document; + }, + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } } - $document = $upserted[0]; // Add $collectionId and $databaseId for all documents $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { $document->setAttribute('$databaseId', $database->getId()); @@ -4675,7 +4762,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen } }; - $processDocument($collection, $document); + $processDocument($collection, $upserted); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), @@ -4685,15 +4772,17 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ) ); - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setContext('collection', $collection) - ->setContext('database', $database) - ->setPayload($response->getPayload(), sensitive: $relationships); + if (!empty($transactionId)) { + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setParam('documentId', $upserted->getId()) + ->setContext('collection', $collection) + ->setContext('database', $database) + ->setPayload($response->getPayload(), sensitive: $relationships); + } - $response->dynamic($document, Response::MODEL_DOCUMENT); + $response->dynamic($upserted, Response::MODEL_DOCUMENT); }); App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/increment') @@ -4727,11 +4816,12 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->param('attribute', '', new Key(), 'Attribute key.') ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) ->param('max', null, new Numeric(), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -4742,33 +4832,60 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::COLLECTION_NOT_FOUND); } - try { - $document = $dbForProject->increaseDocumentAttribute( - collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - id: $documentId, - attribute: $attribute, - value: $value, - max: $max - ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (NotFoundException) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } catch (LimitException) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the maximum value of ' . $max); - } catch (TypeException) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number'); + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + + $dbForProject->createDocument('transactionLog', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'increment', + 'data' => [ + 'attribute' => $attribute, + 'value' => $value, + 'max' => $max, + ] + ])); + + $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId); + } else { + try { + $document = $dbForProject->increaseDocumentAttribute( + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $documentId, + attribute: $attribute, + value: $value, + max: $max + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (NotFoundException) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } catch (LimitException) { + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the maximum value of ' . $max); + } catch (TypeException) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number'); + } } $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collectionId) - ->setContext('collection', $collection) - ->setContext('database', $database); + if (!empty($transactionId)) { + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collectionId) + ->setContext('collection', $collection) + ->setContext('database', $database); + } $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -4804,11 +4921,24 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->param('attribute', '', new Key(), 'Attribute key.') ->param('value', 1, new Numeric(), 'Value to decrement the attribute by. The value must be a number.', true) ->param('min', null, new Numeric(), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function ( + string $databaseId, + string $collectionId, + string $documentId, + string $attribute, + int|float $value, + int|float|null $min, + ?string $transactionId, + Response $response, + Database $dbForProject, + Event $queueForEvents, + StatsUsage $queueForStatsUsage + ) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -4819,33 +4949,64 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::COLLECTION_NOT_FOUND); } - try { - $document = $dbForProject->decreaseDocumentAttribute( - collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - id: $documentId, - attribute: $attribute, - value: $value, - min: $min + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + + $dbForProject->createDocument('transactionLog', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'decrement', + 'data' => [ + 'attribute' => $attribute, + 'value' => $value, + 'min' => $min, + ], + ])); + + // Fetch current document for response without mutating + $document = $dbForProject->getDocument( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documentId ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (NotFoundException) { - throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); - } catch (LimitException) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the minimum value of ' . $min); - } catch (TypeException) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number'); + } else { + try { + $document = $dbForProject->decreaseDocumentAttribute( + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $documentId, + attribute: $attribute, + value: $value, + min: $min + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (NotFoundException) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } catch (LimitException) { + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the minimum value of ' . $min); + } catch (TypeException) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number'); + } } $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collectionId) - ->setContext('collection', $collection) - ->setContext('database', $database); + if (empty($transactionId)) { + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collectionId) + ->setContext('collection', $collection) + ->setContext('database', $database); + } $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -4878,12 +5039,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $databaseId, string $collectionId, string|array $data, array $queries, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) { + ->action(function (string $databaseId, string $collectionId, string|array $data, array $queries, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) { $data = \is_string($data) ? \json_decode($data, true) : $data; @@ -4911,6 +5073,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk update is not supported for collections with relationship attributes'); } + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + } + try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -4987,11 +5159,12 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', false, ['plan']) + ->param('transactionId', null, new UID(), 'Transaction ID for staging operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $databaseId, string $collectionId, array $documents, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) { + ->action(function (string $databaseId, string $collectionId, array $documents, ?string $transactionId, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) { $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -5015,26 +5188,52 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') $documents[$key] = new Document($document); } - $upserted = []; + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } - try { - $modified = $dbForProject->createOrUpdateDocuments( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - $documents, - onNext: function (Document $document) use ($plan, &$upserted) { - if (\count($upserted) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { - $upserted[] = $document; - } - }, - ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (DuplicateException) { - throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); - } catch (RelationshipException $e) { - throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); - } catch (StructureException $e) { - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + $operations = []; + foreach ($documents as $doc) { + $operations[] = new Document([ + 'transactionInternalId' => $transaction->getSequence(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'action' => 'upsert', + 'data' => $doc->getArrayCopy(), + ]); + } + + $dbForProject->createDocuments('transactionLogs', $operations); + + $modified = \count($documents); + $upserted = $documents; + } else { + $upserted = []; + + try { + $modified = $dbForProject->createOrUpdateDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documents, + onNext: function (Document $document) use ($plan, &$upserted) { + if (\count($upserted) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { + $upserted[] = $document; + } + }, + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } } foreach ($upserted as $document) { @@ -5081,12 +5280,13 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('documentId', '', new UID(), 'Document ID.') + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.') ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->action(function (string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -5106,15 +5306,33 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DOCUMENT_NOT_FOUND); } - try { - $dbForProject->deleteDocument( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - $documentId - ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (RestrictedException) { - throw new Exception(Exception::DOCUMENT_DELETE_RESTRICTED); + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + + $dbForProject->createDocument('transactionLog', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $document->getId(), + 'action' => 'delete', + ])); + } else { + try { + $dbForProject->deleteDocument( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documentId + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (RestrictedException) { + throw new Exception(Exception::DOCUMENT_DELETE_RESTRICTED); + } } $operations = 0; @@ -5160,21 +5378,23 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); - $relationships = \array_map( - fn ($document) => $document->getAttribute('key'), - \array_filter( - $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ) - ); + if (!empty($transactionId)) { + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); - $queueForEvents - ->setParam('databaseId', $databaseId) - ->setParam('collectionId', $collection->getId()) - ->setParam('documentId', $document->getId()) - ->setContext('collection', $collection) - ->setContext('database', $database) - ->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships); + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setContext('collection', $collection) + ->setContext('database', $database) + ->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships); + } $response->noContent(); }); @@ -5206,12 +5426,13 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $databaseId, string $collectionId, array $queries, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) { + ->action(function (string $databaseId, string $collectionId, array $queries, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) { $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -5231,6 +5452,16 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for collections with relationship attributes'); } + if (!empty($transactionId)) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status') !== 'pending') { + throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); + } + } + try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -5239,20 +5470,32 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') $documents = []; - try { - $modified = $dbForProject->deleteDocuments( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - $queries, - onNext: function (Document $document) use ($plan, &$documents) { - if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { - $documents[] = $document; - } - }, - ); - } catch (ConflictException) { - throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); - } catch (RestrictedException) { - throw new Exception(Exception::DOCUMENT_DELETE_RESTRICTED); + if (!empty($transactionId)) { + $dbForProject->createDocument('transactionLog', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'delete', + 'data' => [$queries], + ])); + + $modified = 0; + } else { + try { + $modified = $dbForProject->deleteDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $queries, + onNext: function (Document $document) use ($plan, &$documents) { + if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { + $documents[] = $document; + } + }, + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (RestrictedException) { + throw new Exception(Exception::DOCUMENT_DELETE_RESTRICTED); + } } foreach ($documents as $document) { From b71688a587e62f71da6f845f967fd139f7266114 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:07:14 -0400 Subject: [PATCH 009/274] Format --- app/controllers/api/databases.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 269018033a..534b2fbe04 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -5199,7 +5199,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') $operations = []; foreach ($documents as $doc) { - $operations[] = new Document([ + $operations[] = new Document([ 'transactionInternalId' => $transaction->getSequence(), 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), From d13081b4f29c49ad8ae418b81605d67275b8181a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:45:07 -0400 Subject: [PATCH 010/274] Use const --- app/controllers/api/databases.php | 2 +- app/init/constants.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 534b2fbe04..9175b53f19 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1556,7 +1556,7 @@ App::post('/v1/databases/transactions') ], contentType: ContentType::JSON )) - ->param('ttl', 300, new Integer(), 'Seconds before the transaction expires.', true) + ->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') ->action(function (int $ttl, Response $response, Database $dbForProject) { diff --git a/app/init/constants.php b/app/init/constants.php index ebf79086a7..96606ff782 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -52,6 +52,9 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes const APP_DATABASE_QUERY_MAX_VALUES = 500; const APP_DATABASE_ENCRYPT_SIZE_MIN = 150; +const APP_DATABASE_TXN_TTL_MIN = 60; // 1 minute +const APP_DATABASE_TXN_TTL_MAX = 3600; // 1 hour +const APP_DATABASE_TXN_TTL_DEFAULT = 300; // 5 minutes const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_SITES = '/storage/sites'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; From 4da95f765b44b5b9592e5152d4fb7e7783c38b72 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:45:14 -0400 Subject: [PATCH 011/274] Don't require data --- src/Appwrite/Utopia/Database/Validator/Operation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 3f9a15673a..e46862bdb2 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -38,7 +38,7 @@ class Operation extends Validator } // Mandatory keys - $required = ['databaseId', 'collectionId', 'action', 'payload']; + $required = ['databaseId', 'collectionId', 'action']; foreach ($required as $key) { if (!\array_key_exists($key, $value)) { $this->description = "Missing required key: {$key}"; From bd9d827a16b0e665ee7faeca7a52c7d7c176f46b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:54:32 -0400 Subject: [PATCH 012/274] Add tests --- .../e2e/Services/Databases/DatabasesBase.php | 593 ++++++++++++++++-- 1 file changed, 555 insertions(+), 38 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index e34691839d..898996733c 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -3891,28 +3891,47 @@ trait DatabasesBase $this->assertCount(1, $documentsUser2['body']['documents']); } - /** - * @depends testDefaultPermissions - */ - public function testUniqueIndexDuplicate(array $data): array + public function testUniqueIndexDuplicate(): void { - $databaseId = $data['databaseId']; - $uniqueIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ + // Setup: create database, collection, attribute, and initial document + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'key' => 'unique_title', - 'type' => 'unique', - 'attributes' => ['title'], + 'databaseId' => ID::unique(), + 'name' => 'Unique Index DB', ]); - - $this->assertEquals(202, $uniqueIndex['headers']['status-code']); - + $databaseId = $database['body']['$id']; + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::create(Role::user(ID::custom($this->getUser()['$id']))), + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ], + 'documentSecurity' => true, + ]); + $moviesId = $movies['body']['$id']; + $title = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(202, $title['headers']['status-code']); sleep(2); - - // test for failure - $duplicate = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + // Insert initial document + $doc1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -3931,11 +3950,45 @@ trait DatabasesBase Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ] ]); + $this->assertEquals(201, $doc1['headers']['status-code']); + // Create unique index + $uniqueIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'unique_title', + 'type' => 'unique', + 'attributes' => ['title'], + ]); + $this->assertEquals(202, $uniqueIndex['headers']['status-code']); + sleep(2); + + // test for failure (duplicate title) + $duplicate = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'Captain America', + 'releaseYear' => 1944, + 'actors' => [ + 'Chris Evans', + 'Samuel Jackson', + ] + ], + 'permissions' => [ + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ] + ]); $this->assertEquals(409, $duplicate['headers']['status-code']); // Test for exception when updating document to conflict - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -3954,11 +4007,9 @@ trait DatabasesBase Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ] ]); - $this->assertEquals(201, $document['headers']['status-code']); - // Test for exception when updating document to conflict - $duplicate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $document['body']['$id'], array_merge([ + $duplicate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $document['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -3977,17 +4028,48 @@ trait DatabasesBase Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ] ]); - $this->assertEquals(409, $duplicate['headers']['status-code']); - - return $data; } - /** - * @depends testUniqueIndexDuplicate - */ - public function testPersistantCreatedAt(array $data): array + public function testPersistentCreatedAt(): void { + // Setup: create database, collection, attribute + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'CreatedAtDB', + ]); + $databaseId = $database['body']['$id']; + $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Movies', + 'permissions' => [ + Permission::create(Role::user(ID::custom($this->getUser()['$id']))), + Permission::read(Role::user(ID::custom($this->getUser()['$id']))), + Permission::update(Role::user(ID::custom($this->getUser()['$id']))), + Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), + ], + 'documentSecurity' => true, + ]); + $moviesId = $movies['body']['$id']; + $title = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(202, $title['headers']['status-code']); + sleep(2); $headers = $this->getSide() === 'client' ? array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3997,52 +4079,43 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]; - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents', $headers, [ + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', $headers, [ 'documentId' => ID::unique(), 'data' => [ 'title' => 'Creation Date Test', 'releaseYear' => 2000 ] ]); - $this->assertEquals($document['body']['title'], 'Creation Date Test'); - $documentId = $document['body']['$id']; $createdAt = $document['body']['$createdAt']; $updatedAt = $document['body']['$updatedAt']; \sleep(1); - - $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, $headers, [ + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $documentId, $headers, [ 'data' => [ 'title' => 'Updated Date Test', ] ]); - $updatedAtSecond = $document['body']['$updatedAt']; - $this->assertEquals($document['body']['title'], 'Updated Date Test'); $this->assertEquals($document['body']['$createdAt'], $createdAt); $this->assertNotEquals($document['body']['$updatedAt'], $updatedAt); \sleep(1); - - $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, $headers, [ + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $documentId, $headers, [ 'data' => [ 'title' => 'Again Updated Date Test', '$createdAt' => '2022-08-01 13:09:23.040', // $createdAt is not updatable '$updatedAt' => '2022-08-01 13:09:23.050' // system will update it not api ] ]); - $this->assertEquals($document['body']['title'], 'Again Updated Date Test'); $this->assertEquals($document['body']['$createdAt'], $createdAt); $this->assertNotEquals($document['body']['$createdAt'], '2022-08-01 13:09:23.040'); $this->assertNotEquals($document['body']['$updatedAt'], $updatedAt); $this->assertNotEquals($document['body']['$updatedAt'], $updatedAtSecond); $this->assertNotEquals($document['body']['$updatedAt'], '2022-08-01 13:09:23.050'); - - return $data; } public function testUpdatePermissionsWithEmptyPayload(): array @@ -5506,5 +5579,449 @@ trait DatabasesBase $this->assertEquals(400, $typeErr['headers']['status-code']); } + public function testCreateTransaction(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestDatabase' + ]); + $databaseId = $database['body']['$id']; + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 900 + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertArrayHasKey('transactionId', $response['body']); + $this->assertEquals('pending', $response['body']['status']); + $this->assertArrayHasKey('expiresAt', $response['body']); + $this->assertGreaterThanOrEqual(DateTime::addSeconds(new \DateTime(), 800), strtotime($response['body']['expiresAt'])); + $this->assertLessThanOrEqual(DateTime::addSeconds(new \DateTime(), 1000), strtotime($response['body']['expiresAt'])); + + // Failure: invalid TTL + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => -1 + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 1000000 + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testAddOperationsToTransaction(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestDatabase' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + $collectionId = $collection['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $transaction = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 300 + ]); + + $transactionId = $transaction['body']['transactionId']; + + // Add operations + $transaction = $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => [ + 'name' => 'Tester 1' + ], + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => [ + 'name' => 'Tester 2' + ], + ] + ] + ]); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $this->assertNotEmpty($transaction['body']); + + // Failure cases + + // Invalid databaseId + $response = $this->client->call(Client::METHOD_POST, "/databases/invalidDatabaseId/transactions/$transactionId/operations", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'operations' => [ + [ + 'databaseId' => 'invalidDatabaseId', + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => ['name' => 'Invalid Tester'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Invalid collectionId + $response = $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => 'invalidCollectionId', + 'action' => 'create', + 'data' => ['name' => 'Invalid Tester'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Invalid action + $response = $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'invalidAction', + 'data' => ['name' => 'Invalid Tester'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Missing required fields + $response = $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId + // Missing action + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + public function testCommitTransaction(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestDatabase' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionTestCommit', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + $collectionId = $collection['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + \sleep(2); + + $transaction = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $transactionId = $transaction['body']['transactionId']; + + $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => ['name' => 'Tester'], + ] + ] + ]); + + $transaction = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/transactions/$transactionId", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $transaction['headers']['status-code']); + $this->assertEquals('committed', $transaction['body']['status']); + + $documents = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/collections/$collectionId/documents", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(1, count($documents['body']['documents'])); + $this->assertEquals('Tester', $documents['body']['documents'][0]['name']); + } + + public function testRollbackTransaction(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestDatabase' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionTestRollback', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + $collectionId = $collection['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + \sleep(2); + + $transaction = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $transactionId = $transaction['body']['transactionId']; + + $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => [ + 'name' => 'value' + ], + ] + ] + ]); + + $rollback = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/transactions/$transactionId", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rollback' => true + ]); + + $this->assertEquals(200, $rollback['headers']['status-code']); + $this->assertEquals('rolledBack', $rollback['body']['status']); + + $documents = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/collections/$collectionId/documents", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()); + + $this->assertEquals(0, count($documents['body']['documents'])); + } + public function testTransactionConflict(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestConflictDatabase' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionTestConflict', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + $collectionId = $collection['body']['$id']; + + // Add string attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], $this->getHeaders()), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $transaction = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/transactions', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $transactionId = $transaction['body']['transactionId']; + + $this->client->call(Client::METHOD_POST, "/databases/$databaseId/transactions/$transactionId/operations", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => ['attribute' => 'value'], + ] + ] + ]); + + // Commit the transaction + $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/transactions/$transactionId", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + // Attempt to commit again, should fail with a conflict + $conflictResponse = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/transactions/$transactionId", \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(409, $conflictResponse['headers']['status-code']); + } } From 145cb54bc034655bdeb912188d3387eab20a9761 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 16:58:25 -0400 Subject: [PATCH 013/274] Separate bulk --- app/config/collections/projects.php | 4 +-- app/controllers/api/databases.php | 30 ++++++++++++------- .../Utopia/Database/Validator/Operation.php | 26 ++++++++++------ src/Appwrite/Utopia/Response.php | 2 ++ 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index 97cde969de..7a1465fb92 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2651,9 +2651,9 @@ return [ 'orders' => [], ], [ - '$id' => ID::custom('_key_db_coll'), + '$id' => ID::custom('_key_internal_path'), 'type' => Database::INDEX_KEY, - 'attributes' => ['databaseId', 'collectionId'], + 'attributes' => ['databaseInternalId', 'collectionInternalId'], 'lengths' => [], 'orders' => [], ], diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 9175b53f19..0159b199c7 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -4442,10 +4442,10 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLog', new Document([ - 'transactionInternalId' => $transaction->getSequence(), + $dbForProject->createDocument('transactionLogs', new Document([ 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), 'documentId' => $document->getId(), 'data' => $newDocument->getArrayCopy(), 'action' => 'update', @@ -4700,10 +4700,10 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLog', new Document([ - 'transactionInternalId' => $transaction->getSequence(), + $dbForProject->createDocument('transactionLogs', new Document([ 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), 'action' => 'update', 'data' => $newDocument->getArrayCopy(), ])); @@ -4841,7 +4841,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); } - $dbForProject->createDocument('transactionLog', new Document([ + $dbForProject->createDocument('transactionLogs', new Document([ 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), @@ -4958,7 +4958,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); } - $dbForProject->createDocument('transactionLog', new Document([ + $dbForProject->createDocument('transactionLogs', new Document([ 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), @@ -5098,6 +5098,16 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') $documents = []; + if (!empty($transactionId)) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'bulkUpdate', + 'data' => \compact('data', 'queries'), + ])); + } + try { $modified = $dbForProject->updateDocuments( 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), @@ -5315,7 +5325,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); } - $dbForProject->createDocument('transactionLog', new Document([ + $dbForProject->createDocument('transactionLogs', new Document([ 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), @@ -5471,12 +5481,12 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') $documents = []; if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLog', new Document([ + $dbForProject->createDocument('transactionLogs', new Document([ 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'action' => 'delete', - 'data' => [$queries], + 'action' => 'bulkDelete', + 'data' => ['queries' => $queries], ])); $modified = 0; diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index e46862bdb2..45122df800 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -8,12 +8,21 @@ class Operation extends Validator { private string $description = ''; - /** @var string[] */ + /** @var array */ + private array $required = [ + 'databaseId', + 'collectionId', + 'action', + ]; + + /** @var array */ private array $actions = [ 'create', 'update', + 'bulkUpdate', 'upsert', - 'delete' + 'delete', + 'bulkDelete', ]; public function getDescription(): string @@ -38,16 +47,15 @@ class Operation extends Validator } // Mandatory keys - $required = ['databaseId', 'collectionId', 'action']; - foreach ($required as $key) { + foreach ($this->required as $key) { if (!\array_key_exists($key, $value)) { $this->description = "Missing required key: {$key}"; return false; } } - // databaseId / collectionId / action must be non‑empty strings - foreach (['databaseId', 'collectionId', 'action'] as $key) { + // Required keys must be non‑empty + foreach ($this->required as $key) { if (!\is_string($value[$key]) || \trim($value[$key]) === '') { $this->description = "Key '{$key}' must be a non‑empty string"; return false; @@ -60,9 +68,9 @@ class Operation extends Validator return false; } - // Payload must be array (can be empty) - if (!\is_array($value['payload'])) { - $this->description = "Key 'payload' must be an array"; + // Data must be array (can be empty) + if (!\is_array($value['data'])) { + $this->description = "Key 'data' must be an array"; return false; } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index a376dd9cb6..994f0e2e73 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -105,6 +105,7 @@ use Appwrite\Utopia\Response\Model\TemplateSMS; use Appwrite\Utopia\Response\Model\TemplateVariable; use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Topic; +use Appwrite\Utopia\Response\Model\Transaction; use Appwrite\Utopia\Response\Model\UsageBuckets; use Appwrite\Utopia\Response\Model\UsageCollection; use Appwrite\Utopia\Response\Model\UsageDatabase; @@ -515,6 +516,7 @@ class Response extends SwooleResponse ->setModel(new TemplateVariable()) ->setModel(new Token()) ->setModel(new Topic()) + ->setModel(new Transaction()) ->setModel(new UsageBuckets()) ->setModel(new UsageCollection()) ->setModel(new UsageDatabase()) From 0b00ce28e0c241cba4642abf00905551c4c34010 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 19:37:53 -0400 Subject: [PATCH 014/274] Remove redundant attributes --- app/config/collections/projects.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index 7a1465fb92..e76a17abe5 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2537,26 +2537,6 @@ return [ 'array' => false, 'filters' => ['datetime'], ], - [ - '$id' => ID::custom('committedAt'), - 'type' => Database::VAR_DATETIME, - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('rolledBackAt'), - 'type' => Database::VAR_DATETIME, - 'size' => 0, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], ], 'indexes' => [ [ From 1e82c7f0ef37d4e9b6a37ee1126757a26b9a6036 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 21:54:30 -0400 Subject: [PATCH 015/274] Add listTransactions --- app/controllers/api/databases.php | 35 +++++++++++++++++++ .../Utopia/Database/Validator/Operation.php | 2 +- .../Validator/Queries/Transactions.php | 16 +++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 0159b199c7..9819377f9d 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1621,6 +1621,41 @@ App::post('/v1/databases/transactions/:transactionId/operations') ->dynamic($transaction, Response::MODEL_TRANSACTION); }); +App::get('/v1/databases/transactions') + ->desc('List transactions') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'listTransactions', + description: '/docs/references/databases/list-transactions.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::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') + ->action(function (array $queries, Response $response, Database $dbForProject) { + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + $response->dynamic(new Document([ + 'transactions' => $dbForProject->find('transactions', $queries), + 'total' => $dbForProject->count('transactions', $queries), + ]), Response::MODEL_TRANSACTION_LIST); + }); + App::get('/v1/databases/transactions/:transactionId') ->desc('Get transaction') ->groups(['api', 'database', 'transactions']) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 45122df800..989fd76eec 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -19,9 +19,9 @@ class Operation extends Validator private array $actions = [ 'create', 'update', - 'bulkUpdate', 'upsert', 'delete', + 'bulkUpdate', 'bulkDelete', ]; diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php new file mode 100644 index 0000000000..ab3e933d6f --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php @@ -0,0 +1,16 @@ + Date: Tue, 17 Jun 2025 21:54:40 -0400 Subject: [PATCH 016/274] Update params --- app/controllers/api/databases.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 9819377f9d..69537f434f 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1691,7 +1691,7 @@ App::get('/v1/databases/transactions/:transactionId') }); App::patch('/v1/databases/transactions/:transactionId') - ->desc('Update transaction (commit / rollback)') + ->desc('Update transaction') ->groups(['api', 'database', 'transactions']) ->label('scope', 'collections.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) @@ -1710,11 +1710,20 @@ App::patch('/v1/databases/transactions/:transactionId') contentType: ContentType::JSON )) ->param('transactionId', '', new UID(), 'Transaction ID.') - ->param('action', '', new WhiteList(['commit','rollback']), 'Action to take, commit or rollback.') + ->param('commit', false, new Boolean(), 'Commit transaction?', true) + ->param('rollback', false, new Boolean(), 'Rollback transaction?', true) + ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') ->inject('project') - ->action(function (string $transactionId, string $action, ?string $reason, Response $response, Database $dbForProject, Document $project) { + ->action(function (string $transactionId, bool $commit, bool $rollback, ?\DateTime $requestTimestamp, ?string $reason, Response $response, Database $dbForProject, Document $project) { + if (!$commit && !$rollback) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); + } + if ($commit && $rollback) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); + } + $transaction = $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { From 5d5a6fbc5d89d38e2e7a9508bed186dc0adfd9f5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 21:55:08 -0400 Subject: [PATCH 017/274] Add delete transaction --- app/controllers/api/databases.php | 205 +++++++++++++++++++++--------- 1 file changed, 144 insertions(+), 61 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 69537f434f..9f6cfa1aec 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1734,79 +1734,122 @@ App::patch('/v1/databases/transactions/:transactionId') throw new Exception(Exception::TRANSACTION_NOT_READY); } - switch ($action) { - case 'commit': - // Get staged operations - $operations = $dbForProject->find('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - Query::orderAsc('$sequence'), - ]); + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } - $creates = $updates = $deletes = []; + if ($commit) { + $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committing', + ])); - foreach ($operations as $operation) { - $databaseId = $operation['databaseInternalId']; - $collectionId = $operation['collectionInternalId']; - $documentId = $operation['documentInternalId']; + $operations = $dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); - switch ($operation['action']) { - case 'create': - $creates[$databaseId][$collectionId][] = new Document([ - '$id' => $documentId ?? ID::unique(), - ...$operation['data'] - ]); - break; - case 'update': - case 'upsert': - $updates[$databaseId][$collectionId][] = new Document([ - '$id' => $documentId, - ...$operation['data'], - ]); - break; - case 'delete': - $deletes[$databaseId][$collectionId][] = $documentId; - break; - } - } + $creates = $updates = $deletes = $bulkUpdates = $bulkDeletes = []; - unset($databaseId, $collectionId); + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $documentId = $operation['documentId']; - $dbForProject->withTransaction(function () use ($dbForProject, $creates, $updates, $deletes) { - foreach ($creates as $databaseId => $collectionDocs) { - foreach ($collectionDocs as $collectionId => $docs) { - $dbForProject->createDocuments("database_{$databaseId}_collection_{$collectionId}", $docs); + switch ($operation['action']) { + case 'create': + $creates[$databaseInternalId][$collectionInternalId][] = new Document([ + '$id' => $documentId ?? ID::unique(), + ...$operation['data'] + ]); + break; + case 'update': + case 'upsert': + $updates[$databaseInternalId][$collectionInternalId][] = new Document([ + '$id' => $documentId, + ...$operation['data'], + ]); + break; + case 'delete': + $deletes[$databaseInternalId][$collectionInternalId][] = $documentId; + break; + case 'bulkUpdate': + $bulkUpdates[$databaseInternalId][$collectionInternalId][] = [ + 'data' => $operation['data'] ?? null, + 'queries' => $operation['queries'] ?? [], + ]; + break; + case 'bulkDelete': + $bulkDeletes[$databaseInternalId][$collectionInternalId][] = [ + 'queries' => $operation['queries'] ?? [], + ]; + break; } } - foreach ($updates as $databaseId => $collectionDocs) { - foreach ($collectionDocs as $collectionId => $docs) { - $dbForProject->updateDocuments("database_{$databaseId}_collection_{$collectionId}", $docs); + + try { + foreach ($creates as $dbId => $cols) { + foreach ($cols as $colId => $docs) { + $dbForProject->createDocuments("database_{$dbId}_collection_{$colId}", $docs); + } } - } - foreach ($deletes as $databaseId => $collectionDocs) { - foreach ($collectionDocs as $collectionId => $docs) { - $dbForProject->deleteDocuments("database_{$databaseId}_collection_{$collectionId}", $docs); + foreach ($updates as $dbId => $cols) { + foreach ($cols as $colId => $docs) { + $dbForProject->createOrUpdateDocuments("database_{$dbId}_collection_{$colId}", $docs); + } } + foreach ($deletes as $dbId => $cols) { + foreach ($cols as $colId => $ids) { + $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", [ + Query::equal('$id', $ids), + ]); + } + } + foreach ($bulkUpdates as $dbId => $cols) { + foreach ($cols as $colId => $updates) { + foreach ($updates as $update) { + $dbForProject->updateDocuments("database_{$dbId}_collection_{$colId}", $update['data'], $update['queries']); + } + } + } + foreach ($bulkDeletes as $dbId => $cols) { + foreach ($cols as $colId => $deletes) { + foreach ($deletes as $delete) { + $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", $delete['queries']); + } + } + } + + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committed', + ])); + + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + } catch (DuplicateException|ConflictException) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + + throw new Exception(Exception::TRANSACTION_CONFLICT); } }); + }); - $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'committed', - ])); + $transaction = $dbForProject->getDocument('transactions', $transactionId); + } - break; + if ($rollback) { + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); - case 'rollback': - $dbForProject->deleteDocuments('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - Query::orderAsc('$sequence'), - ]); - - $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'rolled_back', - 'rolledBackAt' => DateTime::now(), - 'reason' => $reason ?? 'user_request', - ])); - break; + $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'rolledBack', + ])); } $response @@ -1814,6 +1857,46 @@ App::patch('/v1/databases/transactions/:transactionId') ->dynamic($transaction, Response::MODEL_TRANSACTION); }); +App::delete('/v1/databases/transactions/:transactionId') + ->desc('Delete transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'deleteTransaction', + description: '/docs/references/databases/delete-transaction.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_NOCONTENT, + model: Response::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + )) + ->param('transactionId', '', new UID(), 'Transaction ID.') + ->inject('response') + ->inject('dbForProject') + ->action(function (string $transactionId, Response $response, Database $dbForProject) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + + if (!$dbForProject->deleteDocument('transactions', $transactionId)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove transaction from DB'); + } + + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + + $response->noContent(); + }); + App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->alias('/v1/database/collections/:collectionId/attributes/url') ->desc('Create URL attribute') @@ -3790,8 +3873,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'action' => 'create', 'documentId' => $document->getId(), + 'action' => 'create', 'data' => $document->getArrayCopy(), ]); } @@ -4491,8 +4574,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), 'documentId' => $document->getId(), - 'data' => $newDocument->getArrayCopy(), 'action' => 'update', + 'data' => $newDocument->getArrayCopy(), ])); } else { try { From 75469eed7d6636d93b141e630575ad8336f2106a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 21:55:38 -0400 Subject: [PATCH 018/274] Fix staged values --- app/controllers/api/databases.php | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 9f6cfa1aec..cc51fd0def 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -18,6 +18,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Attributes; use Appwrite\Utopia\Database\Validator\Queries\Collections; use Appwrite\Utopia\Database\Validator\Queries\Databases; use Appwrite\Utopia\Database\Validator\Queries\Indexes; +use Appwrite\Utopia\Database\Validator\Queries\Transactions; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; @@ -1594,23 +1595,35 @@ App::post('/v1/databases/transactions/:transactionId/operations') ->param('operations', [], new ArrayList(new Operation()), 'Array of staged operations.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $transactionId, array $operations, Response $response, Database $dbForProject) { + ->inject('plan') + ->action(function (string $transactionId, array $operations, Response $response, Database $dbForProject, array $plan) { $transaction = $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty() || $transaction['status'] !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } - $staged = []; - foreach ($operations as $op) { + + $databases = $collections = $staged = []; + foreach ($operations as $operation) { + $database = $databases[$operation['databaseId']] ??= $dbForProject->getDocument('databases', $operation['databaseId']); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $collections[$operation['collectionId']] ??= $dbForProject->getDocument('database_' . $database->getSequence(), $operation['collectionId']); + if ($collection->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + $staged[] = new Document([ '$id' => ID::unique(), - 'transactionId' => $transactionId, - 'databaseId' => $op['databaseId'] ?? null, - 'collectionId' => $op['collectionId'] ?? null, - 'documentId' => $op['documentId'] ?? null, - 'action' => $op['action'], - 'data' => $op['data'] ?? [], + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $operation['documentId'] ?? ID::unique(), + 'action' => $operation['action'], + 'data' => $operation['data'] ?? new \stdClass(), ]); } From 910f27016bfb4c3fddd36b06e5f48e7dd6b0ff6c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 17 Jun 2025 22:08:08 -0400 Subject: [PATCH 019/274] Add operation to txn meta --- app/config/collections/projects.php | 10 ++++++++++ app/controllers/api/databases.php | 20 +++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index e76a17abe5..53a665bfab 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2527,6 +2527,16 @@ return [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('operations'), + 'type' => Database::VAR_INTEGER, + 'size' => 0, + 'signed' => false, + 'required' => true, + 'default' => 0, + 'array' => false, + 'filters' => [], + ], [ '$id' => ID::custom('expiresAt'), 'type' => Database::VAR_DATETIME, diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index cc51fd0def..3e0ec7a88b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1564,6 +1564,7 @@ App::post('/v1/databases/transactions') $transaction = $dbForProject->createDocument('transactions', new Document([ '$id' => ID::unique(), 'status' => 'pending', + 'operations' => 0, 'expiresAt' => DateTime::addSeconds(new \DateTime(), $ttl), ])); @@ -1598,11 +1599,19 @@ App::post('/v1/databases/transactions/:transactionId/operations') ->inject('plan') ->action(function (string $transactionId, array $operations, Response $response, Database $dbForProject, array $plan) { $transaction = $dbForProject->getDocument('transactions', $transactionId); - - if ($transaction->isEmpty() || $transaction['status'] !== 'pending') { + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + + if (($existing + \count($operations)) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding ' . \count($operations) . ' would exceed the maximum of ' . $maxBatch + ); + } $databases = $collections = $staged = []; foreach ($operations as $operation) { @@ -1627,7 +1636,12 @@ App::post('/v1/databases/transactions/:transactionId/operations') ]); } - $dbForProject->createDocuments('transactionLogs', $staged); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { + $dbForProject->createDocuments('transactionLogs', $staged); + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'operations' => $existing + \count($operations), + ])); + }); $response ->setStatusCode(Response::STATUS_CODE_CREATED) From b471bd0f581ed06ab886a664d133c8017d141973 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 18 Jun 2025 16:53:47 -0400 Subject: [PATCH 020/274] Add operations to response model --- app/controllers/api/databases.php | 182 +++++++++--------- .../Utopia/Response/Model/Transaction.php | 6 + 2 files changed, 95 insertions(+), 93 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 3e0ec7a88b..5b9d571211 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1752,118 +1752,114 @@ App::patch('/v1/databases/transactions/:transactionId') } $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } - if ($transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::TRANSACTION_NOT_READY); } $now = new \DateTime(); - $expiresAt = new \DateTime($transaction->getAttribute('expiresAt')); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); if ($now > $expiresAt) { throw new Exception(Exception::TRANSACTION_EXPIRED); } if ($commit) { - $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { - $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committing', + ])); + + $operations = $dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + + $creates = $updates = $deletes = $bulkUpdates = $bulkDeletes = []; + + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $documentId = $operation['documentId']; + + switch ($operation['action']) { + case 'create': + $creates[$databaseInternalId][$collectionInternalId][] = new Document([ + '$id' => $documentId ?? ID::unique(), + ...$operation['data'] + ]); + break; + case 'update': + case 'upsert': + $updates[$databaseInternalId][$collectionInternalId][] = new Document([ + '$id' => $documentId, + ...$operation['data'], + ]); + break; + case 'delete': + $deletes[$databaseInternalId][$collectionInternalId][] = $documentId; + break; + case 'bulkUpdate': + $bulkUpdates[$databaseInternalId][$collectionInternalId][] = [ + 'data' => $operation['data'] ?? null, + 'queries' => $operation['queries'] ?? [], + ]; + break; + case 'bulkDelete': + $bulkDeletes[$databaseInternalId][$collectionInternalId][] = [ + 'queries' => $operation['queries'] ?? [], + ]; + break; + } + } + + try { + foreach ($creates as $dbId => $cols) { + foreach ($cols as $colId => $docs) { + $dbForProject->createDocuments("database_{$dbId}_collection_{$colId}", $docs); + } + } + foreach ($updates as $dbId => $cols) { + foreach ($cols as $colId => $docs) { + $dbForProject->createOrUpdateDocuments("database_{$dbId}_collection_{$colId}", $docs); + } + } + foreach ($deletes as $dbId => $cols) { + foreach ($cols as $colId => $ids) { + $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", [ + Query::equal('$id', $ids), + ]); + } + } + foreach ($bulkUpdates as $dbId => $cols) { + foreach ($cols as $colId => $updates) { + foreach ($updates as $update) { + $dbForProject->updateDocuments("database_{$dbId}_collection_{$colId}", $update['data'], $update['queries']); + } + } + } + foreach ($bulkDeletes as $dbId => $cols) { + foreach ($cols as $colId => $deletes) { + foreach ($deletes as $delete) { + $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", $delete['queries']); + } + } + } + $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'committing', + 'status' => 'committed', ])); - $operations = $dbForProject->find('transactionLogs', [ + $dbForProject->deleteDocuments('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), ]); + } catch (DuplicateException|ConflictException) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); - $creates = $updates = $deletes = $bulkUpdates = $bulkDeletes = []; - - foreach ($operations as $operation) { - $databaseInternalId = $operation['databaseInternalId']; - $collectionInternalId = $operation['collectionInternalId']; - $documentId = $operation['documentId']; - - switch ($operation['action']) { - case 'create': - $creates[$databaseInternalId][$collectionInternalId][] = new Document([ - '$id' => $documentId ?? ID::unique(), - ...$operation['data'] - ]); - break; - case 'update': - case 'upsert': - $updates[$databaseInternalId][$collectionInternalId][] = new Document([ - '$id' => $documentId, - ...$operation['data'], - ]); - break; - case 'delete': - $deletes[$databaseInternalId][$collectionInternalId][] = $documentId; - break; - case 'bulkUpdate': - $bulkUpdates[$databaseInternalId][$collectionInternalId][] = [ - 'data' => $operation['data'] ?? null, - 'queries' => $operation['queries'] ?? [], - ]; - break; - case 'bulkDelete': - $bulkDeletes[$databaseInternalId][$collectionInternalId][] = [ - 'queries' => $operation['queries'] ?? [], - ]; - break; - } - } - - try { - foreach ($creates as $dbId => $cols) { - foreach ($cols as $colId => $docs) { - $dbForProject->createDocuments("database_{$dbId}_collection_{$colId}", $docs); - } - } - foreach ($updates as $dbId => $cols) { - foreach ($cols as $colId => $docs) { - $dbForProject->createOrUpdateDocuments("database_{$dbId}_collection_{$colId}", $docs); - } - } - foreach ($deletes as $dbId => $cols) { - foreach ($cols as $colId => $ids) { - $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", [ - Query::equal('$id', $ids), - ]); - } - } - foreach ($bulkUpdates as $dbId => $cols) { - foreach ($cols as $colId => $updates) { - foreach ($updates as $update) { - $dbForProject->updateDocuments("database_{$dbId}_collection_{$colId}", $update['data'], $update['queries']); - } - } - } - foreach ($bulkDeletes as $dbId => $cols) { - foreach ($cols as $colId => $deletes) { - foreach ($deletes as $delete) { - $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", $delete['queries']); - } - } - } - - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'committed', - ])); - - $dbForProject->deleteDocuments('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - ]); - } catch (DuplicateException|ConflictException) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - - throw new Exception(Exception::TRANSACTION_CONFLICT); - } - }); + throw new Exception(Exception::TRANSACTION_CONFLICT); + } }); $transaction = $dbForProject->getDocument('transactions', $transactionId); diff --git a/src/Appwrite/Utopia/Response/Model/Transaction.php b/src/Appwrite/Utopia/Response/Model/Transaction.php index c6eafd18b9..aae2a9b572 100644 --- a/src/Appwrite/Utopia/Response/Model/Transaction.php +++ b/src/Appwrite/Utopia/Response/Model/Transaction.php @@ -34,6 +34,12 @@ class Transaction extends Model 'default' => 'pending', 'example' => 'pending', ]) + ->addRule('operations', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Number of operations in the transaction.', + 'default' => 0, + 'example' => 5, + ]) ->addRule('expiresAt', [ 'type' => self::TYPE_DATETIME, 'description' => 'Expiration time in ISO 8601 format.', From ac329479085a94963e93ed45774ed5e42f0a6d79 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 18 Jun 2025 19:17:47 -0400 Subject: [PATCH 021/274] Update db --- composer.json | 2 +- composer.lock | 39 ++++++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 18507e04f1..2529dc404d 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.71.*", + "utopia-php/database": "dev-feat-transaction-pinning as 0.71.6", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", "utopia-php/dsn": "0.2.1", diff --git a/composer.lock b/composer.lock index ff6884de7d..aee5501bc8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1557e469b3074a6478a0b2fd522e1a2a", + "content-hash": "aabb75770fc1377ff5188957653cbdea", "packages": [ { "name": "adhocore/jwt", @@ -3490,16 +3490,16 @@ }, { "name": "utopia-php/database", - "version": "0.71.6", + "version": "dev-feat-transaction-pinning", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "2bd87acc40af087fc0fdcccc47c43141dff0be5c" + "reference": "8c764ed339caa60687b2362a96712bb6cbabf1eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/2bd87acc40af087fc0fdcccc47c43141dff0be5c", - "reference": "2bd87acc40af087fc0fdcccc47c43141dff0be5c", + "url": "https://api.github.com/repos/utopia-php/database/zipball/8c764ed339caa60687b2362a96712bb6cbabf1eb", + "reference": "8c764ed339caa60687b2362a96712bb6cbabf1eb", "shasum": "" }, "require": { @@ -3540,9 +3540,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.71.6" + "source": "https://github.com/utopia-php/database/tree/feat-transaction-pinning" }, - "time": "2025-06-16T16:48:37+00:00" + "time": "2025-06-18T20:49:54+00:00" }, { "name": "utopia-php/detector", @@ -4807,16 +4807,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.41.7", + "version": "0.41.8", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d8c7bb26ea32ab378faf4e0dfa62fd15fe37c57b" + "reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d8c7bb26ea32ab378faf4e0dfa62fd15fe37c57b", - "reference": "d8c7bb26ea32ab378faf4e0dfa62fd15fe37c57b", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/93ffb24b25b376ca4423e3a5caf6f916673af4b2", + "reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2", "shasum": "" }, "require": { @@ -4852,9 +4852,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.41.7" + "source": "https://github.com/appwrite/sdk-generator/tree/0.41.8" }, - "time": "2025-06-13T17:05:57+00:00" + "time": "2025-06-18T13:20:45+00:00" }, { "name": "doctrine/annotations", @@ -8231,9 +8231,18 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/database", + "version": "dev-feat-transaction-pinning", + "alias": "0.71.6", + "alias_normalized": "0.71.6.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/database": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 2745d8dce7edf4e614aee82d5250557ee44a5f61 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 18 Jun 2025 19:51:37 -0400 Subject: [PATCH 022/274] Add single op counting --- app/controllers/api/databases.php | 268 ++++++++++++++++++++++-------- 1 file changed, 195 insertions(+), 73 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 5b9d571211..26fe7a83b2 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1638,9 +1638,12 @@ App::post('/v1/databases/transactions/:transactionId/operations') $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { $dbForProject->createDocuments('transactionLogs', $staged); - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'operations' => $existing + \count($operations), - ])); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + \count($operations) + ); }); $response @@ -1775,7 +1778,14 @@ App::patch('/v1/databases/transactions/:transactionId') Query::equal('transactionInternalId', [$transaction->getSequence()]), ]); - $creates = $updates = $deletes = $bulkUpdates = $bulkDeletes = []; + $creates + = $updates + = $deletes + = $increments + = $decrements + = $bulkUpdates + = $bulkDeletes + = []; foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; @@ -1799,15 +1809,29 @@ App::patch('/v1/databases/transactions/:transactionId') case 'delete': $deletes[$databaseInternalId][$collectionInternalId][] = $documentId; break; + case 'increment': + $increments[$databaseInternalId][$collectionInternalId][] = [ + 'attribute' => $operation['data']['attribute'], + 'value' => $operation['data']['value'] ?? 1, + 'max' => $operation['data']['max'] ?? null, + ]; + break; + case 'decrement': + $decrements[$databaseInternalId][$collectionInternalId][] = [ + 'attribute' => $operation['data']['attribute'], + 'value' => $operation['data']['value'] ?? 1, + 'min' => $operation['data']['min'] ?? null, + ]; + break; case 'bulkUpdate': $bulkUpdates[$databaseInternalId][$collectionInternalId][] = [ - 'data' => $operation['data'] ?? null, - 'queries' => $operation['queries'] ?? [], + 'data' => $operation['data']['data'] ?? null, + 'queries' => $operation['data']['queries'] ?? [], ]; break; case 'bulkDelete': $bulkDeletes[$databaseInternalId][$collectionInternalId][] = [ - 'queries' => $operation['queries'] ?? [], + 'queries' => $operation['data']['queries'] ?? [], ]; break; } @@ -1831,6 +1855,30 @@ App::patch('/v1/databases/transactions/:transactionId') ]); } } + foreach ($increments as $dbId => $cols) { + foreach ($cols as $colId => $increments) { + foreach ($increments as $increment) { + $dbForProject->increaseDocumentAttribute( + "database_{$dbId}_collection_{$colId}", + $increment['attribute'], + $increment['value'], + $increment['max'] + ); + } + } + } + foreach ($decrements as $dbId => $cols) { + foreach ($cols as $colId => $decrements) { + foreach ($decrements as $decrement) { + $dbForProject->decreaseDocumentAttribute( + "database_{$dbId}_collection_{$colId}", + $decrement['attribute'], + $decrement['value'], + $decrement['min'] + ); + } + } + } foreach ($bulkUpdates as $dbId => $cols) { foreach ($cols as $colId => $updates) { foreach ($updates as $update) { @@ -3654,7 +3702,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('user') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, ?string $documentId, string|array|null $data, ?array $permissions, ?array $documents, ?string $transactionId, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->inject('plan') + ->action(function (string $databaseId, string $collectionId, ?string $documentId, string|array|null $data, ?array $permissions, ?array $documents, ?string $transactionId, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan) { $data = \is_string($data) ? \json_decode($data, true) : $data; @@ -3903,7 +3952,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } try { - $dbForProject->createDocuments('transactionLogs', $operations); + $dbForProject->withTransaction(function () use ($dbForProject, $plan, $transactionId, $operations) { + $dbForProject->createDocuments('transactionLogs', $operations); + $dbForProject->increaseDocumentAttribute( + collection: 'transactions', + id: $transactionId, + attribute:'operations', + value: \count($operations), + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); } catch (DuplicateException) { throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } catch (NotFoundException) { @@ -4442,7 +4500,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + ->inject('plan') + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && \is_null($permissions)) { @@ -4592,14 +4651,22 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $document->getId(), - 'action' => 'update', - 'data' => $newDocument->getArrayCopy(), - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $plan, $transactionId, $database, $collection, $transaction, $document, $newDocument) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $document->getId(), + 'action' => 'update', + 'data' => $newDocument->getArrayCopy(), + ])); + $dbForProject->increaseDocumentAttribute( + collection:'transactions', + id: $transactionId, + attribute: 'operations', + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); } else { try { $document = $dbForProject->updateDocument( @@ -4850,13 +4917,21 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'action' => 'update', - 'data' => $newDocument->getArrayCopy(), - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $newDocument) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'update', + 'data' => $newDocument->getArrayCopy(), + ])); + $dbForProject->increaseDocumentAttribute( + collection: 'transactions', + id: $transactionId, + attribute: 'operations', + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); $upserted = $newDocument; } else { @@ -4991,18 +5066,25 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); } - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $documentId, - 'action' => 'increment', - 'data' => [ - 'attribute' => $attribute, - 'value' => $value, - 'max' => $max, - ] - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $documentId, $attribute, $value, $max) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'increment', + 'data' => [ + 'attribute' => $attribute, + 'value' => $value, + 'max' => $max, + ] + ])); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + ); + }); $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId); } else { @@ -5108,18 +5190,25 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); } - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $documentId, - 'action' => 'decrement', - 'data' => [ - 'attribute' => $attribute, - 'value' => $value, - 'min' => $min, - ], - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $documentId, $attribute, $value, $min) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'decrement', + 'data' => [ + 'attribute' => $attribute, + 'value' => $value, + 'min' => $min, + ], + ])); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + ); + }); // Fetch current document for response without mutating $document = $dbForProject->getDocument( @@ -5249,13 +5338,21 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') $documents = []; if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'action' => 'bulkUpdate', - 'data' => \compact('data', 'queries'), - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $data, $queries) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'bulkUpdate', + 'data' => \compact('data', 'queries'), + ])); + $dbForProject->increaseDocumentAttribute( + collection: 'transactions', + id: $transactionId, + attribute: 'operations', + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); } try { @@ -5368,10 +5465,19 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') ]); } - $dbForProject->createDocuments('transactionLogs', $operations); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $operations) { + $dbForProject->createDocuments('transactionLogs', $operations); + $dbForProject->increaseDocumentAttribute( + collection: 'transactions', + id: $transactionId, + attribute: 'operations', + value: \count($operations), + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); - $modified = \count($documents); - $upserted = $documents; + $modified = \count($documents); + $upserted = $documents; } else { $upserted = []; @@ -5475,13 +5581,21 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::TRANSACTION_INVALID, 'Transaction is not pending'); } - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $document->getId(), - 'action' => 'delete', - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $document) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $document->getId(), + 'action' => 'delete', + ])); + $dbForProject->increaseDocumentAttribute( + collection: 'transactions', + id: $transactionId, + attribute: 'operations', + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); } else { try { $dbForProject->deleteDocument( @@ -5631,13 +5745,21 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') $documents = []; if (!empty($transactionId)) { - $dbForProject->createDocument('transactionLogs', new Document([ - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'action' => 'bulkDelete', - 'data' => ['queries' => $queries], - ])); + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $database, $collection, $transaction, $queries) { + $dbForProject->createDocument('transactionLogs', new Document([ + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'bulkDelete', + 'data' => ['queries' => $queries], + ])); + $dbForProject->increaseDocumentAttribute( + collection: 'transactions', + id: $transactionId, + attribute: 'operations', + max: $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH, + ); + }); $modified = 0; } else { From 07cce5ad3c3198d7a41d941b5fadf2c46d8c8540 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 29 Jul 2025 21:30:02 +1200 Subject: [PATCH 023/274] Optional param --- app/controllers/api/databases.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 26fe7a83b2..9fa157c49d 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -5546,7 +5546,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('documentId', '', new UID(), 'Document ID.') - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.') + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') From 480f8c97ca414113f7d2fa3d766edf4f794ee286 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 11 Aug 2025 08:25:58 +0000 Subject: [PATCH 024/274] Add transaction support for databases with staging and commit/rollback Co-authored-by: jakeb994 --- .../Documents/Attribute/Decrement.php | 3 +- .../Documents/Attribute/Increment.php | 3 +- .../Collections/Documents/Bulk/Delete.php | 3 +- .../Collections/Documents/Bulk/Update.php | 3 +- .../Collections/Documents/Bulk/Upsert.php | 3 +- .../Collections/Documents/Create.php | 3 +- .../Collections/Documents/Delete.php | 3 +- .../Collections/Documents/Update.php | 3 +- .../Collections/Documents/Upsert.php | 3 +- .../Http/Grids/Tables/Rows/Bulk/Delete.php | 1 + .../Http/Grids/Tables/Rows/Bulk/Update.php | 1 + .../Http/Grids/Tables/Rows/Bulk/Upsert.php | 1 + .../Grids/Tables/Rows/Column/Decrement.php | 1 + .../Grids/Tables/Rows/Column/Increment.php | 1 + .../Http/Grids/Tables/Rows/Create.php | 1 + .../Http/Grids/Tables/Rows/Delete.php | 1 + .../Http/Grids/Tables/Rows/Update.php | 1 + .../Http/Grids/Tables/Rows/Upsert.php | 1 + .../Http/Transactions/AddOperations.php | 117 ++++++++ .../Databases/Http/Transactions/Create.php | 73 +++++ .../Databases/Http/Transactions/Delete.php | 76 ++++++ .../Databases/Http/Transactions/Get.php | 69 +++++ .../Databases/Http/Transactions/Update.php | 249 ++++++++++++++++++ .../Databases/Http/Transactions/XList.php | 73 +++++ 24 files changed, 684 insertions(+), 9 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 49a408e64c..fa6bc93845 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -74,6 +74,7 @@ class Decrement extends Action ->param('attribute', '', new Key(), 'Attribute key.') ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) ->param('min', null, new Numeric(), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -81,7 +82,7 @@ class Decrement extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 5eadc96b9e..5bfbc96c7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -74,6 +74,7 @@ class Increment extends Action ->param('attribute', '', new Key(), 'Attribute key.') ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) ->param('max', null, new Numeric(), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -81,7 +82,7 @@ class Increment extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index f44e54f2b4..42206983bd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -70,6 +70,7 @@ class Delete extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -81,7 +82,7 @@ class Delete extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 82b39ef178..0a199c6eb7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -74,6 +74,7 @@ class Update extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -85,7 +86,7 @@ class Update extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string|array $data, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $collectionId, string|array $data, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $data = \is_string($data) ? \json_decode($data, true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 23e6453138..01844b52f4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -71,6 +71,7 @@ class Upsert extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', false, ['plan']) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -78,7 +79,7 @@ class Upsert extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void + public function action(string $databaseId, string $collectionId, array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void { $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 0691249943..5d4629e7a5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -117,6 +117,7 @@ class Create extends Action ->param('data', [], new JSON(), 'Document data as JSON object.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('user') @@ -127,7 +128,7 @@ class Create extends Action ->inject('queueForWebhooks') ->callback($this->action(...)); } - public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void { $data = \is_string($data) ? \json_decode($data, true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 7bc81ab7db..75377fe42b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -71,6 +71,7 @@ class Delete extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('documentId', '', new UID(), 'Document ID.') + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') @@ -79,7 +80,7 @@ class Delete extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 1d06e6d0da..cda4563a4c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -77,6 +77,7 @@ class Update extends Action ->param('documentId', '', new UID(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') @@ -85,7 +86,7 @@ class Update extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index e862896a40..bdd5c1de0a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -80,6 +80,7 @@ class Upsert extends Action ->param('documentId', '', new CustomId(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include all required attributes of the document to be created or updated.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('user') @@ -89,7 +90,7 @@ class Upsert extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php index 6816fc3b13..e040757b7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php @@ -56,6 +56,7 @@ class Delete extends DocumentsDelete ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tables#tablesCreate).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php index 8c640f00f8..72381ee38f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php @@ -58,6 +58,7 @@ class Update extends DocumentsUpdate ->param('tableId', '', new UID(), 'Table ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include only column and value pairs to be updated.', true) ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php index 35f0a99bdc..230f81676a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php @@ -58,6 +58,7 @@ class Upsert extends DocumentsUpsert ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of row data as JSON objects. May contain partial rows.', false, ['plan']) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php index 272510335f..cb04e118ca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php @@ -60,6 +60,7 @@ class Decrement extends DecrementDocumentAttribute ->param('column', '', new Key(), 'Column key.') ->param('value', 1, new Numeric(), 'Value to increment the column by. The value must be a number.', true) ->param('min', null, new Numeric(), 'Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php index 2a28418a6e..4e36a2b912 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php @@ -60,6 +60,7 @@ class Increment extends IncrementDocumentAttribute ->param('column', '', new Key(), 'Column key.') ->param('value', 1, new Numeric(), 'Value to increment the column by. The value must be a number.', true) ->param('max', null, new Numeric(), 'Maximum value for the column. If the current value is greater than this value, an error will be thrown.', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php index d5a84bbb7c..dfbb9a61d2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php @@ -96,6 +96,7 @@ class Create extends DocumentCreate ->param('data', [], new JSON(), 'Row data as JSON object.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('user') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php index f821aabe6e..b13b17b8cd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php @@ -61,6 +61,7 @@ class Delete extends DocumentDelete ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tables#tablesCreate).') ->param('rowId', '', new UID(), 'Row ID.') + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php index 7731e10e9d..c6ce1a9795 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php @@ -60,6 +60,7 @@ class Update extends DocumentUpdate ->param('rowId', '', new UID(), 'Row ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php index d9756eab87..157956ca18 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php @@ -62,6 +62,7 @@ class Upsert extends DocumentUpsert ->param('rowId', '', new UID(), 'Row ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include all required columns of the row to be created or updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('user') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php new file mode 100644 index 0000000000..197148a18a --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php @@ -0,0 +1,117 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/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: 'databases', + group: 'transactions', + name: 'createOperations', + description: '/docs/references/databases/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()), 'Array of staged operations.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('plan') + ->callback($this->action(...)); + } + + public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, array $plan): void + { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + + if (($existing + \count($operations)) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding ' . \count($operations) . ' would exceed the maximum of ' . $maxBatch + ); + } + + $databases = $collections = $staged = []; + foreach ($operations as $operation) { + $database = $databases[$operation['databaseId']] ??= $dbForProject->getDocument('databases', $operation['databaseId']); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = $collections[$operation['collectionId']] ??= $dbForProject->getDocument('database_' . $database->getSequence(), $operation['collectionId']); + if ($collection->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + $staged[] = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $operation['documentId'] ?? ID::unique(), + 'action' => $operation['action'], + 'data' => $operation['data'] ?? new \stdClass(), + ]); + } + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { + $dbForProject->createDocuments('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + \count($operations) + ); + }); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); + } +} \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php new file mode 100644 index 0000000000..9c4577c4c6 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php @@ -0,0 +1,73 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/databases/transactions') + ->desc('Create transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'createTransaction', + description: '/docs/references/databases/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(...)); + } + + public function action(int $ttl, UtopiaResponse $response, Database $dbForProject): void + { + $transaction = $dbForProject->createDocument('transactions', new Document([ + '$id' => ID::unique(), + 'status' => 'pending', + 'operations' => 0, + 'expiresAt' => DateTime::addSeconds(new \DateTime(), $ttl), + ])); + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); + } +} \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php new file mode 100644 index 0000000000..ae6f41fb2d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php @@ -0,0 +1,76 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/databases/transactions/:transactionId') + ->desc('Delete transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'deleteTransaction', + description: '/docs/references/databases/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(...)); + } + + public function action(string $transactionId, UtopiaResponse $response, Database $dbForProject): void + { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + + if (!$dbForProject->deleteDocument('transactions', $transactionId)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove transaction from DB'); + } + + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + + $response->noContent(); + } +} \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php new file mode 100644 index 0000000000..1a8286e658 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php @@ -0,0 +1,69 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/transactions/:transactionId') + ->desc('Get transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'getTransaction', + description: '/docs/references/databases/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(...)); + } + + public function action(string $transactionId, UtopiaResponse $response, Database $dbForProject): void + { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); + } +} \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php new file mode 100644 index 0000000000..d32e8d4c20 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -0,0 +1,249 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/databases/transactions/:transactionId') + ->desc('Update transaction') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'collections.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'updateTransaction', + description: '/docs/references/databases/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('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('project') + ->callback($this->action(...)); + } + + public function action(string $transactionId, bool $commit, bool $rollback, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Document $project): void + { + if (!$commit && !$rollback) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); + } + if ($commit && $rollback) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); + } + + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); + } + + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + + if ($commit) { + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committing', + ])); + + $operations = $dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + + $creates + = $updates + = $deletes + = $increments + = $decrements + = $bulkUpdates + = $bulkDeletes + = []; + + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $documentId = $operation['documentId']; + + switch ($operation['action']) { + case 'create': + $creates[$databaseInternalId][$collectionInternalId][] = new Document([ + '$id' => $documentId ?? ID::unique(), + ...$operation['data'] + ]); + break; + case 'update': + case 'upsert': + $updates[$databaseInternalId][$collectionInternalId][] = new Document([ + '$id' => $documentId, + ...$operation['data'], + ]); + break; + case 'delete': + $deletes[$databaseInternalId][$collectionInternalId][] = $documentId; + break; + case 'increment': + $increments[$databaseInternalId][$collectionInternalId][] = [ + 'attribute' => $operation['data']['attribute'], + 'value' => $operation['data']['value'] ?? 1, + 'max' => $operation['data']['max'] ?? null, + ]; + break; + case 'decrement': + $decrements[$databaseInternalId][$collectionInternalId][] = [ + 'attribute' => $operation['data']['attribute'], + 'value' => $operation['data']['value'] ?? 1, + 'min' => $operation['data']['min'] ?? null, + ]; + break; + case 'bulkUpdate': + $bulkUpdates[$databaseInternalId][$collectionInternalId][] = [ + 'data' => $operation['data']['data'] ?? null, + 'queries' => $operation['data']['queries'] ?? [], + ]; + break; + case 'bulkDelete': + $bulkDeletes[$databaseInternalId][$collectionInternalId][] = [ + 'queries' => $operation['data']['queries'] ?? [], + ]; + break; + } + } + + try { + foreach ($creates as $dbId => $cols) { + foreach ($cols as $colId => $docs) { + $dbForProject->createDocuments("database_{$dbId}_collection_{$colId}", $docs); + } + } + foreach ($updates as $dbId => $cols) { + foreach ($cols as $colId => $docs) { + $dbForProject->createOrUpdateDocuments("database_{$dbId}_collection_{$colId}", $docs); + } + } + foreach ($deletes as $dbId => $cols) { + foreach ($cols as $colId => $ids) { + $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", [ + Query::equal('$id', $ids), + ]); + } + } + foreach ($increments as $dbId => $cols) { + foreach ($cols as $colId => $increments) { + foreach ($increments as $increment) { + $dbForProject->increaseDocumentAttribute( + "database_{$dbId}_collection_{$colId}", + $increment['attribute'], + $increment['value'], + $increment['max'] + ); + } + } + } + foreach ($decrements as $dbId => $cols) { + foreach ($cols as $colId => $decrements) { + foreach ($decrements as $decrement) { + $dbForProject->decreaseDocumentAttribute( + "database_{$dbId}_collection_{$colId}", + $decrement['attribute'], + $decrement['value'], + $decrement['min'] + ); + } + } + } + foreach ($bulkUpdates as $dbId => $cols) { + foreach ($cols as $colId => $updates) { + foreach ($updates as $update) { + $dbForProject->updateDocuments("database_{$dbId}_collection_{$colId}", $update['data'], $update['queries']); + } + } + } + foreach ($bulkDeletes as $dbId => $cols) { + foreach ($cols as $colId => $deletes) { + foreach ($deletes as $delete) { + $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", $delete['queries']); + } + } + } + + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committed', + ])); + + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + } catch (DuplicateException|ConflictException) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + + throw new Exception(Exception::TRANSACTION_CONFLICT); + } + }); + + $transaction = $dbForProject->getDocument('transactions', $transactionId); + } + + if ($rollback) { + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + + $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'rolledBack', + ])); + } + + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); + } +} \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php new file mode 100644 index 0000000000..ee58807d6f --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php @@ -0,0 +1,73 @@ +setHttpMethod(self::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/databases/transactions') + ->desc('List transactions') + ->groups(['api', 'database', 'transactions']) + ->label('scope', 'transactions.read') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('sdk', new Method( + namespace: 'databases', + group: 'transactions', + name: 'listTransactions', + description: '/docs/references/databases/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(...)); + } + + public function action(array $queries, UtopiaResponse $response, Database $dbForProject): void + { + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + $response->dynamic(new Document([ + 'transactions' => $dbForProject->find('transactions', $queries), + 'total' => $dbForProject->count('transactions', $queries), + ]), UtopiaResponse::MODEL_TRANSACTION_LIST); + } +} \ No newline at end of file From 5886c53ce3044916fb22b8ca7f74ea67e0cc6110 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 11 Aug 2025 08:43:30 +0000 Subject: [PATCH 025/274] Add transaction staging support for document operations Co-authored-by: jakeb994 --- .../Documents/Attribute/Decrement.php | 45 ++++++++++++++++ .../Documents/Attribute/Increment.php | 45 ++++++++++++++++ .../Collections/Documents/Bulk/Delete.php | 38 ++++++++++++++ .../Collections/Documents/Bulk/Update.php | 39 ++++++++++++++ .../Collections/Documents/Bulk/Upsert.php | 39 ++++++++++++++ .../Collections/Documents/Create.php | 51 +++++++++++++++++++ .../Collections/Documents/Delete.php | 33 ++++++++++++ .../Collections/Documents/Update.php | 42 +++++++++++++++ .../Collections/Documents/Upsert.php | 41 +++++++++++++++ 9 files changed, 373 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index fa6bc93845..8742499348 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -94,6 +94,51 @@ class Decrement extends Action throw new Exception($this->getParentNotFoundException()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'decrement', + 'data' => [ + 'attribute' => $attribute, + 'value' => $value, + 'min' => $min, + ], + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually decrementing + $mockDocument = new Document([ + '$id' => $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + $attribute => $value, // Mock response - actual value would be computed during commit + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($mockDocument, $this->getResponseModel()); + return; + } + try { $document = $dbForProject->decreaseDocumentAttribute( collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 5bfbc96c7b..f497d781f1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -94,6 +94,51 @@ class Increment extends Action throw new Exception($this->getParentNotFoundException()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'increment', + 'data' => [ + 'attribute' => $attribute, + 'value' => $value, + 'max' => $max, + ], + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually incrementing + $mockDocument = new Document([ + '$id' => $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + $attribute => $value, // Mock response - actual value would be computed during commit + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($mockDocument, $this->getResponseModel()); + return; + } + try { $document = $dbForProject->increaseDocumentAttribute( collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index 42206983bd..19c9c7f005 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -109,6 +109,44 @@ class Delete extends Action throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => null, // Bulk operation doesn't have specific document ID + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => $queries, + ], + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually deleting documents + $response->dynamic(new Document([ + $this->getSdkGroup() => [], + 'total' => 0, // Can't predict how many would be deleted + ]), $this->getResponseModel()); + return; + } + $documents = []; try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 0a199c6eb7..c2e2218c4e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -121,6 +121,45 @@ class Update extends Action throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => null, // Bulk operation doesn't have specific document ID + 'action' => 'bulkUpdate', + 'data' => [ + 'data' => $data, + 'queries' => $queries, + ], + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually updating documents + $response->dynamic(new Document([ + $this->getSdkGroup() => [], + 'total' => 0, // Can't predict how many would be updated + ]), $this->getResponseModel()); + return; + } + if ($data['$permissions']) { $validator = new Permissions(); if (!$validator->isValid($data['$permissions'])) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 01844b52f4..ba05ce414d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -100,6 +100,45 @@ class Upsert extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operations in transaction logs + $staged = []; + foreach ($documents as $document) { + $staged[] = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $document['$id'] ?? ID::unique(), + 'action' => 'upsert', + 'data' => $document, + ]); + } + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocuments('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + \count($staged) + ); + }); + + // Return successful response without actually upserting documents + $response->dynamic(new Document([ + $this->getSdkGroup() => [], + 'total' => \count($documents), + ]), $this->getResponseModel()); + return; + } + foreach ($documents as $key => $document) { $documents[$key] = new Document($document); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 5d4629e7a5..bf447454ed 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -189,6 +189,57 @@ class Create extends Action throw new Exception($this->getParentNotFoundException()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation(s) in transaction logs + $staged = []; + foreach ($documents as $document) { + $staged[] = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $document['$id'] ?? $documentId ?? ID::unique(), + 'action' => 'create', + 'data' => $document, + ]); + } + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocuments('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + \count($staged) + ); + }); + + // Return successful response without actually creating documents + if ($isBulk) { + $response->dynamic(new Document([ + $this->getSdkGroup() => [], + 'total' => \count($documents), + ]), $this->getBulkResponseModel()); + } else { + $mockDocument = new Document([ + '$id' => $documents[0]['$id'] ?? $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + ...$documents[0] + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($mockDocument, $this->getResponseModel()); + } + return; + } + $hasRelationships = \array_filter( $collection->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 75377fe42b..e6dc7e1555 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -104,6 +104,39 @@ class Delete extends Action throw new Exception($this->getNotFoundException()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'delete', + 'data' => [], + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually deleting document + $response->noContent(); + return; + } + try { $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) { $dbForProject->deleteDocument( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index cda4563a4c..33a1ebcc7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -128,6 +128,48 @@ class Update extends Action throw new Exception($this->getNotFoundException()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'update', + 'data' => $data, + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually updating document + $mockDocument = new Document([ + '$id' => $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + ...$document->getArrayCopy(), + ...$data + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($mockDocument, $this->getResponseModel()); + return; + } + // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions, [ Database::PERMISSION_READ, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index bdd5c1de0a..61fdbd312d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -111,6 +111,47 @@ class Upsert extends Action throw new Exception($this->getParentNotFoundException()); } + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'upsert', + 'data' => $data, + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually upserting document + $mockDocument = new Document([ + '$id' => $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + ...$data + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($mockDocument, $this->getResponseModel()); + return; + } + $allowedPermissions = [ Database::PERMISSION_READ, Database::PERMISSION_UPDATE, From 5cec34b802ce310b7d24f113ebd7722eca5d09d4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 12 Aug 2025 00:45:30 +1200 Subject: [PATCH 026/274] Fix merge --- src/Appwrite/Utopia/Response.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 8cf5899823..1e8f9d5ee8 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -218,6 +218,10 @@ class Response extends SwooleResponse public const MODEL_COLUMN_DATETIME = 'columnDatetime'; public const MODEL_COLUMN_RELATIONSHIP = 'columnRelationship'; + // Transactions + public const MODEL_TRANSACTION = 'transaction'; + public const MODEL_TRANSACTION_LIST = 'transactionList'; + // Users public const MODEL_ACCOUNT = 'account'; public const MODEL_USER = 'user'; From a71333ac60ff164c482dc13fe0f7dfb2782e594f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 12 Aug 2025 12:00:28 +0000 Subject: [PATCH 027/274] Refactor transaction replay to preserve exact order and timestamps Co-authored-by: jakeb994 --- .../Databases/Http/Transactions/Update.php | 184 +++++++----------- 1 file changed, 72 insertions(+), 112 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index d32e8d4c20..8793847a38 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -93,124 +93,84 @@ class Update extends Action 'status' => 'committing', ])); + // Fetch operations ordered by creation time to maintain exact sequence $operations = $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc('$createdAt'), ]); - $creates - = $updates - = $deletes - = $increments - = $decrements - = $bulkUpdates - = $bulkDeletes - = []; - - foreach ($operations as $operation) { - $databaseInternalId = $operation['databaseInternalId']; - $collectionInternalId = $operation['collectionInternalId']; - $documentId = $operation['documentId']; - - switch ($operation['action']) { - case 'create': - $creates[$databaseInternalId][$collectionInternalId][] = new Document([ - '$id' => $documentId ?? ID::unique(), - ...$operation['data'] - ]); - break; - case 'update': - case 'upsert': - $updates[$databaseInternalId][$collectionInternalId][] = new Document([ - '$id' => $documentId, - ...$operation['data'], - ]); - break; - case 'delete': - $deletes[$databaseInternalId][$collectionInternalId][] = $documentId; - break; - case 'increment': - $increments[$databaseInternalId][$collectionInternalId][] = [ - 'attribute' => $operation['data']['attribute'], - 'value' => $operation['data']['value'] ?? 1, - 'max' => $operation['data']['max'] ?? null, - ]; - break; - case 'decrement': - $decrements[$databaseInternalId][$collectionInternalId][] = [ - 'attribute' => $operation['data']['attribute'], - 'value' => $operation['data']['value'] ?? 1, - 'min' => $operation['data']['min'] ?? null, - ]; - break; - case 'bulkUpdate': - $bulkUpdates[$databaseInternalId][$collectionInternalId][] = [ - 'data' => $operation['data']['data'] ?? null, - 'queries' => $operation['data']['queries'] ?? [], - ]; - break; - case 'bulkDelete': - $bulkDeletes[$databaseInternalId][$collectionInternalId][] = [ - 'queries' => $operation['data']['queries'] ?? [], - ]; - break; - } - } - try { - foreach ($creates as $dbId => $cols) { - foreach ($cols as $colId => $docs) { - $dbForProject->createDocuments("database_{$dbId}_collection_{$colId}", $docs); - } - } - foreach ($updates as $dbId => $cols) { - foreach ($cols as $colId => $docs) { - $dbForProject->createOrUpdateDocuments("database_{$dbId}_collection_{$colId}", $docs); - } - } - foreach ($deletes as $dbId => $cols) { - foreach ($cols as $colId => $ids) { - $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", [ - Query::equal('$id', $ids), - ]); - } - } - foreach ($increments as $dbId => $cols) { - foreach ($cols as $colId => $increments) { - foreach ($increments as $increment) { - $dbForProject->increaseDocumentAttribute( - "database_{$dbId}_collection_{$colId}", - $increment['attribute'], - $increment['value'], - $increment['max'] - ); + // Replay operations in exact order they were created + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $documentId = $operation['documentId']; + $action = $operation['action']; + $data = $operation['data']; + $operationCreatedAt = new \DateTime($operation['$createdAt']); + + $collectionName = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; + + // Wrap each operation with the timestamp from when it was logged + $dbForProject->withRequestTimestamp($operationCreatedAt, function () use ($dbForProject, $action, $collectionName, $documentId, $data) { + switch ($action) { + case 'create': + $document = new Document([ + '$id' => $documentId ?? ID::unique(), + ...$data + ]); + $dbForProject->createDocument($collectionName, $document); + break; + + case 'update': + case 'upsert': + $document = new Document([ + '$id' => $documentId, + ...$data, + ]); + $dbForProject->createOrUpdateDocument($collectionName, $document); + break; + + case 'delete': + $dbForProject->deleteDocument($collectionName, $documentId); + break; + + case 'increment': + $dbForProject->increaseDocumentAttribute( + collection: $collectionName, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + max: $data['max'] ?? null + ); + break; + + case 'decrement': + $dbForProject->decreaseDocumentAttribute( + collection: $collectionName, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + min: $data['min'] ?? null + ); + break; + + case 'bulkUpdate': + $dbForProject->updateDocuments( + $collectionName, + $data['data'] ?? null, + $data['queries'] ?? [] + ); + break; + + case 'bulkDelete': + $dbForProject->deleteDocuments( + $collectionName, + $data['queries'] ?? [] + ); + break; } - } - } - foreach ($decrements as $dbId => $cols) { - foreach ($cols as $colId => $decrements) { - foreach ($decrements as $decrement) { - $dbForProject->decreaseDocumentAttribute( - "database_{$dbId}_collection_{$colId}", - $decrement['attribute'], - $decrement['value'], - $decrement['min'] - ); - } - } - } - foreach ($bulkUpdates as $dbId => $cols) { - foreach ($cols as $colId => $updates) { - foreach ($updates as $update) { - $dbForProject->updateDocuments("database_{$dbId}_collection_{$colId}", $update['data'], $update['queries']); - } - } - } - foreach ($bulkDeletes as $dbId => $cols) { - foreach ($cols as $colId => $deletes) { - foreach ($deletes as $delete) { - $dbForProject->deleteDocuments("database_{$dbId}_collection_{$colId}", $delete['queries']); - } - } + }); } $dbForProject->updateDocument('transactions', $transactionId, new Document([ From 6d477688d74a53b163b090066fd6e7ba03739bf9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 Aug 2025 09:50:45 +0000 Subject: [PATCH 028/274] Enforce max operations per transaction in database operations Co-authored-by: jakeb994 --- .../Collections/Documents/Attribute/Decrement.php | 15 ++++++++++++++- .../Collections/Documents/Attribute/Increment.php | 15 ++++++++++++++- .../Collections/Documents/Bulk/Delete.php | 10 ++++++++++ .../Collections/Documents/Bulk/Update.php | 10 ++++++++++ .../Collections/Documents/Bulk/Upsert.php | 10 ++++++++++ .../Databases/Collections/Documents/Create.php | 13 ++++++++++++- .../Databases/Collections/Documents/Delete.php | 15 ++++++++++++++- .../Databases/Collections/Documents/Update.php | 15 ++++++++++++++- .../Databases/Collections/Documents/Upsert.php | 13 ++++++++++++- 9 files changed, 110 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 8742499348..b383bffbe6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -79,10 +79,13 @@ class Decrement extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') + ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { @@ -101,6 +104,16 @@ class Decrement extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index f497d781f1..311c07dedf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -79,10 +79,13 @@ class Increment extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') + ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { @@ -101,6 +104,16 @@ class Increment extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index 19c9c7f005..b52f38cf7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -116,6 +116,16 @@ class Delete extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index c2e2218c4e..c6ea163604 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -128,6 +128,16 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index ba05ce414d..50ca33d002 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -107,6 +107,16 @@ class Upsert extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + \count($documents)) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operations in transaction logs $staged = []; foreach ($documents as $document) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index bf447454ed..3a544bb869 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -126,9 +126,10 @@ class Create extends Action ->inject('queueForRealtime') ->inject('queueForFunctions') ->inject('queueForWebhooks') + ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $data = \is_string($data) ? \json_decode($data, true) @@ -196,6 +197,16 @@ class Create extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + \count($documents)) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation(s) in transaction logs $staged = []; foreach ($documents as $document) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index e6dc7e1555..c6a2218f54 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -77,10 +77,13 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') + ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -111,6 +114,16 @@ class Delete extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 33a1ebcc7a..a616f592cb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -83,10 +83,13 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') + ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -135,6 +138,16 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 61fdbd312d..b69f58f48b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -87,10 +87,11 @@ class Upsert extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -118,6 +119,16 @@ class Upsert extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + // Enforce max operations per transaction + $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + // Stage the operation in transaction logs $staged = new Document([ '$id' => ID::unique(), From dffe030d16ee4ae53297329eb339abf384d86656 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 Aug 2025 10:06:43 +0000 Subject: [PATCH 029/274] Replace databasesBatchSize with databasesTransactionSize in database operations Co-authored-by: jakeb994 --- app/init/constants.php | 1 + .../Databases/Collections/Documents/Attribute/Decrement.php | 2 +- .../Databases/Collections/Documents/Attribute/Increment.php | 2 +- .../Http/Databases/Collections/Documents/Bulk/Delete.php | 2 +- .../Http/Databases/Collections/Documents/Bulk/Update.php | 2 +- .../Http/Databases/Collections/Documents/Bulk/Upsert.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Create.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Delete.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Update.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Upsert.php | 2 +- .../Modules/Databases/Http/Transactions/AddOperations.php | 2 +- 11 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index d217174215..c5b83f2594 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -31,6 +31,7 @@ const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls const APP_LIMIT_DATABASE_BATCH = 100; // Default maximum batch size for database operations +const APP_LIMIT_DATABASE_TRANSACTION = 100; // Default maximum operations per transaction const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index b383bffbe6..7013e4bce6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -105,7 +105,7 @@ class Decrement extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 311c07dedf..24a521724b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -105,7 +105,7 @@ class Increment extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index b52f38cf7b..0c27ea420a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -117,7 +117,7 @@ class Delete extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index c6ea163604..8cca45661d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -129,7 +129,7 @@ class Update extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 50ca33d002..0868a2519b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -108,7 +108,7 @@ class Upsert extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + \count($documents)) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 3a544bb869..e0d7d03180 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -198,7 +198,7 @@ class Create extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + \count($documents)) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index c6a2218f54..59f72d727d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -115,7 +115,7 @@ class Delete extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index a616f592cb..76d3bc692b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -139,7 +139,7 @@ class Update extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index b69f58f48b..fff6c53978 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -120,7 +120,7 @@ class Upsert extends Action } // Enforce max operations per transaction - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + 1) > $maxBatch) { throw new Exception( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php index 197148a18a..d8d31f0d8f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php @@ -67,7 +67,7 @@ class AddOperations extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } - $maxBatch = $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH; + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); if (($existing + \count($operations)) > $maxBatch) { From 335052bc847930012482f6e8ebfb7ad3ffebb74d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 Aug 2025 10:17:54 +0000 Subject: [PATCH 030/274] Remove unused function and webhook event queue injections in database actions Co-authored-by: jakeb994 --- .../Databases/Collections/Documents/Attribute/Decrement.php | 4 +--- .../Databases/Collections/Documents/Attribute/Increment.php | 4 +--- .../Databases/Http/Databases/Collections/Documents/Create.php | 4 +--- .../Databases/Http/Databases/Collections/Documents/Delete.php | 4 +--- .../Databases/Http/Databases/Collections/Documents/Update.php | 4 +--- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 7013e4bce6..f71ed4f187 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -79,13 +79,11 @@ class Decrement extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('queueForFunctions') - ->inject('queueForWebhooks') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 24a521724b..d9d9beefa7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -79,13 +79,11 @@ class Increment extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('queueForFunctions') - ->inject('queueForWebhooks') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index e0d7d03180..fe8b688a54 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -124,12 +124,10 @@ class Create extends Action ->inject('queueForEvents') ->inject('queueForStatsUsage') ->inject('queueForRealtime') - ->inject('queueForFunctions') - ->inject('queueForWebhooks') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, array $plan): void { $data = \is_string($data) ? \json_decode($data, true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 59f72d727d..b176d24ef5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -77,13 +77,11 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('queueForFunctions') - ->inject('queueForWebhooks') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 76d3bc692b..611652b48d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -83,13 +83,11 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('queueForFunctions') - ->inject('queueForWebhooks') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array From 31a7df721488e704be720092fafc04faf8dab215 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 14 Aug 2025 22:20:30 +1200 Subject: [PATCH 031/274] Import fixes --- .../Http/Databases/Collections/Documents/Bulk/Delete.php | 1 + .../Http/Databases/Collections/Documents/Bulk/Update.php | 3 ++- .../Http/Databases/Collections/Documents/Bulk/Upsert.php | 1 + .../Http/Databases/Collections/Documents/Create.php | 7 +++++-- .../Http/Databases/Collections/Documents/Delete.php | 9 +++++++-- .../Http/Databases/Collections/Documents/Update.php | 8 ++++++-- .../Http/Databases/Collections/Documents/Upsert.php | 7 +++++-- .../Modules/Databases/Http/Transactions/Create.php | 3 +-- .../{AddOperations.php => Operations/Create.php} | 4 ++-- 9 files changed, 30 insertions(+), 13 deletions(-) rename src/Appwrite/Platform/Modules/Databases/Http/Transactions/{AddOperations.php => Operations/Create.php} (97%) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index 19c9c7f005..727daa8576 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -17,6 +17,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Restricted as RestrictedException; +use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index c2e2218c4e..d34c819fc4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -18,6 +18,7 @@ use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Structure as StructureException; +use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; @@ -108,7 +109,7 @@ class Update extends Action $hasRelationships = \array_filter( $collection->getAttribute('attributes', []), - fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); if ($hasRelationships) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index ba05ce414d..ca94c19302 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -17,6 +17,7 @@ use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Structure as StructureException; +use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 1090b869f5..2eb1896b51 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -194,8 +194,11 @@ class Create extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); } // Stage the operation(s) in transaction logs diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index e6dc7e1555..925b046d17 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -13,8 +13,10 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Restricted as RestrictedException; +use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -107,8 +109,11 @@ class Delete extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); } // Stage the operation in transaction logs diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 33a1ebcc7a..ef65fe9d40 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -120,6 +120,7 @@ class Update extends Action throw new Exception($this->getInvalidStructureException(), 'Attribute "$updatedAt" can not be modified. Please use a server SDK with an API key to modify server attributes.'); } } + // Read permission should not be required for update /** @var Document $document */ $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); @@ -131,8 +132,11 @@ class Update extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); } // Stage the operation in transaction logs diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 61fdbd312d..e598a69be7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -114,8 +114,11 @@ class Upsert extends Action // Handle transaction staging if ($transactionId !== null) { $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); } // Stage the operation in transaction logs diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php index 9c4577c4c6..25a16e19e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php @@ -9,10 +9,9 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; -use Utopia\Database\Validator\UID; -use Utopia\DateTime\DateTime; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Range; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php similarity index 97% rename from src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php rename to src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php index 197148a18a..40f03e4ccb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/AddOperations.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php @@ -1,6 +1,6 @@ Date: Thu, 14 Aug 2025 22:22:12 +1200 Subject: [PATCH 032/274] Fix create inject --- .../Databases/Http/Databases/Collections/Documents/Create.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 0327677372..8fd334fd1e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -126,10 +126,12 @@ class Create extends Action ->inject('queueForEvents') ->inject('queueForStatsUsage') ->inject('queueForRealtime') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, array $plan): void + public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void { $data = \is_string($data) ? \json_decode($data, true) From 76034f801b5893a6a756d0ccbaaee7edce3d47a0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:31:27 +1200 Subject: [PATCH 033/274] Add failed error --- app/config/errors.php | 7 ++++++- src/Appwrite/Extend/Exception.php | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/config/errors.php b/app/config/errors.php index fe64a0ce05..d786940c07 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -963,7 +963,12 @@ return [ ], Exception::TRANSACTION_INVALID => [ 'name' => Exception::TRANSACTION_INVALID, - 'description' => 'The transaction is invalid. Please check the transaction data and try again.', + 'description' => 'The transaction is invalid. Please check the transaction state and try again.', + 'code' => 400, + ], + Exception::TRANSACTION_FAILED => [ + 'name' => Exception::TRANSACTION_FAILED, + 'description' => 'The transaction has errored. Please check the transaction data and try again.', 'code' => 400, ], Exception::TRANSACTION_EXPIRED => [ diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 73185d8fab..2465623d18 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -267,6 +267,7 @@ class Exception extends \Exception public const TRANSACTION_NOT_FOUND = 'transaction_not_found'; public const TRANSACTION_ALREADY_EXISTS = 'transaction_already_exists'; public const TRANSACTION_INVALID = 'transaction_invalid'; + public const TRANSACTION_FAILED = 'transaction_expired'; public const TRANSACTION_EXPIRED = 'transaction_expired'; public const TRANSACTION_CONFLICT = 'transaction_conflict'; public const TRANSACTION_LIMIT_EXCEEDED = 'transaction_limit_exceeded'; From 22358482627f7ade32392ba567283386ec61b2c8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:32:03 +1200 Subject: [PATCH 034/274] Clear txn logs on worker --- app/init/constants.php | 2 ++ .../Databases/Http/Transactions/Update.php | 13 +++++++------ src/Appwrite/Platform/Workers/Deletes.php | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index c5b83f2594..e811fbe306 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -105,8 +105,10 @@ const BUILD_TYPE_RETRY = 'retry'; // Deletion Types const DELETE_TYPE_DATABASES = 'databases'; + const DELETE_TYPE_DOCUMENT = 'document'; const DELETE_TYPE_COLLECTIONS = 'collections'; +const DELETE_TYPE_TRANSACTION = 'transaction'; const DELETE_TYPE_PROJECTS = 'projects'; const DELETE_TYPE_SITES = 'sites'; const DELETE_TYPE_FUNCTIONS = 'functions'; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 8793847a38..db17157783 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -177,9 +177,10 @@ class Update extends Action 'status' => 'committed', ])); - $dbForProject->deleteDocuments('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - ]); + // Clear the transaction logs + $queueForDeletes + ->setType(DELETE_TYPE_DOCUMENT) + ->setDocument($transaction); } catch (DuplicateException|ConflictException) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', @@ -193,9 +194,9 @@ class Update extends Action } if ($rollback) { - $dbForProject->deleteDocuments('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - ]); + $queueForDeletes + ->setType(DELETE_TYPE_DOCUMENT) + ->setDocument($transaction); $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'rolledBack', diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index aa511c2209..c7dda8ccf0 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -130,6 +130,9 @@ class Deletes extends Action case DELETE_TYPE_RULES: $this->deleteRule($dbForPlatform, $document, $certificates); break; + case DELETE_TYPE_TRANSACTION: + $this->deleteTransactionLogs($getProjectDB, $document, $project); + break; default: Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); break; @@ -1299,4 +1302,20 @@ class Deletes extends Action ); } } + + private function deleteTransactionLogs(callable $getProjectDB, Document $document, Document $project): void + { + $dbForProject = $getProjectDB($project); + $transactionId = $document->getId(); + $transactionInternalId = $document->getSequence(); + + try { + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', [$transactionInternalId]), + ]); + Console::info("Transaction logs for {$transactionId} deleted."); + } catch (Throwable $th) { + Console::error("Failed to delete transaction logs for {$transactionId}: " . $th->getMessage()); + } + } } From 3d1ae4feda6d0ab38a050cc2c179305de9bc717b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:34:20 +1200 Subject: [PATCH 035/274] Process permissions before txn logging --- .../Documents/Attribute/Decrement.php | 8 +- .../Documents/Attribute/Increment.php | 4 +- .../Collections/Documents/Bulk/Delete.php | 2 - .../Collections/Documents/Bulk/Update.php | 15 ++- .../Collections/Documents/Bulk/Upsert.php | 33 +++--- .../Collections/Documents/Update.php | 112 +++++++++--------- .../Collections/Documents/Upsert.php | 108 ++++++++--------- 7 files changed, 141 insertions(+), 141 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index f71ed4f187..1a6a7477af 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -13,10 +13,12 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Type as TypeException; +use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; @@ -85,12 +87,12 @@ class Decrement extends Action public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } @@ -142,7 +144,7 @@ class Decrement extends Action '$id' => $documentId, '$collectionId' => $collectionId, '$databaseId' => $databaseId, - $attribute => $value, // Mock response - actual value would be computed during commit + $attribute => $value, ]); $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index d9d9beefa7..b4246377ca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -13,10 +13,12 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; +use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Type as TypeException; +use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; @@ -142,7 +144,7 @@ class Increment extends Action '$id' => $documentId, '$collectionId' => $collectionId, '$databaseId' => $databaseId, - $attribute => $value, // Mock response - actual value would be computed during commit + $attribute => $value, ]); $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index da919a64bf..fedbf7d5b5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -133,7 +133,6 @@ class Delete extends Action 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => null, // Bulk operation doesn't have specific document ID 'action' => 'bulkDelete', 'data' => [ 'queries' => $queries, @@ -146,7 +145,6 @@ class Delete extends Action 'transactions', $transactionId, 'operations', - 1 ); }); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index fdc8efdd83..caaeaced1a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -122,6 +122,13 @@ class Update extends Action throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } + if ($data['$permissions']) { + $validator = new Permissions(); + if (!$validator->isValid($data['$permissions'])) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); + } + } + // Handle transaction staging if ($transactionId !== null) { $transaction = $dbForProject->getDocument('transactions', $transactionId); @@ -145,7 +152,6 @@ class Update extends Action 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => null, // Bulk operation doesn't have specific document ID 'action' => 'bulkUpdate', 'data' => [ 'data' => $data, @@ -171,13 +177,6 @@ class Update extends Action return; } - if ($data['$permissions']) { - $validator = new Permissions(); - if (!$validator->isValid($data['$permissions'])) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); - } - } - $documents = []; try { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index e3cbe56752..b3288cb4fb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -101,6 +101,10 @@ class Upsert extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); } + foreach ($documents as $key => $document) { + $documents[$key] = new Document($document); + } + // Handle transaction staging if ($transactionId !== null) { $transaction = $dbForProject->getDocument('transactions', $transactionId); @@ -111,7 +115,7 @@ class Upsert extends Action // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); - if (($existing + \count($documents)) > $maxBatch) { + if (($existing + 1) > $maxBatch) { throw new Exception( Exception::TRANSACTION_LIMIT_EXCEEDED, 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch @@ -119,21 +123,17 @@ class Upsert extends Action } // Stage the operations in transaction logs - $staged = []; - foreach ($documents as $document) { - $staged[] = new Document([ - '$id' => ID::unique(), - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $document['$id'] ?? ID::unique(), - 'action' => 'upsert', - 'data' => $document, - ]); - } + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'action' => 'bulkUpsert', + 'data' => $documents, + ]); $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { - $dbForProject->createDocuments('transactionLogs', $staged); + $dbForProject->createDocument('transactionLogs', $staged); $dbForProject->increaseDocumentAttribute( 'transactions', $transactionId, @@ -147,11 +147,8 @@ class Upsert extends Action $this->getSdkGroup() => [], 'total' => \count($documents), ]), $this->getResponseModel()); - return; - } - foreach ($documents as $key => $document) { - $documents[$key] = new Document($document); + return; } $upserted = []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 5ae817dbed..0332ca3673 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -130,61 +130,6 @@ class Update extends Action throw new Exception($this->getNotFoundException()); } - // Handle transaction staging - if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty()) { - throw new Exception(Exception::TRANSACTION_NOT_FOUND); - } - if ($transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::TRANSACTION_NOT_READY); - } - - // Enforce max operations per transaction - $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; - $existing = $transaction->getAttribute('operations', 0); - if (($existing + 1) > $maxBatch) { - throw new Exception( - Exception::TRANSACTION_LIMIT_EXCEEDED, - 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch - ); - } - - // Stage the operation in transaction logs - $staged = new Document([ - '$id' => ID::unique(), - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $documentId, - 'action' => 'update', - 'data' => $data, - ]); - - $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { - $dbForProject->createDocument('transactionLogs', $staged); - $dbForProject->increaseDocumentAttribute( - 'transactions', - $transactionId, - 'operations', - 1 - ); - }); - - // Return successful response without actually updating document - $mockDocument = new Document([ - '$id' => $documentId, - '$collectionId' => $collectionId, - '$databaseId' => $databaseId, - ...$document->getArrayCopy(), - ...$data - ]); - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_OK) - ->dynamic($mockDocument, $this->getResponseModel()); - return; - } - // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions, [ Database::PERMISSION_READ, @@ -298,6 +243,63 @@ class Update extends Action ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); + + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); + } + + // Enforce max operations per transaction + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'update', + 'data' => $data, + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually updating document + $mockDocument = new Document([ + '$id' => $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + ...$document->getArrayCopy(), + ...$data + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_OK) + ->dynamic($mockDocument, $this->getResponseModel()); + return; + } + + try { $document = $dbForProject->withRequestTimestamp( $requestTimestamp, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index e1a4153518..316fe4f484 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -112,60 +112,6 @@ class Upsert extends Action throw new Exception($this->getParentNotFoundException()); } - // Handle transaction staging - if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty()) { - throw new Exception(Exception::TRANSACTION_NOT_FOUND); - } - if ($transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::TRANSACTION_NOT_READY); - } - - // Enforce max operations per transaction - $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; - $existing = $transaction->getAttribute('operations', 0); - if (($existing + 1) > $maxBatch) { - throw new Exception( - Exception::TRANSACTION_LIMIT_EXCEEDED, - 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch - ); - } - - // Stage the operation in transaction logs - $staged = new Document([ - '$id' => ID::unique(), - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $documentId, - 'action' => 'upsert', - 'data' => $data, - ]); - - $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { - $dbForProject->createDocument('transactionLogs', $staged); - $dbForProject->increaseDocumentAttribute( - 'transactions', - $transactionId, - 'operations', - 1 - ); - }); - - // Return successful response without actually upserting document - $mockDocument = new Document([ - '$id' => $documentId, - '$collectionId' => $collectionId, - '$databaseId' => $databaseId, - ...$data - ]); - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($mockDocument, $this->getResponseModel()); - return; - } - $allowedPermissions = [ Database::PERMISSION_READ, Database::PERMISSION_UPDATE, @@ -300,6 +246,60 @@ class Upsert extends Action ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); + } + + // Enforce max operations per transaction + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch + ); + } + + // Stage the operation in transaction logs + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $documentId, + 'action' => 'upsert', + 'data' => $data, + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + 1 + ); + }); + + // Return successful response without actually upserting document + $mockDocument = new Document([ + '$id' => $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + ...$data + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($mockDocument, $this->getResponseModel()); + return; + } + $upserted = []; try { $dbForProject->withPreserveDates(function () use (&$upserted, $dbForProject, $database, $collection, $newDocument) { From ede88a533ec31cf6ef374ee54f79e6af03ee6ae1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:35:06 +1200 Subject: [PATCH 036/274] Handle create single/bulk split --- .../Collections/Documents/Create.php | 123 +++++++++--------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 8fd334fd1e..5e7c20b76d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -192,70 +192,6 @@ class Create extends Action throw new Exception($this->getParentNotFoundException()); } - // Handle transaction staging - if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty()) { - throw new Exception(Exception::TRANSACTION_NOT_FOUND); - } - if ($transaction->getAttribute('status', '') !== 'pending') { - throw new Exception(Exception::TRANSACTION_NOT_READY); - } - - // Enforce max operations per transaction - $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; - $existing = $transaction->getAttribute('operations', 0); - if (($existing + \count($documents)) > $maxBatch) { - throw new Exception( - Exception::TRANSACTION_LIMIT_EXCEEDED, - 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch - ); - } - - // Stage the operation(s) in transaction logs - $staged = []; - foreach ($documents as $document) { - $staged[] = new Document([ - '$id' => ID::unique(), - 'databaseInternalId' => $database->getSequence(), - 'collectionInternalId' => $collection->getSequence(), - 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $document['$id'] ?? $documentId ?? ID::unique(), - 'action' => 'create', - 'data' => $document, - ]); - } - - $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { - $dbForProject->createDocuments('transactionLogs', $staged); - $dbForProject->increaseDocumentAttribute( - 'transactions', - $transactionId, - 'operations', - \count($staged) - ); - }); - - // Return successful response without actually creating documents - if ($isBulk) { - $response->dynamic(new Document([ - $this->getSdkGroup() => [], - 'total' => \count($documents), - ]), $this->getBulkResponseModel()); - } else { - $mockDocument = new Document([ - '$id' => $documents[0]['$id'] ?? $documentId, - '$collectionId' => $collectionId, - '$databaseId' => $databaseId, - ...$documents[0] - ]); - $response - ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) - ->dynamic($mockDocument, $this->getResponseModel()); - } - return; - } - $hasRelationships = \array_filter( $collection->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP @@ -439,6 +375,65 @@ class Create extends Action return $document; }, $documents); + // Handle transaction staging + if ($transactionId !== null) { + $transaction = $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { + throw new Exception(Exception::TRANSACTION_NOT_READY); + } + + // Enforce max operations per transaction + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; + $existing = $transaction->getAttribute('operations', 0); + if (($existing + 1) > $maxBatch) { + throw new Exception( + Exception::TRANSACTION_LIMIT_EXCEEDED, + 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch + ); + } + + $staged = new Document([ + '$id' => ID::unique(), + 'databaseInternalId' => $database->getSequence(), + 'collectionInternalId' => $collection->getSequence(), + 'transactionInternalId' => $transaction->getSequence(), + 'documentId' => $isBulk ? null: $documentId, + 'action' => $isBulk ? 'bulkCreate' : 'create', + 'data' => $isBulk ? $documents : $documents[0], + ]); + + $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged) { + $dbForProject->createDocument('transactionLogs', $staged); + $dbForProject->increaseDocumentAttribute( + 'transactions', + $transactionId, + 'operations', + ); + }); + + // Return successful response without actually creating documents + if ($isBulk) { + $response->dynamic(new Document([ + $this->getSdkGroup() => [], + 'total' => \count($documents), + ]), $this->getBulkResponseModel()); + } else { + $mockDocument = new Document([ + '$id' => $documents[0]['$id'] ?? $documentId, + '$collectionId' => $collectionId, + '$databaseId' => $databaseId, + ...$documents[0] + ]); + $response + ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) + ->dynamic($mockDocument, $this->getResponseModel()); + } + return; + } + try { $dbForProject->withPreserveDates( fn () => $dbForProject->createDocuments( From 84bf2641aae9d813f5fdbe425a2c4ff31858da63 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:35:20 +1200 Subject: [PATCH 037/274] Fix scope --- .../Platform/Modules/Databases/Http/Transactions/Update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index db17157783..d41cdbf20d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -38,7 +38,7 @@ class Update extends Action ->setHttpPath('/v1/databases/transactions/:transactionId') ->desc('Update transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'collections.write') + ->label('scope', 'transactions.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', From 3a7d8d2296fdfdf0ef065fc29be436820fc99138 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:35:41 +1200 Subject: [PATCH 038/274] Remove redundant order --- .../Modules/Databases/Http/Transactions/Update.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index d41cdbf20d..d7da3c0df5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -57,14 +57,13 @@ class Update extends Action ->param('transactionId', '', new UID(), 'Transaction ID.') ->param('commit', false, new Boolean(), 'Commit transaction?', true) ->param('rollback', false, new Boolean(), 'Rollback transaction?', true) - ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') - ->inject('project') + ->inject('queueForDeletes') ->callback($this->action(...)); } - public function action(string $transactionId, bool $commit, bool $rollback, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Document $project): void + public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Delete $queueForDeletes): void { if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); @@ -88,15 +87,14 @@ class Update extends Action } if ($commit) { - $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $transaction, $requestTimestamp) { + $dbForProject->withTransaction(function () use ($dbForProject, $queueForDeletes, $transactionId, $transaction) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committing', ])); - // Fetch operations ordered by creation time to maintain exact sequence + // Fetch operations ordered by sequence by default $operations = $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), - Query::orderAsc('$createdAt'), ]); try { From d6544f412de4742e341afdbbe4e5cb0d0be63a02 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:36:26 +1200 Subject: [PATCH 039/274] Add missing bulk cases on commit --- .../Databases/Http/Transactions/Update.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index d7da3c0df5..0240f3e9f6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -152,14 +152,30 @@ class Update extends Action min: $data['min'] ?? null ); break; - + + case 'bulkCreate': + $documents = []; + foreach ($data as $docData) { + $documents[] = new Document($docData); + } + $dbForProject->createDocuments($collectionId, $documents); + break; + case 'bulkUpdate': $dbForProject->updateDocuments( - $collectionName, + $collectionId, $data['data'] ?? null, $data['queries'] ?? [] ); break; + + case 'bulkUpsert': + $documents = []; + foreach ($data as $docData) { + $documents[] = new Document($docData); + } + $dbForProject->createOrUpdateDocuments($collectionId, $documents); + break; case 'bulkDelete': $dbForProject->deleteDocuments( From fb31fbea9bf06a593b60a3e211c9e24187f1cc53 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:36:42 +1200 Subject: [PATCH 040/274] Ensure parsed queries --- .../Platform/Modules/Databases/Http/Transactions/Update.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 0240f3e9f6..419d83eb41 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -165,7 +165,7 @@ class Update extends Action $dbForProject->updateDocuments( $collectionId, $data['data'] ?? null, - $data['queries'] ?? [] + Query::parseQueries($data['queries'] ?? []) ); break; @@ -179,8 +179,8 @@ class Update extends Action case 'bulkDelete': $dbForProject->deleteDocuments( - $collectionName, - $data['queries'] ?? [] + $collectionId, + Query::parseQueries($data['queries'] ?? []) ); break; } From 5cd99de6e4ec969291de7f42b084f13d57e7110c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:36:59 +1200 Subject: [PATCH 041/274] Remove redundant read --- .../Platform/Modules/Databases/Http/Transactions/Update.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 419d83eb41..ee40bbc6cd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -187,7 +187,7 @@ class Update extends Action }); } - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committed', ])); @@ -203,8 +203,6 @@ class Update extends Action throw new Exception(Exception::TRANSACTION_CONFLICT); } }); - - $transaction = $dbForProject->getDocument('transactions', $transactionId); } if ($rollback) { From 9838571184f437b80a08d53b49419fef7c2c0a7a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:37:18 +1200 Subject: [PATCH 042/274] Catch explicit txn exception --- .../Platform/Modules/Databases/Http/Transactions/Update.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index ee40bbc6cd..d42e242e82 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -201,6 +201,12 @@ class Update extends Action ])); throw new Exception(Exception::TRANSACTION_CONFLICT); + } catch (TransactionException $e) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + + throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); } }); } From dfeadfd50f24885b3336e30c3ee182d68f8851d1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:37:29 +1200 Subject: [PATCH 043/274] Update naming --- .../Databases/Http/Transactions/Update.php | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index d42e242e82..27251f3fd9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Transactions; +use Appwrite\Event\Delete; use Appwrite\Extend\Exception; use Appwrite\Platform\Action; use Appwrite\SDK\AuthType; @@ -13,6 +14,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; @@ -102,40 +104,37 @@ class Update extends Action foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; $collectionInternalId = $operation['collectionInternalId']; + $collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; $documentId = $operation['documentId']; + $createdAt = new \DateTime($operation['$createdAt']); $action = $operation['action']; $data = $operation['data']; - $operationCreatedAt = new \DateTime($operation['$createdAt']); - - $collectionName = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; // Wrap each operation with the timestamp from when it was logged - $dbForProject->withRequestTimestamp($operationCreatedAt, function () use ($dbForProject, $action, $collectionName, $documentId, $data) { + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $queueForDeletes, $action, $collectionId, $documentId, $data) { switch ($action) { case 'create': - $document = new Document([ - '$id' => $documentId ?? ID::unique(), - ...$data - ]); - $dbForProject->createDocument($collectionName, $document); + $document = new Document($data); + $dbForProject->createDocument($collectionId, $document); break; - + case 'update': + $document = new Document($data); + $dbForProject->updateDocument($collectionId, $documentId, $document); + break; + case 'upsert': - $document = new Document([ - '$id' => $documentId, - ...$data, - ]); - $dbForProject->createOrUpdateDocument($collectionName, $document); + $document = new Document($data); + $dbForProject->createOrUpdateDocuments($collectionId, [$document]); break; case 'delete': - $dbForProject->deleteDocument($collectionName, $documentId); + $dbForProject->deleteDocument($collectionId, $documentId); break; case 'increment': $dbForProject->increaseDocumentAttribute( - collection: $collectionName, + collection: $collectionId, id: $documentId, attribute: $data['attribute'], value: $data['value'] ?? 1, @@ -145,7 +144,7 @@ class Update extends Action case 'decrement': $dbForProject->decreaseDocumentAttribute( - collection: $collectionName, + collection: $collectionId, id: $documentId, attribute: $data['attribute'], value: $data['value'] ?? 1, From 6e6364638dcef2b5e28f2ca31e31b9efec13e374 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:37:39 +1200 Subject: [PATCH 044/274] Add missing validator ops --- src/Appwrite/Utopia/Database/Validator/Operation.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 989fd76eec..17a2839f59 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -21,7 +21,9 @@ class Operation extends Validator 'update', 'upsert', 'delete', + 'bulkCreate', 'bulkUpdate', + 'bulkUpsert', 'bulkDelete', ]; From 2a5ab1f8aae86eae30f30c94917e46cb49e2f870 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:48:37 +1200 Subject: [PATCH 045/274] Update src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Utopia/Database/Validator/Queries/Transactions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php index ab3e933d6f..8a9604a7ae 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php @@ -4,7 +4,8 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class Transactions extends Base { - public const array ALLOWED_ATTRIBUTES = [ + /** @var string[] */ + public const ALLOWED_ATTRIBUTES = [ 'status', 'expiresAt', ]; From 875338d133273c04acb111e615011782a6e32ca2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 00:48:56 +1200 Subject: [PATCH 046/274] Update src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php index 8a9604a7ae..2f557e5489 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php @@ -12,6 +12,6 @@ class Transactions extends Base public function __construct() { - parent::__construct('functions', self::ALLOWED_ATTRIBUTES); + parent::__construct('transactions', self::ALLOWED_ATTRIBUTES); } } From e9c730e0c1ed267d90a70a3d694e6502158cfddd Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:03:18 +1200 Subject: [PATCH 047/274] Add txn id to create multi-method params --- .../Databases/Http/Databases/Collections/Documents/Create.php | 2 ++ .../Utopia/Database/Validator/Queries/Transactions.php | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 5e7c20b76d..6c9a7ee228 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -82,6 +82,7 @@ class Create extends Action new Parameter('documentId', optional: false), new Parameter('data', optional: false), new Parameter('permissions', optional: true), + new Parameter('transactionId', optional: true), ], deprecated: new Deprecated( since: '1.8.0', @@ -106,6 +107,7 @@ class Create extends Action new Parameter('databaseId', optional: false), new Parameter('collectionId', optional: false), new Parameter('documents', optional: false), + new Parameter('transactionId', optional: true), ], deprecated: new Deprecated( since: '1.8.0', diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php index 2f557e5489..b49494c0af 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Transactions.php @@ -4,8 +4,8 @@ namespace Appwrite\Utopia\Database\Validator\Queries; class Transactions extends Base { - /** @var string[] */ - public const ALLOWED_ATTRIBUTES = [ + /** @var array */ + public const array ALLOWED_ATTRIBUTES = [ 'status', 'expiresAt', ]; From 56d7716ddb1998ea01a3300f59e7d6a755152d86 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:03:36 +1200 Subject: [PATCH 048/274] Require data param for ops --- src/Appwrite/Utopia/Database/Validator/Operation.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 17a2839f59..8ef3817668 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -13,6 +13,7 @@ class Operation extends Validator 'databaseId', 'collectionId', 'action', + 'data', ]; /** @var array */ From 0533568f9e6ad385c123acac5839b9ec6937183b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:03:46 +1200 Subject: [PATCH 049/274] Fix test --- tests/e2e/Services/Databases/Legacy/DatabasesBase.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 11d64077d9..ddc0af99c7 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -6195,10 +6195,10 @@ trait DatabasesBase $this->assertEquals(200, $rollback['headers']['status-code']); $this->assertEquals('rolledBack', $rollback['body']['status']); - $documents = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/collections/$collectionId/documents", [ + $documents = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/collections/$collectionId/documents", \array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()); + ], $this->getHeaders())); $this->assertEquals(0, count($documents['body']['documents'])); } @@ -6260,7 +6260,7 @@ trait DatabasesBase 'databaseId' => $databaseId, 'collectionId' => $collectionId, 'action' => 'create', - 'data' => ['attribute' => 'value'], + 'data' => ['name' => 'value'], ] ] ]); From bed46be72005bf6b34b44c3841e10e9973c904e8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:05:03 +1200 Subject: [PATCH 050/274] Add grids params --- .../Modules/Databases/Http/Grids/Tables/Rows/Create.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php index db6029def9..dbebfd28c4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php @@ -69,6 +69,7 @@ class Create extends DocumentCreate new Parameter('rowId', optional: false), new Parameter('data', optional: false), new Parameter('permissions', optional: true), + new Parameter('transactionId', optional: true), ] ), new Method( @@ -89,6 +90,7 @@ class Create extends DocumentCreate new Parameter('databaseId', optional: false), new Parameter('tableId', optional: false), new Parameter('rows', optional: false), + new Parameter('transactionId', optional: true), ] ) ]) @@ -97,7 +99,7 @@ class Create extends DocumentCreate ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tables#tablesCreate). Make sure to define columns before creating rows.') ->param('data', [], new JSON(), 'Row data as JSON object.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) + ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of rows data as JSON objects.', true, ['plan']) ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') From ad8cebc6e3daf9f8cd8045e2a7570ec62d9c2a4f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:32:09 +1200 Subject: [PATCH 051/274] Ensure updated txn is returned from bulk op add --- .../Modules/Databases/Http/Transactions/Operations/Create.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php index 4974556cec..91631fc257 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php @@ -100,9 +100,9 @@ class Create extends Action ]); } - $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { + $transaction = $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { $dbForProject->createDocuments('transactionLogs', $staged); - $dbForProject->increaseDocumentAttribute( + return $dbForProject->increaseDocumentAttribute( 'transactions', $transactionId, 'operations', From 46f8d76d7a47fd2284a14da0335e358d442d6000 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:32:21 +1200 Subject: [PATCH 052/274] Fix defualts for bulk op add --- .../Modules/Databases/Http/Transactions/Operations/Create.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php index 91631fc257..74f6337751 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php @@ -94,9 +94,9 @@ class Create extends Action 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $operation['documentId'] ?? ID::unique(), + 'documentId' => $operation['documentId'] ?? null, 'action' => $operation['action'], - 'data' => $operation['data'] ?? new \stdClass(), + 'data' => $operation['data'] ?? [], ]); } From 2b3795224d707756059b3c58eb3abfcc3d5c1e90 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:33:22 +1200 Subject: [PATCH 053/274] Check for documentId on ops that require it --- src/Appwrite/Utopia/Database/Validator/Operation.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 8ef3817668..ac585e26bc 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -71,6 +71,15 @@ class Operation extends Validator return false; } + // If action requires documentId, it must be present + if ( + isset($this->requiresDocumentId[$value['action']]) && + !\array_key_exists('documentId', $value) + ) { + $this->description = "Key 'documentId' is required for action '{$value['action']}'"; + return false; + } + // Data must be array (can be empty) if (!\is_array($value['data'])) { $this->description = "Key 'data' must be an array"; From d444bb66b87aef4761a49a81897ef8298e09d6a3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:33:35 +1200 Subject: [PATCH 054/274] Truemaps for fast lookup --- .../Utopia/Database/Validator/Operation.php | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index ac585e26bc..25b9adcd9a 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -16,16 +16,24 @@ class Operation extends Validator 'data', ]; - /** @var array */ + /** @var array */ + private array $requiresDocumentId = [ + 'create' => true, + 'update' => true, + 'upsert' => true, + 'delete' => true, + ]; + + /** @var array */ private array $actions = [ - 'create', - 'update', - 'upsert', - 'delete', - 'bulkCreate', - 'bulkUpdate', - 'bulkUpsert', - 'bulkDelete', + 'create' => true, + 'update' => true, + 'upsert' => true, + 'delete' => true, + 'bulkCreate' => true, + 'bulkUpdate' => true, + 'bulkUpsert' => true, + 'bulkDelete' => true, ]; public function getDescription(): string @@ -66,7 +74,7 @@ class Operation extends Validator } // Validate action - if (!\in_array($value['action'], $this->actions, true)) { + if (!isset($this->actions[$value['action']])) { $this->description = "Key 'action' must be one of: " . \implode(', ', $this->actions); return false; } From 44daaae981c14147cf14d012137cd1fa8828793b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:33:44 +1200 Subject: [PATCH 055/274] Update deps --- composer.lock | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/composer.lock b/composer.lock index b97dbb2e7f..a0d261c518 100644 --- a/composer.lock +++ b/composer.lock @@ -1228,16 +1228,16 @@ }, { "name": "open-telemetry/context", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc" + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc", - "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6", + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6", "shasum": "" }, "require": { @@ -1283,7 +1283,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-04T03:25:06+00:00" + "time": "2025-08-13T01:12:00+00:00" }, { "name": "open-telemetry/exporter-otlp", @@ -3545,16 +3545,16 @@ }, { "name": "utopia-php/database", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "eaa4e275cefdeeb90bcece2f056e05b59f5b1473" + "reference": "33f35f5daeebd587f79abf362cfb512b9df3cd50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/eaa4e275cefdeeb90bcece2f056e05b59f5b1473", - "reference": "eaa4e275cefdeeb90bcece2f056e05b59f5b1473", + "url": "https://api.github.com/repos/utopia-php/database/zipball/33f35f5daeebd587f79abf362cfb512b9df3cd50", + "reference": "33f35f5daeebd587f79abf362cfb512b9df3cd50", "shasum": "" }, "require": { @@ -3595,9 +3595,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.0.0" + "source": "https://github.com/utopia-php/database/tree/1.0.1" }, - "time": "2025-08-11T13:56:31+00:00" + "time": "2025-08-13T12:28:06+00:00" }, { "name": "utopia-php/detector", @@ -5440,16 +5440,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -5468,7 +5468,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -5492,9 +5492,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-07-27T20:03:57+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -8397,18 +8397,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/database", - "version": "dev-feat-transaction-pinning", - "alias": "0.71.6", - "alias_normalized": "0.71.6.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/database": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From 4b4969e79b3ce457a37c0f4710192bb8ca6a5f3f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:40:08 +1200 Subject: [PATCH 056/274] Sync response --- src/Appwrite/Utopia/Response.php | 188 +++++++++---------------------- 1 file changed, 54 insertions(+), 134 deletions(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 1e8f9d5ee8..53f38b9016 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -161,6 +161,7 @@ class Response extends SwooleResponse public const MODEL_METRIC_LIST = 'metricList'; public const MODEL_METRIC_BREAKDOWN = 'metricBreakdown'; public const MODEL_ERROR_DEV = 'errorDev'; + public const MODEL_BASE_LIST = 'baseList'; public const MODEL_USAGE_DATABASES = 'usageDatabases'; public const MODEL_USAGE_DATABASE = 'usageDatabase'; public const MODEL_USAGE_TABLE = 'usageTable'; @@ -378,6 +379,13 @@ class Response extends SwooleResponse // Console public const MODEL_CONSOLE_VARIABLES = 'consoleVariables'; + // Deprecated + public const MODEL_PERMISSIONS = 'permissions'; + public const MODEL_RULE = 'rule'; + public const MODEL_TASK = 'task'; + public const MODEL_DOMAIN = 'domain'; + public const MODEL_DOMAIN_LIST = 'domainList'; + // Tests (keep last) public const MODEL_MOCK = 'mock'; @@ -441,56 +449,29 @@ class Response extends SwooleResponse ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) ->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false)) - ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) - ->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH)) - ->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET)) - ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) - ->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT)) - ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) - ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) - ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) - ->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT)) ->setModel(new BaseList('Dev Keys List', self::MODEL_DEV_KEY_LIST, 'devKeys', self::MODEL_DEV_KEY, true, false)) - ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) - ->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION)) - ->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE)) - ->setModel(new BaseList('Framework Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST, 'frameworkProviderRepositories', self::MODEL_PROVIDER_REPOSITORY_FRAMEWORK)) - ->setModel(new BaseList('Frameworks List', self::MODEL_FRAMEWORK_LIST, 'frameworks', self::MODEL_FRAMEWORK)) - ->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION)) - ->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION)) - ->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY)) - ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) - ->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION)) - ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) - ->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE)) - ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG)) - ->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP)) - ->setModel(new BaseList('Message List', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE)) - ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) - ->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT)) - ->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION)) - ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) + ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) - ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) - ->setModel(new BaseList('Provider List', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER)) - ->setModel(new BaseList('Resource Tokens List', self::MODEL_RESOURCE_TOKEN_LIST, 'tokens', self::MODEL_RESOURCE_TOKEN)) - ->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE)) - ->setModel(new BaseList('Runtime Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST, 'runtimeProviderRepositories', self::MODEL_PROVIDER_REPOSITORY_RUNTIME)) - ->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME)) - ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) - ->setModel(new BaseList('Site Templates List', self::MODEL_TEMPLATE_SITE_LIST, 'templates', self::MODEL_TEMPLATE_SITE)) - ->setModel(new BaseList('Sites List', self::MODEL_SITE_LIST, 'sites', self::MODEL_SITE)) - ->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION)) + ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) + ->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT)) + ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) + ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) + ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) + ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) + ->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE)) ->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS)) + ->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE)) + ->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE)) + ->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER)) + ->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE)) + ->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC)) ->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER)) ->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET)) - ->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM)) - ->setModel(new BaseList('Topic List', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC)) ->setModel(new BaseList('Transaction List', self::MODEL_TRANSACTION_LIST, 'transactions', self::MODEL_TRANSACTION)) - ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) + ->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION)) + ->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT)) + ->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION)) ->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT)) - ->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE)) - ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) // Entities ->setModel(new Database()) // Collection API Models @@ -531,75 +512,30 @@ class Response extends SwooleResponse ->setModel(new AlgoSha()) ->setModel(new AlgoPhpass()) ->setModel(new AlgoBcrypt()) - ->setModel(new AlgoMd5()) - ->setModel(new AlgoPhpass()) ->setModel(new AlgoScrypt()) ->setModel(new AlgoScryptModified()) - ->setModel(new AlgoSha()) - ->setModel(new Attribute()) - ->setModel(new AttributeBoolean()) - ->setModel(new AttributeDatetime()) - ->setModel(new AttributeEmail()) - ->setModel(new AttributeEnum()) - ->setModel(new AttributeFloat()) - ->setModel(new AttributeIP()) - ->setModel(new AttributeInteger()) - ->setModel(new AttributeList()) - ->setModel(new AttributeRelationship()) - ->setModel(new AttributeString()) - ->setModel(new AttributeURL()) - ->setModel(new AuthProvider()) - ->setModel(new Branch()) - ->setModel(new Bucket()) - ->setModel(new Collection()) - ->setModel(new ConsoleVariables()) - ->setModel(new Continent()) - ->setModel(new Country()) - ->setModel(new Currency()) - ->setModel(new Database()) - ->setModel(new Deployment()) - ->setModel(new DetectionFramework()) - ->setModel(new DetectionRuntime()) - ->setModel(new DevKey()) - ->setModel(new Execution()) - ->setModel(new File()) - ->setModel(new Framework()) - ->setModel(new FrameworkAdapter()) - ->setModel(new Func()) - ->setModel(new Headers()) - ->setModel(new HealthAntivirus()) - ->setModel(new HealthCertificate()) - ->setModel(new HealthQueue()) - ->setModel(new HealthStatus()) - ->setModel(new HealthTime()) - ->setModel(new HealthVersion()) + ->setModel(new AlgoArgon2()) + ->setModel(new Account()) + ->setModel(new Preferences()) + ->setModel(new Session()) ->setModel(new Identity()) - ->setModel(new Index()) - ->setModel(new Installation()) + ->setModel(new Token()) ->setModel(new JWT()) - ->setModel(new Key()) - ->setModel(new Language()) ->setModel(new Locale()) ->setModel(new LocaleCode()) - ->setModel(new Log()) - ->setModel(new MFAChallenge()) - ->setModel(new MFAFactors()) - ->setModel(new MFARecoveryCodes()) - ->setModel(new MFAType()) + ->setModel(new File()) + ->setModel(new Bucket()) + ->setModel(new ResourceToken()) + ->setModel(new Team()) ->setModel(new Membership()) - ->setModel(new Message()) - ->setModel(new Metric()) - ->setModel(new MetricBreakdown()) - ->setModel(new Migration()) - ->setModel(new MigrationFirebaseProject()) - ->setModel(new MigrationReport()) - ->setModel(new MockNumber()) - ->setModel(new ModelDocument()) - ->setModel(new Phone()) - ->setModel(new Platform()) - ->setModel(new Preferences()) - ->setModel(new Project()) - ->setModel(new Provider()) + ->setModel(new Site()) + ->setModel(new TemplateSite()) + ->setModel(new TemplateFramework()) + ->setModel(new Func()) + ->setModel(new TemplateFunction()) + ->setModel(new TemplateRuntime()) + ->setModel(new TemplateVariable()) + ->setModel(new Installation()) ->setModel(new ProviderRepository()) ->setModel(new ProviderRepositoryFramework()) ->setModel(new ProviderRepositoryRuntime()) @@ -648,38 +584,22 @@ class Response extends SwooleResponse ->setModel(new Headers()) ->setModel(new Specification()) ->setModel(new Rule()) - ->setModel(new Runtime()) - ->setModel(new Session()) - ->setModel(new Site()) - ->setModel(new Specification()) - ->setModel(new Subscriber()) - ->setModel(new Target()) - ->setModel(new Team()) - ->setModel(new TemplateEmail()) - ->setModel(new TemplateFramework()) - ->setModel(new TemplateFunction()) - ->setModel(new TemplateRuntime()) ->setModel(new TemplateSMS()) - ->setModel(new TemplateSite()) - ->setModel(new TemplateVariable()) - ->setModel(new Token()) + ->setModel(new TemplateEmail()) + ->setModel(new ConsoleVariables()) + ->setModel(new MFAChallenge()) + ->setModel(new MFARecoveryCodes()) + ->setModel(new MFAType()) + ->setModel(new MFAFactors()) + ->setModel(new Provider()) + ->setModel(new Message()) ->setModel(new Topic()) ->setModel(new Transaction()) - ->setModel(new UsageBuckets()) - ->setModel(new UsageCollection()) - ->setModel(new UsageDatabase()) - ->setModel(new UsageDatabases()) - ->setModel(new UsageFunction()) - ->setModel(new UsageFunctions()) - ->setModel(new UsageProject()) - ->setModel(new UsageSite()) - ->setModel(new UsageSites()) - ->setModel(new UsageStorage()) - ->setModel(new UsageUsers()) - ->setModel(new User()) - ->setModel(new Variable()) - ->setModel(new VcsContent()) - ->setModel(new Webhook()) + ->setModel(new Subscriber()) + ->setModel(new Target()) + ->setModel(new Migration()) + ->setModel(new MigrationReport()) + ->setModel(new MigrationFirebaseProject()) // Tests (keep last) ->setModel(new Mock()); From ef8d1b525d2761da3f6efa517084feac93b2efda Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 01:54:45 +1200 Subject: [PATCH 057/274] Fix missing grids injections --- .../Databases/Http/Grids/Tables/Rows/Column/Decrement.php | 1 + .../Databases/Http/Grids/Tables/Rows/Column/Increment.php | 1 + .../Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php | 1 + .../Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php | 1 + .../Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php | 1 + .../Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php | 1 + 6 files changed, 6 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php index cb04e118ca..c5bcf5015e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php @@ -65,6 +65,7 @@ class Decrement extends DecrementDocumentAttribute ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('plan') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php index 4e36a2b912..0902a850a7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php @@ -65,6 +65,7 @@ class Increment extends IncrementDocumentAttribute ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('plan') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php index dbebfd28c4..cf0adddb89 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php @@ -109,6 +109,7 @@ class Create extends DocumentCreate ->inject('queueForRealtime') ->inject('queueForFunctions') ->inject('queueForWebhooks') + ->inject('plan') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php index b13b17b8cd..ca1914b422 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php @@ -67,6 +67,7 @@ class Delete extends DocumentDelete ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('plan') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php index c6ce1a9795..bed6c549f9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php @@ -66,6 +66,7 @@ class Update extends DocumentUpdate ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('plan') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php index 157956ca18..daf9147390 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php @@ -69,6 +69,7 @@ class Upsert extends DocumentUpsert ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('plan') ->callback($this->action(...)); } } From 237263a194fc68385bbc0f56e54d496735abbf6e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 15 Aug 2025 02:01:44 +1200 Subject: [PATCH 058/274] Lint --- .../Collections/Documents/Attribute/Decrement.php | 4 ++-- .../Databases/Collections/Documents/Bulk/Update.php | 2 +- .../Http/Databases/Collections/Documents/Create.php | 4 ++-- .../Modules/Databases/Http/Transactions/Create.php | 2 +- .../Modules/Databases/Http/Transactions/Delete.php | 2 +- .../Modules/Databases/Http/Transactions/Get.php | 2 +- .../Databases/Http/Transactions/Operations/Create.php | 2 +- .../Modules/Databases/Http/Transactions/Update.php | 11 +++++------ .../Modules/Databases/Http/Transactions/XList.php | 2 +- 9 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 1a6a7477af..7f67889a1c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -87,12 +87,12 @@ class Decrement extends Action public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { - $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty()) { throw new Exception($this->getParentNotFoundException()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index caaeaced1a..c198e9cc7b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -109,7 +109,7 @@ class Update extends Action $hasRelationships = \array_filter( $collection->getAttribute('attributes', []), - fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ); if ($hasRelationships) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 6c9a7ee228..d4a7e2cfda 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -402,7 +402,7 @@ class Create extends Action 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $isBulk ? null: $documentId, + 'documentId' => $isBulk ? null : $documentId, 'action' => $isBulk ? 'bulkCreate' : 'create', 'data' => $isBulk ? $documents : $documents[0], ]); @@ -476,7 +476,7 @@ class Create extends Action ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); // per collection $response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED); - + if ($isBulk) { $response->dynamic(new Document([ 'total' => count($documents), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php index 25a16e19e3..2c98565568 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php @@ -69,4 +69,4 @@ class Create extends Action ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); } -} \ No newline at end of file +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php index ae6f41fb2d..511f1140bd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Delete.php @@ -73,4 +73,4 @@ class Delete extends Action $response->noContent(); } -} \ No newline at end of file +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php index 1a8286e658..12b8629b6a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Get.php @@ -66,4 +66,4 @@ class Get extends Action ->setStatusCode(SwooleResponse::STATUS_CODE_OK) ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); } -} \ No newline at end of file +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php index 74f6337751..eac2b930eb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Operations/Create.php @@ -114,4 +114,4 @@ class Create extends Action ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); } -} \ No newline at end of file +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 27251f3fd9..4775fd06c9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -15,7 +15,6 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Transaction as TransactionException; -use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -127,11 +126,11 @@ class Update extends Action $document = new Document($data); $dbForProject->createOrUpdateDocuments($collectionId, [$document]); break; - + case 'delete': $dbForProject->deleteDocument($collectionId, $documentId); break; - + case 'increment': $dbForProject->increaseDocumentAttribute( collection: $collectionId, @@ -141,7 +140,7 @@ class Update extends Action max: $data['max'] ?? null ); break; - + case 'decrement': $dbForProject->decreaseDocumentAttribute( collection: $collectionId, @@ -175,7 +174,7 @@ class Update extends Action } $dbForProject->createOrUpdateDocuments($collectionId, $documents); break; - + case 'bulkDelete': $dbForProject->deleteDocuments( $collectionId, @@ -224,4 +223,4 @@ class Update extends Action ->setStatusCode(SwooleResponse::STATUS_CODE_OK) ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); } -} \ No newline at end of file +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php index ee58807d6f..bf8a33a793 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/XList.php @@ -70,4 +70,4 @@ class XList extends Action 'total' => $dbForProject->count('transactions', $queries), ]), UtopiaResponse::MODEL_TRANSACTION_LIST); } -} \ No newline at end of file +} From 14002dc20d38044ee9f7dcb67b3695aaacf9ebef Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 18 Aug 2025 17:37:16 +1200 Subject: [PATCH 059/274] Remove invalid return --- tests/e2e/Services/Databases/Legacy/DatabasesBase.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 580f9144b6..d652673370 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -4349,8 +4349,6 @@ trait DatabasesBase $this->assertEquals($document['body']['$updatedAt'], DateTime::formatTz('2022-08-01 13:09:23.050')); } - - return $data; } public function testUpdatePermissionsWithEmptyPayload(): array From c9cf38d6304bac6edf5ec62bdf0e7617f915abc5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 01:31:53 +1200 Subject: [PATCH 060/274] Add expired log deletion --- app/init/constants.php | 1 + composer.lock | 169 +++++++++++++----- .../Databases/Http/Transactions/Update.php | 1 + src/Appwrite/Platform/Workers/Deletes.php | 80 ++++++--- 4 files changed, 183 insertions(+), 68 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index 66933149a8..2e22a4ca3c 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -109,6 +109,7 @@ const DELETE_TYPE_DATABASES = 'databases'; const DELETE_TYPE_DOCUMENT = 'document'; const DELETE_TYPE_COLLECTIONS = 'collections'; const DELETE_TYPE_TRANSACTION = 'transaction'; +const DELETE_TYPE_EXPIRED_TRANSACTIONS = 'expired_transactions'; const DELETE_TYPE_PROJECTS = 'projects'; const DELETE_TYPE_SITES = 'sites'; const DELETE_TYPE_FUNCTIONS = 'functions'; diff --git a/composer.lock b/composer.lock index 3bf17bc228..90bb2f12f6 100644 --- a/composer.lock +++ b/composer.lock @@ -2599,16 +2599,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "1c064a0c67749923483216b081066642751cc2c7" + "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7", - "reference": "1c064a0c67749923483216b081066642751cc2c7", + "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", "shasum": "" }, "require": { @@ -2616,6 +2616,7 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -2674,7 +2675,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.2" + "source": "https://github.com/symfony/http-client/tree/v7.3.3" }, "funding": [ { @@ -2694,7 +2695,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-27T07:45:05+00:00" }, { "name": "symfony/http-client-contracts", @@ -2939,6 +2940,86 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, { "name": "symfony/service-contracts", "version": "v3.6.0", @@ -3557,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "1.2.3", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "8a536fead840d9da6ee819fe6b80e0f047997f69" + "reference": "87fb55e86892eecd726635eb1829acb743c2c156" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/8a536fead840d9da6ee819fe6b80e0f047997f69", - "reference": "8a536fead840d9da6ee819fe6b80e0f047997f69", + "url": "https://api.github.com/repos/utopia-php/database/zipball/87fb55e86892eecd726635eb1829acb743c2c156", + "reference": "87fb55e86892eecd726635eb1829acb743c2c156", "shasum": "" }, "require": { @@ -3607,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.2.3" + "source": "https://github.com/utopia-php/database/tree/1.2.4" }, - "time": "2025-08-27T11:47:04+00:00" + "time": "2025-09-01T06:01:09+00:00" }, { "name": "utopia-php/detector", @@ -4109,16 +4190,16 @@ }, { "name": "utopia-php/migration", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "0e4499d9dd2c90c2be188cc5fb7a32d9a892b569" + "reference": "38171023efd3abe650d2abc5ac65f5df52311da6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/0e4499d9dd2c90c2be188cc5fb7a32d9a892b569", - "reference": "0e4499d9dd2c90c2be188cc5fb7a32d9a892b569", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/38171023efd3abe650d2abc5ac65f5df52311da6", + "reference": "38171023efd3abe650d2abc5ac65f5df52311da6", "shasum": "" }, "require": { @@ -4159,9 +4240,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.0.0" + "source": "https://github.com/utopia-php/migration/tree/1.0.1" }, - "time": "2025-08-13T09:15:53+00:00" + "time": "2025-08-28T13:41:25+00:00" }, { "name": "utopia-php/orchestration", @@ -7410,16 +7491,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "shasum": "" }, "require": { @@ -7484,7 +7565,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.3" }, "funding": [ { @@ -7504,7 +7585,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/filesystem", @@ -7646,16 +7727,16 @@ }, { "name": "symfony/options-resolver", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { @@ -7693,7 +7774,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { @@ -7713,7 +7794,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { "name": "symfony/polyfill-ctype", @@ -8047,16 +8128,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "shasum": "" }, "require": { @@ -8088,7 +8169,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.3" }, "funding": [ { @@ -8099,25 +8180,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-08-18T09:42:54+00:00" }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "shasum": "" }, "require": { @@ -8175,7 +8260,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.3" }, "funding": [ { @@ -8195,7 +8280,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "textalk/websocket", diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 4775fd06c9..dfae4f3a50 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -193,6 +193,7 @@ class Update extends Action $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); + } catch (DuplicateException|ConflictException) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index c7dda8ccf0..b68c758169 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -86,14 +86,15 @@ class Deletes extends Action string $executionRetention, string $auditRetention, Log $log - ): void { + ): void + { $payload = $message->getPayload() ?? []; if (empty($payload)) { throw new Exception('Missing payload'); } - $type = $payload['type'] ?? ''; + $type = $payload['type'] ?? ''; $datetime = $payload['datetime'] ?? null; $hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null; $resource = $payload['resource'] ?? null; @@ -185,6 +186,7 @@ class Deletes extends Action $this->deleteAuditLogs($project, $getProjectDB, $auditRetention); $this->deleteUsageStats($project, $getProjectDB, $getLogsDB, $hourlyUsageRetentionDatetime); $this->deleteExpiredSessions($project, $getProjectDB); + $this->deleteExpiredTransactions($project, $getProjectDB); break; default: throw new \Exception('No delete operation for type: ' . \strval($type)); @@ -308,9 +310,9 @@ class Deletes extends Action * @param Document $project * @param callable $getProjectDB * @param string $resource + * @param string|null $resourceType * @return void * @throws Authorization - * @param string|null $resourceType * @throws Exception */ private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource, string $resourceType = null): void @@ -398,7 +400,7 @@ class Deletes extends Action */ private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void { - /** @var Database $dbForProject*/ + /** @var Database $dbForProject */ $dbForProject = $getProjectDB($project); $selects = [...$this->selects, 'time']; @@ -413,7 +415,7 @@ class Deletes extends Action ], $dbForProject); if ($project->getId() !== 'console') { - /** @var Database $dbForLogs*/ + /** @var Database $dbForLogs */ $dbForLogs = call_user_func($getLogsDB, $project); // Delete Usage stats from logsDB @@ -455,16 +457,16 @@ class Deletes extends Action } /** - * @param Database $dbForPlatform - * @param Document $document - * @return void - * @throws Authorization - * @throws DatabaseException - * @throws Conflict - * @throws Restricted - * @throws Structure - * @throws Exception - */ + * @param Database $dbForPlatform + * @param Document $document + * @return void + * @throws Authorization + * @throws DatabaseException + * @throws Conflict + * @throws Restricted + * @throws Structure + * @throws Exception + */ protected function deleteProjectsByTeam(Database $dbForPlatform, callable $getProjectDB, CertificatesAdapter $certificates, Document $document): void { @@ -542,7 +544,7 @@ class Deletes extends Action ); } } catch (Throwable $e) { - Console::error('Error deleting '.$collection->getId().' '.$e->getMessage()); + Console::error('Error deleting ' . $collection->getId() . ' ' . $e->getMessage()); } }); @@ -609,7 +611,7 @@ class Deletes extends Action ); } elseif ($sharedTablesV2) { $queries = \array_map( - fn ($id) => Query::notEqual('$id', $id), + fn($id) => Query::notEqual('$id', $id), $projectCollectionIds ); @@ -953,14 +955,14 @@ class Deletes extends Action } Console::info("Deleting screenshots for deployment " . $deployment->getId()); - $bucket = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')); + $bucket = ValidatorAuthorization::skip(fn() => $dbForPlatform->getDocument('buckets', 'screenshots')); if ($bucket->isEmpty()) { Console::error('Failed to get bucket for deployment screenshots'); return; } foreach ($screenshotIds as $id) { - $file = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id)); + $file = ValidatorAuthorization::skip(fn() => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id)); if ($file->isEmpty()) { Console::error('Failed to get deployment screenshot: ' . $id); @@ -1117,12 +1119,10 @@ class Deletes extends Action array $queries, Database $database, ?callable $callback = null - ): void { + ): void + { $start = \microtime(true); - $deleteBatchSize = Database::DELETE_BATCH_SIZE; - $deleteBatchSize = 500; // TODO: Set right value in DB library after investigation - /** * deleteDocuments uses a cursor, we need to add a unique order by field or use default */ @@ -1130,11 +1130,10 @@ class Deletes extends Action $count = $database->deleteDocuments( $collection, $queries, - $deleteBatchSize, - $callback + onNext: $callback ); } catch (Throwable $th) { - $tenant = $database->getSharedTables() ? 'Tenant:'.$database->getTenant() : ''; + $tenant = $database->getSharedTables() ? 'Tenant:' . $database->getTenant() : ''; Console::error("Failed to delete documents for collection:{$database->getNamespace()}_{$collection} {$tenant} :{$th->getMessage()}"); return; } @@ -1318,4 +1317,33 @@ class Deletes extends Action Console::error("Failed to delete transaction logs for {$transactionId}: " . $th->getMessage()); } } + + private function deleteExpiredTransactions(Document $project, callable $getProjectDB): void + { + $dbForProject = $getProjectDB($project); + $transactionInternalIds = []; + + try { + $dbForProject->deleteDocuments('transactions', [ + Query::equal('status', ['pending']), + Query::lessThan('expiresAt', DateTime::format(new \DateTime())), + ], onNext: function (Document $transaction) use ($dbForProject, $project, &$transactionInternalIds) { + $transactionInternalIds[] = $transaction->getSequence(); + }, onError: function (Throwable $th) use ($project) { + // Swallow errors to avoid breaking the cleanup process + }); + } catch (Throwable $th) { + Console::error("Failed to find expired transactions for project {$project->getId()}: " . $th->getMessage()); + } + + if (empty($transactionInternalIds)) { + return; + } + + $dbForProject->deleteDocuments('transactionLogs', [ + Query::equal('transactionInternalId', $transactionInternalIds), + ], onError: function (Throwable $th) use ($project) { + // Swallow errors to avoid breaking the cleanup process + }); + } } From 3f0ad7f6c76d9da9bd3bbba6f8ce48ae9736aabb Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 03:41:53 +1200 Subject: [PATCH 061/274] Update registry --- .../Modules/Databases/Services/Http.php | 12 +++---- .../Databases/Services/Registry/Databases.php | 31 ------------------- .../Registry/{Collections.php => Legacy.php} | 23 +++++++++++++- .../Registry/{Tables.php => TablesDB.php} | 2 +- .../Services/Registry/Transactions.php | 27 ++++++++++++++++ 5 files changed, 56 insertions(+), 39 deletions(-) delete mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php rename src/Appwrite/Platform/Modules/Databases/Services/Registry/{Collections.php => Legacy.php} (87%) rename src/Appwrite/Platform/Modules/Databases/Services/Registry/{Tables.php => TablesDB.php} (99%) create mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index ccd9dfd140..e5a2d92b40 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -3,9 +3,9 @@ namespace Appwrite\Platform\Modules\Databases\Services; use Appwrite\Platform\Modules\Databases\Http\Init\Timeout; -use Appwrite\Platform\Modules\Databases\Services\Registry\Collections as CollectionsRegistry; -use Appwrite\Platform\Modules\Databases\Services\Registry\Databases as DatabasesRegistry; -use Appwrite\Platform\Modules\Databases\Services\Registry\Tables as TablesRegistry; +use Appwrite\Platform\Modules\Databases\Services\Registry\Legacy as LegacyRegistry; +use Appwrite\Platform\Modules\Databases\Services\Registry\TablesDB as TablesDBRegistry; +use Appwrite\Platform\Modules\Databases\Services\Registry\Transactions as TransactionsRegistry; use Utopia\Platform\Service; class Http extends Service @@ -17,9 +17,9 @@ class Http extends Service $this->addAction(Timeout::getName(), new Timeout()); foreach ([ - DatabasesRegistry::class, - CollectionsRegistry::class, - TablesRegistry::class, + LegacyRegistry::class, + TablesDBRegistry::class, + TransactionsRegistry::class, ] as $registrar) { new $registrar($this); } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php deleted file mode 100644 index 81c9174253..0000000000 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Databases.php +++ /dev/null @@ -1,31 +0,0 @@ -addAction(CreateDatabase::getName(), new CreateDatabase()); - $service->addAction(GetDatabase::getName(), new GetDatabase()); - $service->addAction(UpdateDatabase::getName(), new UpdateDatabase()); - $service->addAction(DeleteDatabase::getName(), new DeleteDatabase()); - $service->addAction(ListDatabases::getName(), new ListDatabases()); - $service->addAction(ListDatabaseLogs::getName(), new ListDatabaseLogs()); - $service->addAction(GetDatabaseUsage::getName(), new GetDatabaseUsage()); - $service->addAction(ListDatabaseUsage::getName(), new ListDatabaseUsage()); - } -} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php similarity index 87% rename from src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php rename to src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php index bb0002ed47..ad51178197 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Collections.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php @@ -48,6 +48,14 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Logs\XList as use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Update as UpdateCollection; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Usage\Get as GetCollectionUsage; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\XList as ListCollections; +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\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; +use Appwrite\Platform\Modules\Databases\Http\Databases\XList as ListDatabases; use Utopia\Platform\Service; /** @@ -59,16 +67,29 @@ use Utopia\Platform\Service; * - Attributes * - Indexes */ -class Collections extends Base +class Legacy extends Base { protected function register(Service $service): void { + $this->registerDatabaseActions($service); $this->registerCollectionActions($service); $this->registerDocumentActions($service); $this->registerAttributeActions($service); $this->registerIndexActions($service); } + public function registerDatabaseActions(Service $service): void + { + $service->addAction(CreateDatabase::getName(), new CreateDatabase()); + $service->addAction(GetDatabase::getName(), new GetDatabase()); + $service->addAction(UpdateDatabase::getName(), new UpdateDatabase()); + $service->addAction(DeleteDatabase::getName(), new DeleteDatabase()); + $service->addAction(ListDatabases::getName(), new ListDatabases()); + $service->addAction(ListDatabaseLogs::getName(), new ListDatabaseLogs()); + $service->addAction(GetDatabaseUsage::getName(), new GetDatabaseUsage()); + $service->addAction(ListDatabaseUsage::getName(), new ListDatabaseUsage()); + } + private function registerCollectionActions(Service $service): void { $service->addAction(CreateCollection::getName(), new CreateCollection()); diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php similarity index 99% rename from src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php rename to src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php index 7772aff933..4d0456d9c0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Tables.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php @@ -66,7 +66,7 @@ use Utopia\Platform\Service; * - Columns * - Column-Indexes */ -class Tables extends Base +class TablesDB extends Base { protected function register(Service $service): void { diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php new file mode 100644 index 0000000000..3b02ffaec0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php @@ -0,0 +1,27 @@ +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()); + } +} \ No newline at end of file From 70ef7059c309db4d3e4d5738606116aa6f6cb33f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 03:42:19 +1200 Subject: [PATCH 062/274] Add increment validation --- .../Utopia/Database/Validator/Operation.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 25b9adcd9a..594648db3e 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -13,7 +13,6 @@ class Operation extends Validator 'databaseId', 'collectionId', 'action', - 'data', ]; /** @var array */ @@ -22,6 +21,8 @@ class Operation extends Validator 'update' => true, 'upsert' => true, 'delete' => true, + 'increment' => true, + 'decrement' => true, ]; /** @var array */ @@ -30,6 +31,8 @@ class Operation extends Validator 'update' => true, 'upsert' => true, 'delete' => true, + 'increment' => true, + 'decrement' => true, 'bulkCreate' => true, 'bulkUpdate' => true, 'bulkUpsert' => true, @@ -75,7 +78,7 @@ class Operation extends Validator // Validate action if (!isset($this->actions[$value['action']])) { - $this->description = "Key 'action' must be one of: " . \implode(', ', $this->actions); + $this->description = "Key 'action' must be one of: " . \implode(', ', array_keys($this->actions)); return false; } @@ -88,7 +91,11 @@ class Operation extends Validator return false; } - // Data must be array (can be empty) + // Data must be present and must be array (can be empty) + if (!\array_key_exists('data', $value)) { + $this->description = "Missing required key: data"; + return false; + } if (!\is_array($value['data'])) { $this->description = "Key 'data' must be an array"; return false; From 7861ee8cbda4eedcdfcea8b304607a63397e5f09 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 03:42:44 +1200 Subject: [PATCH 063/274] Add transaction scopes to test keys --- tests/e2e/Scopes/ProjectCustom.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index c2b4896814..49aabaae67 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -70,6 +70,8 @@ trait ProjectCustom 'databases.write', 'collections.read', 'collections.write', + 'transactions.read', + 'transactions.write', 'tables.read', 'tables.write', 'documents.read', From 04a9ff9c2305344fd172df6df3bf974a7137eee7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 03:42:56 +1200 Subject: [PATCH 064/274] Ensure ID on create doc op --- .../Platform/Modules/Databases/Http/Transactions/Update.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index dfae4f3a50..8282e61ef4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -88,7 +88,7 @@ class Update extends Action } if ($commit) { - $dbForProject->withTransaction(function () use ($dbForProject, $queueForDeletes, $transactionId, $transaction) { + $dbForProject->withTransaction(function () use ($dbForProject, $queueForDeletes, $transactionId, &$transaction) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committing', ])); @@ -113,6 +113,9 @@ class Update extends Action $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $queueForDeletes, $action, $collectionId, $documentId, $data) { switch ($action) { case 'create': + if ($documentId && !isset($data['$id'])) { + $data['$id'] = $documentId; + } $document = new Document($data); $dbForProject->createDocument($collectionId, $document); break; From 5528e9964bb76a03bc667da5327d64d5e22d84f4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 03:53:08 +1200 Subject: [PATCH 065/274] Updates tests --- .../Transactions/ACIDComplianceTest.php | 628 ++++++++++++++++++ .../DatabasesTransactionsTest.php | 363 ++++++++++ 2 files changed, 991 insertions(+) create mode 100644 tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php create mode 100644 tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php diff --git a/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php b/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php new file mode 100644 index 0000000000..4c0b0eecc3 --- /dev/null +++ b/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php @@ -0,0 +1,628 @@ +client->call(Client::METHOD_POST, '/databases', 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, '/databases/' . $databaseId . '/collections', 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 attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, '/databases/' . $databaseId . '/collections/' . $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, + 'attributes' => ['email'] + ]); + + sleep(3); + + // Create first document outside transaction + $doc1 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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, '/databases/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, "/databases/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, "/databases/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, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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 testTransactionConsistency(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', 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, '/databases/' . $databaseId . '/collections', 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 attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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 attribute with min/max constraints + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, '/databases/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, "/databases/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, "/databases/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, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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 testTransactionIsolation(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', 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, '/databases/' . $databaseId . '/collections', 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 attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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, '/databases/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, '/databases/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, "/databases/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' => [ + 'attribute' => 'counter', + 'value' => 10 + ] + ] + ] + ]); + + // Transaction 2: Increment counter by 5 + $this->client->call(Client::METHOD_POST, "/databases/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' => [ + 'attribute' => 'counter', + 'value' => 5 + ] + ] + ] + ]); + + // Commit first transaction + $response1 = $this->client->call(Client::METHOD_PATCH, "/databases/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, "/databases/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, "/databases/{$databaseId}/collections/{$collectionId}/documents/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 testTransactionDurability(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', 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, '/databases/' . $databaseId . '/collections', 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 attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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, '/databases/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, "/databases/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, "/databases/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, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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, "/databases/{$databaseId}/collections/{$collectionId}/documents/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, "/databases/{$databaseId}/collections/{$collectionId}/documents/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, "/databases/{$databaseId}/collections/{$collectionId}/documents/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, "/databases/{$databaseId}/collections/{$collectionId}/documents/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, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(2, $documents['body']['total']); + } +} \ No newline at end of file diff --git a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php new file mode 100644 index 0000000000..35340f8b0e --- /dev/null +++ b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php @@ -0,0 +1,363 @@ +client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test creating a transaction with default TTL + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertArrayHasKey('$id', $response['body']); + $this->assertArrayHasKey('status', $response['body']); + $this->assertArrayHasKey('operations', $response['body']); + $this->assertArrayHasKey('expiresAt', $response['body']); + $this->assertEquals('pending', $response['body']['status']); + $this->assertEquals(0, $response['body']['operations']); + + $transactionId1 = $response['body']['$id']; + + // Test creating a transaction with custom TTL + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 900 + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals('pending', $response['body']['status']); + + $expiresAt = new \DateTime($response['body']['expiresAt']); + $now = new \DateTime(); + $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); + $this->assertGreaterThan(800, $diff); + $this->assertLessThan(1000, $diff); + + $transactionId2 = $response['body']['$id']; + + // Test invalid TTL values + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 30 // Below minimum + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 4000 // Above maximum + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + return [ + 'databaseId' => $databaseId, + 'transactionId1' => $transactionId1, + 'transactionId2' => $transactionId2 + ]; + } + + /** + * @depends testCreate + */ + public function testAddOperations(array $data): array + { + $databaseId = $data['databaseId']; + $transactionId = $data['transactionId1']; + + // Create a collection for testing + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionOperationsTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $collectionId = $collection['body']['$id']; + + // Add attributes + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + // Wait for attribute to be created + sleep(2); + + // Add valid operations + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc1', + 'data' => [ + '$id' => 'doc1', + 'name' => 'Test Document 1' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc2', + 'data' => [ + '$id' => 'doc2', + 'name' => 'Test Document 2' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['operations']); + + // Test adding more operations + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'update', + 'documentId' => 'doc1', + 'data' => [ + 'name' => 'Updated Document 1' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(3, $response['body']['operations']); + + // Test invalid database ID + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => 'invalid_database', + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Test invalid collection ID + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => 'invalid_collection', + 'action' => 'create', + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + return array_merge($data, [ + 'collectionId' => $collectionId + ]); + } + + /** + * @depends testAddOperations + */ + public function testCommit(array $data): void + { + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + $transactionId = $data['transactionId1']; + + // Commit the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('committed', $response['body']['status']); + + // Verify documents were created + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(2, $documents['body']['total']); + + // Verify the update was applied + $doc1Found = false; + foreach ($documents['body']['documents'] as $doc) { + if ($doc['$id'] === 'doc1') { + $this->assertEquals('Updated Document 1', $doc['name']); + $doc1Found = true; + } + } + $this->assertTrue($doc1Found, 'Document doc1 should exist with updated name'); + + // Test committing already committed transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * @depends testCreate + */ + public function testRollback(array $data): void + { + $databaseId = $data['databaseId']; + $transactionId = $data['transactionId2']; + + // Create a collection for rollback test + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionRollbackTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'value', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // Add operations + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'data' => [ + '$id' => 'rollback_doc', + 'value' => 'Should not exist' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Rollback the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rollback' => true + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('rolledBack', $response['body']['status']); + + // Verify no documents were created + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(0, $documents['body']['total']); + } +} \ No newline at end of file From 2202dc9747864db0acf75a5f2215ab06955b8035 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 04:29:42 +1200 Subject: [PATCH 066/274] Handle intra-transactional ops with state tracking --- .../Databases/Http/Transactions/Update.php | 195 ++++++++++++------ .../Services/Registry/Transactions.php | 4 +- src/Appwrite/Platform/Workers/Deletes.php | 12 +- .../Transactions/ACIDComplianceTest.php | 8 +- .../DatabasesTransactionsTest.php | 42 ++-- 5 files changed, 171 insertions(+), 90 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 8282e61ef4..58ec470bd2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -93,13 +93,16 @@ class Update extends Action 'status' => 'committing', ])); - // Fetch operations ordered by sequence by default + // Fetch operations ordered by sequence by default to + // replay operations in exact order they were created $operations = $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), ]); + // Track transaction state for cross-operation visibility + $state = []; + try { - // Replay operations in exact order they were created foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; $collectionInternalId = $operation['collectionInternalId']; @@ -109,33 +112,28 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; - // Wrap each operation with the timestamp from when it was logged - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $queueForDeletes, $action, $collectionId, $documentId, $data) { + // Check if this operation depends on documents created in same transaction + $dependent = \in_array($action, ['update', 'increment', 'decrement']) + && isset($state[$collectionId][$documentId]); + + if ($dependent) { + // Don't use timestamp wrapper for dependent operations switch ($action) { - case 'create': - if ($documentId && !isset($data['$id'])) { - $data['$id'] = $documentId; - } - $document = new Document($data); - $dbForProject->createDocument($collectionId, $document); - break; - case 'update': - $document = new Document($data); - $dbForProject->updateDocument($collectionId, $documentId, $document); - break; - - case 'upsert': - $document = new Document($data); - $dbForProject->createOrUpdateDocuments($collectionId, [$document]); - break; - - case 'delete': - $dbForProject->deleteDocument($collectionId, $documentId); + // Update the state document directly + $existing = $state[$collectionId][$documentId]; + foreach ($data as $key => $value) { + $existing->setAttribute($key, $value); + } + $state[$collectionId][$documentId] = $dbForProject->updateDocument( + $collectionId, + $documentId, + $existing + ); break; case 'increment': - $dbForProject->increaseDocumentAttribute( + $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, attribute: $data['attribute'], @@ -145,7 +143,7 @@ class Update extends Action break; case 'decrement': - $dbForProject->decreaseDocumentAttribute( + $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, attribute: $data['attribute'], @@ -153,51 +151,126 @@ class Update extends Action min: $data['min'] ?? null ); break; - - case 'bulkCreate': - $documents = []; - foreach ($data as $docData) { - $documents[] = new Document($docData); - } - $dbForProject->createDocuments($collectionId, $documents); - break; - - case 'bulkUpdate': - $dbForProject->updateDocuments( - $collectionId, - $data['data'] ?? null, - Query::parseQueries($data['queries'] ?? []) - ); - break; - - case 'bulkUpsert': - $documents = []; - foreach ($data as $docData) { - $documents[] = new Document($docData); - } - $dbForProject->createOrUpdateDocuments($collectionId, $documents); - break; - - case 'bulkDelete': - $dbForProject->deleteDocuments( - $collectionId, - Query::parseQueries($data['queries'] ?? []) - ); - break; } - }); + } else { + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $queueForDeletes, $action, $collectionId, $documentId, $data, &$state) { + switch ($action) { + case 'create': + if ($documentId && !isset($data['$id'])) { + $data['$id'] = $documentId; + } + $state[$collectionId][$documentId] = $dbForProject->createDocument( + $collectionId, + new Document($data), + ); + break; + + case 'update': + $document = new Document($data); + $state[$collectionId][$documentId] = $dbForProject->updateDocument( + $collectionId, + $documentId, + $document, + ); + break; + + case 'upsert': + $document = new Document($data); + $dbForProject->createOrUpdateDocuments( + $collectionId, + [$document], + onNext: function (Document $document) use (&$state, $collectionId) { + $state[$collectionId][$document->getId()] = $document; + } + ); + break; + + case 'delete': + $dbForProject->deleteDocument($collectionId, $documentId); + + if (isset($state[$collectionId][$documentId])) { + unset($state[$collectionId][$documentId]); + } + break; + + case 'increment': + $dbForProject->increaseDocumentAttribute( + collection: $collectionId, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + max: $data['max'] ?? null + ); + break; + + case 'decrement': + $dbForProject->decreaseDocumentAttribute( + collection: $collectionId, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + min: $data['min'] ?? null + ); + break; + + case 'bulkCreate': + $dbForProject->createDocuments( + $collectionId, + array_map(fn ($data) => new Document($data), $data), + onNext: function (Document $document) use (&$state, $collectionId) { + $state[$collectionId][$document->getId()] = $document; + + } + ); + break; + + case 'bulkUpdate': + $dbForProject->updateDocuments( + $collectionId, + $data['data'] ?? null, + Query::parseQueries($data['queries'] ?? []) + ); + break; + + case 'bulkUpsert': + $dbForProject->createOrUpdateDocuments( + $collectionId, + array_map(fn ($data) => new Document($data), $data), + onNext: function (Document $document) use (&$state, $collectionId) { + $state[$collectionId][$document->getId()] = $document; + + } + ); + break; + + case 'bulkDelete': + $dbForProject->deleteDocuments( + $collectionId, + Query::parseQueries($data['queries'] ?? []) + ); + break; + } + }); + } } - $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'committed', - ])); + $transaction = $dbForProject->updateDocument( + 'transactions', + $transactionId, + new Document( + [ + 'status' => 'committed', + ] + ) + ); // Clear the transaction logs $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); - } catch (DuplicateException|ConflictException) { + } catch (DuplicateException|ConflictException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ])); diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php index 3b02ffaec0..b41d6adcdb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php @@ -5,9 +5,9 @@ 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 Appwrite\Platform\Modules\Databases\Http\Transactions\Operations\Create as CreateOperations; use Utopia\Platform\Service; /** @@ -24,4 +24,4 @@ class Transactions extends Base $service->addAction(ListTransactions::getName(), new ListTransactions()); $service->addAction(CreateOperations::getName(), new CreateOperations()); } -} \ No newline at end of file +} diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index b68c758169..b647b57908 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -86,8 +86,7 @@ class Deletes extends Action string $executionRetention, string $auditRetention, Log $log - ): void - { + ): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { @@ -611,7 +610,7 @@ class Deletes extends Action ); } elseif ($sharedTablesV2) { $queries = \array_map( - fn($id) => Query::notEqual('$id', $id), + fn ($id) => Query::notEqual('$id', $id), $projectCollectionIds ); @@ -955,14 +954,14 @@ class Deletes extends Action } Console::info("Deleting screenshots for deployment " . $deployment->getId()); - $bucket = ValidatorAuthorization::skip(fn() => $dbForPlatform->getDocument('buckets', 'screenshots')); + $bucket = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')); if ($bucket->isEmpty()) { Console::error('Failed to get bucket for deployment screenshots'); return; } foreach ($screenshotIds as $id) { - $file = ValidatorAuthorization::skip(fn() => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id)); + $file = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id)); if ($file->isEmpty()) { Console::error('Failed to get deployment screenshot: ' . $id); @@ -1119,8 +1118,7 @@ class Deletes extends Action array $queries, Database $database, ?callable $callback = null - ): void - { + ): void { $start = \microtime(true); /** diff --git a/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php b/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php index 4c0b0eecc3..42a2967685 100644 --- a/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php +++ b/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php @@ -7,12 +7,10 @@ use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Database; -use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Database\Query; class ACIDComplianceTest extends Scope { @@ -162,7 +160,7 @@ class ACIDComplianceTest extends Scope $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, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ 'content-type' => 'application/json', @@ -577,7 +575,7 @@ class ACIDComplianceTest extends Scope '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 @@ -625,4 +623,4 @@ class ACIDComplianceTest extends Scope $this->assertEquals(2, $documents['body']['total']); } -} \ No newline at end of file +} diff --git a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php index 35340f8b0e..1ed8e6963b 100644 --- a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php +++ b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php @@ -37,7 +37,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); $this->assertEquals(201, $response['headers']['status-code']); $this->assertArrayHasKey('$id', $response['body']); @@ -53,13 +54,14 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'ttl' => 900 ]); $this->assertEquals(201, $response['headers']['status-code']); $this->assertEquals('pending', $response['body']['status']); - + $expiresAt = new \DateTime($response['body']['expiresAt']); $now = new \DateTime(); $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); @@ -72,7 +74,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'ttl' => 30 // Below minimum ]); @@ -81,7 +84,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'ttl' => 4000 // Above maximum ]); @@ -142,7 +146,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'operations' => [ [ 'databaseId' => $databaseId, @@ -174,7 +179,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'operations' => [ [ 'databaseId' => $databaseId, @@ -195,7 +201,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'operations' => [ [ 'databaseId' => 'invalid_database', @@ -212,7 +219,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'operations' => [ [ 'databaseId' => $databaseId, @@ -243,7 +251,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'commit' => true ]); @@ -258,7 +267,7 @@ class DatabasesTransactionsTest extends Scope $this->assertEquals(200, $documents['headers']['status-code']); $this->assertEquals(2, $documents['body']['total']); - + // Verify the update was applied $doc1Found = false; foreach ($documents['body']['documents'] as $doc) { @@ -273,7 +282,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'commit' => true ]); @@ -324,7 +334,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'operations' => [ [ 'databaseId' => $databaseId, @@ -344,7 +355,8 @@ class DatabasesTransactionsTest extends Scope $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ 'rollback' => true ]); @@ -360,4 +372,4 @@ class DatabasesTransactionsTest extends Scope $this->assertEquals(200, $documents['headers']['status-code']); $this->assertEquals(0, $documents['body']['total']); } -} \ No newline at end of file +} From 1d8d757960ed8a252d40e1dd6b83d6bfc137d755 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 04:32:59 +1200 Subject: [PATCH 067/274] Add txn to workflow --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cebdc02163..75fb2cce26 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -169,6 +169,7 @@ jobs: Console, Databases/Legacy, Databases/TablesDB, + Databases/Transactions, Functions, FunctionsSchedule, GraphQL, From 76287beca038f58c2bbdd79a8a9c5127ce674055 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 04:36:34 +1200 Subject: [PATCH 068/274] Format --- app/config/collections/projects.php | 170 ++++++++++++++-------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index e48dcef451..7bd1d4a7d8 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2524,138 +2524,138 @@ return [ 'transactions' => [ '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('transactions'), - 'name' => 'Transactions', - 'attributes' => [ + '$id' => ID::custom('transactions'), + 'name' => 'Transactions', + 'attributes' => [ [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_STRING, - 'size' => 16, // pending | committing | committed | rolled_back | failed - 'signed' => true, + '$id' => ID::custom('status'), + 'type' => Database::VAR_STRING, + 'size' => 16, // pending | committing | committed | rolled_back | failed + 'signed' => true, 'required' => false, - 'default' => 'pending', - 'array' => false, - 'filters' => [], + 'default' => 'pending', + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('operations'), - 'type' => Database::VAR_INTEGER, - 'size' => 0, - 'signed' => false, + '$id' => ID::custom('operations'), + 'type' => Database::VAR_INTEGER, + 'size' => 0, + 'signed' => false, 'required' => true, - 'default' => 0, - 'array' => false, - 'filters' => [], + 'default' => 0, + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('expiresAt'), - 'type' => Database::VAR_DATETIME, - 'size' => 0, - 'signed' => true, + '$id' => ID::custom('expiresAt'), + 'type' => Database::VAR_DATETIME, + 'size' => 0, + 'signed' => true, 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], ], ], 'indexes' => [ [ - '$id' => ID::custom('_key_status'), - 'type' => Database::INDEX_KEY, + '$id' => ID::custom('_key_status'), + 'type' => Database::INDEX_KEY, 'attributes' => ['status'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], ], [ - '$id' => ID::custom('_key_expires'), - 'type' => Database::INDEX_KEY, + '$id' => ID::custom('_key_expires'), + 'type' => Database::INDEX_KEY, 'attributes' => ['expiresAt'], - 'lengths' => [], - 'orders' => [Database::ORDER_DESC], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], ], ], ], 'transactionLogs' => [ '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('transactionLogs'), - 'name' => 'Transaction Logs', - 'attributes' => [ + '$id' => ID::custom('transactionLogs'), + 'name' => 'Transaction Logs', + 'attributes' => [ [ - '$id' => ID::custom('transactionInternalId'), - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - 'signed' => true, + '$id' => ID::custom('transactionInternalId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], + 'default' => null, + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('databaseInternalId'), - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - 'signed' => true, + '$id' => ID::custom('databaseInternalId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], + 'default' => null, + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('collectionInternalId'), - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - 'signed' => true, + '$id' => ID::custom('collectionInternalId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], + 'default' => null, + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('documentId'), - 'type' => Database::VAR_STRING, - 'size' => Database::LENGTH_KEY, - 'signed' => true, + '$id' => ID::custom('documentId'), + 'type' => Database::VAR_STRING, + 'size' => Database::LENGTH_KEY, + 'signed' => true, 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], + 'default' => null, + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('action'), - 'type' => Database::VAR_STRING, - 'size' => 32, // create | update | upsert | increment | decrement | delete - 'signed' => true, + '$id' => ID::custom('action'), + 'type' => Database::VAR_STRING, + 'size' => 32, // create | update | upsert | increment | decrement | delete + 'signed' => true, 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], + 'default' => null, + 'array' => false, + 'filters' => [], ], [ - '$id' => ID::custom('data'), - 'type' => Database::VAR_STRING, - 'size' => 65535, - 'signed' => false, + '$id' => ID::custom('data'), + 'type' => Database::VAR_STRING, + 'size' => 65535, + 'signed' => false, 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => ['json'], + 'default' => null, + 'array' => false, + 'filters' => ['json'], ], ], 'indexes' => [ [ - '$id' => ID::custom('_key_transaction'), - 'type' => Database::INDEX_KEY, + '$id' => ID::custom('_key_transaction'), + 'type' => Database::INDEX_KEY, 'attributes' => ['transactionInternalId'], - 'lengths' => [], - 'orders' => [], + 'lengths' => [], + 'orders' => [], ], [ - '$id' => ID::custom('_key_internal_path'), - 'type' => Database::INDEX_KEY, + '$id' => ID::custom('_key_internal_path'), + 'type' => Database::INDEX_KEY, 'attributes' => ['databaseInternalId', 'collectionInternalId'], - 'lengths' => [], - 'orders' => [], + 'lengths' => [], + 'orders' => [], ], ], ], From c34efeff1ebc30d1255db93b7feda2ac987f6f30 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 04:37:48 +1200 Subject: [PATCH 069/274] Add to the other workflow --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 75fb2cce26..2ca0c58db8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -249,6 +249,7 @@ jobs: Console, Databases/Legacy, Databases/TablesDB, + Databases/Transactions, Functions, FunctionsSchedule, GraphQL, From 00f91c444c47b6b30acc7f04cba44ae01880380f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 04:55:31 +1200 Subject: [PATCH 070/274] Fix tests --- .../Databases/Transactions/DatabasesTransactionsTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php index 1ed8e6963b..513051fcc0 100644 --- a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php +++ b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php @@ -155,7 +155,6 @@ class DatabasesTransactionsTest extends Scope 'action' => 'create', 'documentId' => 'doc1', 'data' => [ - '$id' => 'doc1', 'name' => 'Test Document 1' ] ], @@ -165,7 +164,6 @@ class DatabasesTransactionsTest extends Scope 'action' => 'create', 'documentId' => 'doc2', 'data' => [ - '$id' => 'doc2', 'name' => 'Test Document 2' ] ] @@ -208,12 +206,13 @@ class DatabasesTransactionsTest extends Scope 'databaseId' => 'invalid_database', 'collectionId' => $collectionId, 'action' => 'create', + 'documentId' => ID::unique(), 'data' => ['name' => 'Test'] ] ] ]); - $this->assertEquals(404, $response['headers']['status-code']); + $this->assertEquals(404, $response['headers']['status-code'], 'Invalid database should return 404. Got: ' . json_encode($response['body'])); // Test invalid collection ID $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ @@ -226,6 +225,7 @@ class DatabasesTransactionsTest extends Scope 'databaseId' => $databaseId, 'collectionId' => 'invalid_collection', 'action' => 'create', + 'documentId' => ID::unique(), 'data' => ['name' => 'Test'] ] ] @@ -341,8 +341,8 @@ class DatabasesTransactionsTest extends Scope 'databaseId' => $databaseId, 'collectionId' => $collectionId, 'action' => 'create', + 'documentId' => 'rollback_doc', 'data' => [ - '$id' => 'rollback_doc', 'value' => 'Should not exist' ] ] From 903e9b52c23ba7ee2b97227eae889d5245fe6b74 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Sep 2025 23:54:21 +1200 Subject: [PATCH 071/274] Fix size limit exception --- app/config/errors.php | 4 +- app/controllers/general.php | 3 +- app/init/constants.php | 1 + src/Appwrite/Extend/Exception.php | 502 +++++++++--------- .../Databases/Http/Transactions/Update.php | 6 + 5 files changed, 265 insertions(+), 251 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index fa45550ba9..098c87c574 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -988,8 +988,8 @@ return [ ], Exception::TRANSACTION_LIMIT_EXCEEDED => [ 'name' => Exception::TRANSACTION_LIMIT_EXCEEDED, - 'description' => 'The maximum number of transactions has been reached.', - 'code' => 400, + 'description' => 'The maximum number of operations per transaction has been exceeded.', + 'code' => 422, ], Exception::TRANSACTION_NOT_READY => [ 'name' => Exception::TRANSACTION_NOT_READY, diff --git a/app/controllers/general.php b/app/controllers/general.php index 40ce66b574..a87aa45c4f 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1231,7 +1231,7 @@ App::error() } /** - * If its not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php + * If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php */ if (!$publish && $project->getId() !== 'console') { if (!Auth::isPrivilegedUser(Authorization::getRoles())) { @@ -1333,6 +1333,7 @@ App::error() case 409: // Error allowed publicly case 412: // Error allowed publicly case 416: // Error allowed publicly + case 422: // Error allowed publicly case 429: // Error allowed publicly case 451: // Error allowed publicly case 501: // Error allowed publicly diff --git a/app/init/constants.php b/app/init/constants.php index 2e22a4ca3c..2a068eba97 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -56,6 +56,7 @@ const APP_DATABASE_ENCRYPT_SIZE_MIN = 150; const APP_DATABASE_TXN_TTL_MIN = 60; // 1 minute const APP_DATABASE_TXN_TTL_MAX = 3600; // 1 hour const APP_DATABASE_TXN_TTL_DEFAULT = 300; // 5 minutes +const APP_DATABASE_TXN_MAX_OPERATIONS = 100; // Maximum operations per transaction const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_SITES = '/storage/sites'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 558f402ea2..cfa55c540e 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -36,341 +36,341 @@ class Exception extends \Exception */ /** General */ - public const GENERAL_UNKNOWN = 'general_unknown'; - public const GENERAL_MOCK = 'general_mock'; - public const GENERAL_ACCESS_FORBIDDEN = 'general_access_forbidden'; - public const GENERAL_RESOURCE_BLOCKED = 'general_resource_blocked'; - public const GENERAL_UNKNOWN_ORIGIN = 'general_unknown_origin'; - public const GENERAL_API_DISABLED = 'general_api_disabled'; - public const GENERAL_SERVICE_DISABLED = 'general_service_disabled'; - public const GENERAL_UNAUTHORIZED_SCOPE = 'general_unauthorized_scope'; - public const GENERAL_RATE_LIMIT_EXCEEDED = 'general_rate_limit_exceeded'; - public const GENERAL_SMTP_DISABLED = 'general_smtp_disabled'; - public const GENERAL_PHONE_DISABLED = 'general_phone_disabled'; - public const GENERAL_ARGUMENT_INVALID = 'general_argument_invalid'; - public const GENERAL_COLUMN_QUERY_LIMIT_EXCEEDED = 'general_column_query_limit_exceeded'; - public const GENERAL_ATTRIBUTE_QUERY_LIMIT_EXCEEDED = 'general_attribute_query_limit_exceeded'; - public const GENERAL_QUERY_INVALID = 'general_query_invalid'; - public const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; - public const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; - public const GENERAL_SERVER_ERROR = 'general_server_error'; - public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; - public const GENERAL_CODES_DISABLED = 'general_codes_disabled'; - public const GENERAL_USAGE_DISABLED = 'general_usage_disabled'; - public const GENERAL_NOT_IMPLEMENTED = 'general_not_implemented'; - public const GENERAL_INVALID_EMAIL = 'general_invalid_email'; - public const GENERAL_INVALID_PHONE = 'general_invalid_phone'; - public const GENERAL_REGION_ACCESS_DENIED = 'general_region_access_denied'; - public const GENERAL_BAD_REQUEST = 'general_bad_request'; + public const string GENERAL_UNKNOWN = 'general_unknown'; + public const string GENERAL_MOCK = 'general_mock'; + public const string GENERAL_ACCESS_FORBIDDEN = 'general_access_forbidden'; + public const string GENERAL_RESOURCE_BLOCKED = 'general_resource_blocked'; + public const string GENERAL_UNKNOWN_ORIGIN = 'general_unknown_origin'; + public const string GENERAL_API_DISABLED = 'general_api_disabled'; + public const string GENERAL_SERVICE_DISABLED = 'general_service_disabled'; + public const string GENERAL_UNAUTHORIZED_SCOPE = 'general_unauthorized_scope'; + public const string GENERAL_RATE_LIMIT_EXCEEDED = 'general_rate_limit_exceeded'; + public const string GENERAL_SMTP_DISABLED = 'general_smtp_disabled'; + public const string GENERAL_PHONE_DISABLED = 'general_phone_disabled'; + public const string GENERAL_ARGUMENT_INVALID = 'general_argument_invalid'; + public const string GENERAL_COLUMN_QUERY_LIMIT_EXCEEDED = 'general_column_query_limit_exceeded'; + public const string GENERAL_ATTRIBUTE_QUERY_LIMIT_EXCEEDED = 'general_attribute_query_limit_exceeded'; + public const string GENERAL_QUERY_INVALID = 'general_query_invalid'; + public const string GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; + public const string GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; + public const string GENERAL_SERVER_ERROR = 'general_server_error'; + public const string GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; + public const string GENERAL_CODES_DISABLED = 'general_codes_disabled'; + public const string GENERAL_USAGE_DISABLED = 'general_usage_disabled'; + public const string GENERAL_NOT_IMPLEMENTED = 'general_not_implemented'; + public const string GENERAL_INVALID_EMAIL = 'general_invalid_email'; + public const string GENERAL_INVALID_PHONE = 'general_invalid_phone'; + public const string GENERAL_REGION_ACCESS_DENIED = 'general_region_access_denied'; + public const string GENERAL_BAD_REQUEST = 'general_bad_request'; /** Users */ - public const USER_COUNT_EXCEEDED = 'user_count_exceeded'; - public const USER_CONSOLE_COUNT_EXCEEDED = 'user_console_count_exceeded'; - public const USER_JWT_INVALID = 'user_jwt_invalid'; - public const USER_ALREADY_EXISTS = 'user_already_exists'; - public const USER_BLOCKED = 'user_blocked'; - public const USER_INVALID_TOKEN = 'user_invalid_token'; - public const USER_PASSWORD_RESET_REQUIRED = 'user_password_reset_required'; - public const USER_EMAIL_NOT_WHITELISTED = 'user_email_not_whitelisted'; - public const USER_IP_NOT_WHITELISTED = 'user_ip_not_whitelisted'; - public const USER_INVALID_CODE = 'user_invalid_code'; - public const USER_INVALID_CREDENTIALS = 'user_invalid_credentials'; - public const USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited'; - public const USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists'; - public const USER_NOT_FOUND = 'user_not_found'; - public const USER_PASSWORD_RECENTLY_USED = 'password_recently_used'; - public const USER_PASSWORD_PERSONAL_DATA = 'password_personal_data'; - public const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists'; - public const USER_PASSWORD_MISMATCH = 'user_password_mismatch'; - public const USER_SESSION_NOT_FOUND = 'user_session_not_found'; - public const USER_IDENTITY_NOT_FOUND = 'user_identity_not_found'; - public const USER_UNAUTHORIZED = 'user_unauthorized'; - public const USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported'; - public const USER_PHONE_ALREADY_EXISTS = 'user_phone_already_exists'; - public const USER_PHONE_NOT_FOUND = 'user_phone_not_found'; - public const USER_PHONE_NOT_VERIFIED = 'user_phone_not_verified'; - public const USER_EMAIL_NOT_FOUND = 'user_email_not_found'; - public const USER_EMAIL_NOT_VERIFIED = 'user_email_not_verified'; - public const USER_MISSING_ID = 'user_missing_id'; - public const USER_MORE_FACTORS_REQUIRED = 'user_more_factors_required'; - public const USER_INVALID_CHALLENGE = 'user_invalid_challenge'; - public const USER_AUTHENTICATOR_NOT_FOUND = 'user_authenticator_not_found'; - public const USER_AUTHENTICATOR_ALREADY_VERIFIED = 'user_authenticator_already_verified'; - public const USER_RECOVERY_CODES_ALREADY_EXISTS = 'user_recovery_codes_already_exists'; - public const USER_RECOVERY_CODES_NOT_FOUND = 'user_recovery_codes_not_found'; - public const USER_CHALLENGE_REQUIRED = 'user_challenge_required'; - public const USER_OAUTH2_BAD_REQUEST = 'user_oauth2_bad_request'; - public const USER_OAUTH2_UNAUTHORIZED = 'user_oauth2_unauthorized'; - public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error'; - public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_already_verified'; - public const USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_verified'; - public const USER_DELETION_PROHIBITED = 'user_deletion_prohibited'; - public const USER_TARGET_NOT_FOUND = 'user_target_not_found'; - public const USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists'; - public const USER_API_KEY_AND_SESSION_SET = 'user_key_and_session_set'; + public const string USER_COUNT_EXCEEDED = 'user_count_exceeded'; + public const string USER_CONSOLE_COUNT_EXCEEDED = 'user_console_count_exceeded'; + public const string USER_JWT_INVALID = 'user_jwt_invalid'; + public const string USER_ALREADY_EXISTS = 'user_already_exists'; + public const string USER_BLOCKED = 'user_blocked'; + public const string USER_INVALID_TOKEN = 'user_invalid_token'; + public const string USER_PASSWORD_RESET_REQUIRED = 'user_password_reset_required'; + public const string USER_EMAIL_NOT_WHITELISTED = 'user_email_not_whitelisted'; + public const string USER_IP_NOT_WHITELISTED = 'user_ip_not_whitelisted'; + public const string USER_INVALID_CODE = 'user_invalid_code'; + public const string USER_INVALID_CREDENTIALS = 'user_invalid_credentials'; + public const string USER_ANONYMOUS_CONSOLE_PROHIBITED = 'user_anonymous_console_prohibited'; + public const string USER_SESSION_ALREADY_EXISTS = 'user_session_already_exists'; + public const string USER_NOT_FOUND = 'user_not_found'; + public const string USER_PASSWORD_RECENTLY_USED = 'password_recently_used'; + public const string USER_PASSWORD_PERSONAL_DATA = 'password_personal_data'; + public const string USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists'; + public const string USER_PASSWORD_MISMATCH = 'user_password_mismatch'; + public const string USER_SESSION_NOT_FOUND = 'user_session_not_found'; + public const string USER_IDENTITY_NOT_FOUND = 'user_identity_not_found'; + public const string USER_UNAUTHORIZED = 'user_unauthorized'; + public const string USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported'; + public const string USER_PHONE_ALREADY_EXISTS = 'user_phone_already_exists'; + public const string USER_PHONE_NOT_FOUND = 'user_phone_not_found'; + public const string USER_PHONE_NOT_VERIFIED = 'user_phone_not_verified'; + public const string USER_EMAIL_NOT_FOUND = 'user_email_not_found'; + public const string USER_EMAIL_NOT_VERIFIED = 'user_email_not_verified'; + public const string USER_MISSING_ID = 'user_missing_id'; + public const string USER_MORE_FACTORS_REQUIRED = 'user_more_factors_required'; + public const string USER_INVALID_CHALLENGE = 'user_invalid_challenge'; + public const string USER_AUTHENTICATOR_NOT_FOUND = 'user_authenticator_not_found'; + public const string USER_AUTHENTICATOR_ALREADY_VERIFIED = 'user_authenticator_already_verified'; + public const string USER_RECOVERY_CODES_ALREADY_EXISTS = 'user_recovery_codes_already_exists'; + public const string USER_RECOVERY_CODES_NOT_FOUND = 'user_recovery_codes_not_found'; + public const string USER_CHALLENGE_REQUIRED = 'user_challenge_required'; + public const string USER_OAUTH2_BAD_REQUEST = 'user_oauth2_bad_request'; + public const string USER_OAUTH2_UNAUTHORIZED = 'user_oauth2_unauthorized'; + public const string USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error'; + public const string USER_EMAIL_ALREADY_VERIFIED = 'user_email_already_verified'; + public const string USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_verified'; + public const string USER_DELETION_PROHIBITED = 'user_deletion_prohibited'; + public const string USER_TARGET_NOT_FOUND = 'user_target_not_found'; + public const string USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists'; + public const string USER_API_KEY_AND_SESSION_SET = 'user_key_and_session_set'; - public const API_KEY_EXPIRED = 'api_key_expired'; + public const string API_KEY_EXPIRED = 'api_key_expired'; /** Teams */ - public const TEAM_NOT_FOUND = 'team_not_found'; - public const TEAM_INVITE_NOT_FOUND = 'team_invite_not_found'; - public const TEAM_INVALID_SECRET = 'team_invalid_secret'; - public const TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch'; - public const TEAM_INVITE_MISMATCH = 'team_invite_mismatch'; - public const TEAM_ALREADY_EXISTS = 'team_already_exists'; + public const string TEAM_NOT_FOUND = 'team_not_found'; + public const string TEAM_INVITE_NOT_FOUND = 'team_invite_not_found'; + public const string TEAM_INVALID_SECRET = 'team_invalid_secret'; + public const string TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch'; + public const string TEAM_INVITE_MISMATCH = 'team_invite_mismatch'; + public const string TEAM_ALREADY_EXISTS = 'team_already_exists'; /** Console */ - public const RESOURCE_ALREADY_EXISTS = 'resource_already_exists'; + public const string RESOURCE_ALREADY_EXISTS = 'resource_already_exists'; /** Membership */ - public const MEMBERSHIP_NOT_FOUND = 'membership_not_found'; - public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; - public const MEMBERSHIP_DELETION_PROHIBITED = 'membership_deletion_prohibited'; - public const MEMBERSHIP_DOWNGRADE_PROHIBITED = 'membership_downgrade_prohibited'; + public const string MEMBERSHIP_NOT_FOUND = 'membership_not_found'; + public const string MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; + public const string MEMBERSHIP_DELETION_PROHIBITED = 'membership_deletion_prohibited'; + public const string MEMBERSHIP_DOWNGRADE_PROHIBITED = 'membership_downgrade_prohibited'; /** Avatars */ - public const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found'; - public const AVATAR_NOT_FOUND = 'avatar_not_found'; - public const AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found'; - public const AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed'; - public const AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found'; - public const AVATAR_SVG_SANITIZATION_FAILED = 'avatar_svg_sanitization_failed'; + public const string AVATAR_SET_NOT_FOUND = 'avatar_set_not_found'; + public const string AVATAR_NOT_FOUND = 'avatar_not_found'; + public const string AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found'; + public const string AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed'; + public const string AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found'; + public const string AVATAR_SVG_SANITIZATION_FAILED = 'avatar_svg_sanitization_failed'; /** Storage */ - public const STORAGE_FILE_ALREADY_EXISTS = 'storage_file_already_exists'; - public const STORAGE_FILE_NOT_FOUND = 'storage_file_not_found'; - public const STORAGE_DEVICE_NOT_FOUND = 'storage_device_not_found'; - public const STORAGE_FILE_EMPTY = 'storage_file_empty'; - public const STORAGE_FILE_TYPE_UNSUPPORTED = 'storage_file_type_unsupported'; - public const STORAGE_INVALID_FILE_SIZE = 'storage_invalid_file_size'; - public const STORAGE_INVALID_FILE = 'storage_invalid_file'; - public const STORAGE_BUCKET_ALREADY_EXISTS = 'storage_bucket_already_exists'; - public const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found'; - public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range'; - public const STORAGE_INVALID_RANGE = 'storage_invalid_range'; - public const STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id'; - public const STORAGE_FILE_NOT_PUBLIC = 'storage_file_not_public'; + public const string STORAGE_FILE_ALREADY_EXISTS = 'storage_file_already_exists'; + public const string STORAGE_FILE_NOT_FOUND = 'storage_file_not_found'; + public const string STORAGE_DEVICE_NOT_FOUND = 'storage_device_not_found'; + public const string STORAGE_FILE_EMPTY = 'storage_file_empty'; + public const string STORAGE_FILE_TYPE_UNSUPPORTED = 'storage_file_type_unsupported'; + public const string STORAGE_INVALID_FILE_SIZE = 'storage_invalid_file_size'; + public const string STORAGE_INVALID_FILE = 'storage_invalid_file'; + public const string STORAGE_BUCKET_ALREADY_EXISTS = 'storage_bucket_already_exists'; + public const string STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found'; + public const string STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range'; + public const string STORAGE_INVALID_RANGE = 'storage_invalid_range'; + public const string STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id'; + public const string STORAGE_FILE_NOT_PUBLIC = 'storage_file_not_public'; /** VCS */ - public const INSTALLATION_NOT_FOUND = 'installation_not_found'; - public const PROVIDER_REPOSITORY_NOT_FOUND = 'provider_repository_not_found'; - public const REPOSITORY_NOT_FOUND = 'repository_not_found'; - public const PROVIDER_CONTRIBUTION_CONFLICT = 'provider_contribution_conflict'; - public const GENERAL_PROVIDER_FAILURE = 'general_provider_failure'; + public const string INSTALLATION_NOT_FOUND = 'installation_not_found'; + public const string PROVIDER_REPOSITORY_NOT_FOUND = 'provider_repository_not_found'; + public const string REPOSITORY_NOT_FOUND = 'repository_not_found'; + public const string PROVIDER_CONTRIBUTION_CONFLICT = 'provider_contribution_conflict'; + public const string GENERAL_PROVIDER_FAILURE = 'general_provider_failure'; /** Sites */ - public const SITE_NOT_FOUND = 'site_not_found'; - public const SITE_TEMPLATE_NOT_FOUND = 'site_template_not_found'; + public const string SITE_NOT_FOUND = 'site_not_found'; + public const string SITE_TEMPLATE_NOT_FOUND = 'site_template_not_found'; /** Functions */ - public const FUNCTION_NOT_FOUND = 'function_not_found'; - public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported'; - public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing'; - public const FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout'; - public const FUNCTION_TEMPLATE_NOT_FOUND = 'function_template_not_found'; - public const FUNCTION_RUNTIME_NOT_DETECTED = 'function_runtime_not_detected'; - public const FUNCTION_EXECUTE_PERMISSION_MISSING = 'function_execute_permission_missing'; + public const string FUNCTION_NOT_FOUND = 'function_not_found'; + public const string FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported'; + public const string FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing'; + public const string FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout'; + public const string FUNCTION_TEMPLATE_NOT_FOUND = 'function_template_not_found'; + public const string FUNCTION_RUNTIME_NOT_DETECTED = 'function_runtime_not_detected'; + public const string FUNCTION_EXECUTE_PERMISSION_MISSING = 'function_execute_permission_missing'; /** Deployments */ - public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found'; + public const string DEPLOYMENT_NOT_FOUND = 'deployment_not_found'; /** Builds */ - public const BUILD_NOT_FOUND = 'build_not_found'; - public const BUILD_NOT_READY = 'build_not_ready'; - public const BUILD_IN_PROGRESS = 'build_in_progress'; - public const BUILD_ALREADY_COMPLETED = 'build_already_completed'; - public const BUILD_CANCELED = 'build_canceled'; - public const BUILD_FAILED = 'build_failed'; + public const string BUILD_NOT_FOUND = 'build_not_found'; + public const string BUILD_NOT_READY = 'build_not_ready'; + public const string BUILD_IN_PROGRESS = 'build_in_progress'; + public const string BUILD_ALREADY_COMPLETED = 'build_already_completed'; + public const string BUILD_CANCELED = 'build_canceled'; + public const string BUILD_FAILED = 'build_failed'; /** Execution */ - public const EXECUTION_NOT_FOUND = 'execution_not_found'; - public const EXECUTION_IN_PROGRESS = 'execution_in_progress'; + public const string EXECUTION_NOT_FOUND = 'execution_not_found'; + public const string EXECUTION_IN_PROGRESS = 'execution_in_progress'; /** Log */ - public const LOG_NOT_FOUND = 'log_not_found'; + public const string LOG_NOT_FOUND = 'log_not_found'; /** Databases */ - public const DATABASE_NOT_FOUND = 'database_not_found'; - public const DATABASE_ALREADY_EXISTS = 'database_already_exists'; - public const DATABASE_TIMEOUT = 'database_timeout'; - public const DATABASE_QUERY_ORDER_NULL = 'database_query_order_null'; + public const string DATABASE_NOT_FOUND = 'database_not_found'; + public const string DATABASE_ALREADY_EXISTS = 'database_already_exists'; + public const string DATABASE_TIMEOUT = 'database_timeout'; + public const string DATABASE_QUERY_ORDER_NULL = 'database_query_order_null'; /** Collections */ - public const COLLECTION_NOT_FOUND = 'collection_not_found'; - public const COLLECTION_ALREADY_EXISTS = 'collection_already_exists'; - public const COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded'; + public const string COLLECTION_NOT_FOUND = 'collection_not_found'; + public const string COLLECTION_ALREADY_EXISTS = 'collection_already_exists'; + public const string COLLECTION_LIMIT_EXCEEDED = 'collection_limit_exceeded'; /** Tables */ - public const TABLE_NOT_FOUND = 'table_not_found'; - public const TABLE_ALREADY_EXISTS = 'table_already_exists'; - public const TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; + public const string TABLE_NOT_FOUND = 'table_not_found'; + public const string TABLE_ALREADY_EXISTS = 'table_already_exists'; + public const string TABLE_LIMIT_EXCEEDED = 'table_limit_exceeded'; /** Documents */ - public const DOCUMENT_NOT_FOUND = 'document_not_found'; - public const DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure'; - public const DOCUMENT_MISSING_DATA = 'document_missing_data'; - public const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload'; - public const DOCUMENT_ALREADY_EXISTS = 'document_already_exists'; - public const DOCUMENT_UPDATE_CONFLICT = 'document_update_conflict'; - public const DOCUMENT_DELETE_RESTRICTED = 'document_delete_restricted'; + public const string DOCUMENT_NOT_FOUND = 'document_not_found'; + public const string DOCUMENT_INVALID_STRUCTURE = 'document_invalid_structure'; + public const string DOCUMENT_MISSING_DATA = 'document_missing_data'; + public const string DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload'; + public const string DOCUMENT_ALREADY_EXISTS = 'document_already_exists'; + public const string DOCUMENT_UPDATE_CONFLICT = 'document_update_conflict'; + public const string DOCUMENT_DELETE_RESTRICTED = 'document_delete_restricted'; /** Rows */ - public const ROW_NOT_FOUND = 'row_not_found'; - public const ROW_INVALID_STRUCTURE = 'row_invalid_structure'; - public const ROW_MISSING_DATA = 'row_missing_data'; - public const ROW_MISSING_PAYLOAD = 'row_missing_payload'; - public const ROW_ALREADY_EXISTS = 'row_already_exists'; - public const ROW_UPDATE_CONFLICT = 'row_update_conflict'; - public const ROW_DELETE_RESTRICTED = 'row_delete_restricted'; + public const string ROW_NOT_FOUND = 'row_not_found'; + public const string ROW_INVALID_STRUCTURE = 'row_invalid_structure'; + public const string ROW_MISSING_DATA = 'row_missing_data'; + public const string ROW_MISSING_PAYLOAD = 'row_missing_payload'; + public const string ROW_ALREADY_EXISTS = 'row_already_exists'; + public const string ROW_UPDATE_CONFLICT = 'row_update_conflict'; + public const string ROW_DELETE_RESTRICTED = 'row_delete_restricted'; /** Attributes */ - public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found'; - public const ATTRIBUTE_UNKNOWN = 'attribute_unknown'; - public const ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available'; - public const ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported'; - public const ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported'; - public const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists'; - public const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded'; - public const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid'; - public const ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid'; - public const ATTRIBUTE_INVALID_RESIZE = 'attribute_invalid_resize'; + public const string ATTRIBUTE_NOT_FOUND = 'attribute_not_found'; + public const string ATTRIBUTE_UNKNOWN = 'attribute_unknown'; + public const string ATTRIBUTE_NOT_AVAILABLE = 'attribute_not_available'; + public const string ATTRIBUTE_FORMAT_UNSUPPORTED = 'attribute_format_unsupported'; + public const string ATTRIBUTE_DEFAULT_UNSUPPORTED = 'attribute_default_unsupported'; + public const string ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists'; + public const string ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded'; + public const string ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid'; + public const string ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid'; + public const string ATTRIBUTE_INVALID_RESIZE = 'attribute_invalid_resize'; /** Columns */ - public const COLUMN_NOT_FOUND = 'column_not_found'; - public const COLUMN_UNKNOWN = 'column_unknown'; - public const COLUMN_NOT_AVAILABLE = 'column_not_available'; - public const COLUMN_FORMAT_UNSUPPORTED = 'column_format_unsupported'; - public const COLUMN_DEFAULT_UNSUPPORTED = 'column_default_unsupported'; - public const COLUMN_ALREADY_EXISTS = 'column_already_exists'; - public const COLUMN_LIMIT_EXCEEDED = 'column_limit_exceeded'; - public const COLUMN_VALUE_INVALID = 'column_value_invalid'; - public const COLUMN_TYPE_INVALID = 'column_type_invalid'; - public const COLUMN_INVALID_RESIZE = 'column_invalid_resize'; + public const string COLUMN_NOT_FOUND = 'column_not_found'; + public const string COLUMN_UNKNOWN = 'column_unknown'; + public const string COLUMN_NOT_AVAILABLE = 'column_not_available'; + public const string COLUMN_FORMAT_UNSUPPORTED = 'column_format_unsupported'; + public const string COLUMN_DEFAULT_UNSUPPORTED = 'column_default_unsupported'; + public const string COLUMN_ALREADY_EXISTS = 'column_already_exists'; + public const string COLUMN_LIMIT_EXCEEDED = 'column_limit_exceeded'; + public const string COLUMN_VALUE_INVALID = 'column_value_invalid'; + public const string COLUMN_TYPE_INVALID = 'column_type_invalid'; + public const string COLUMN_INVALID_RESIZE = 'column_invalid_resize'; /** Relationship */ - public const RELATIONSHIP_VALUE_INVALID = 'relationship_value_invalid'; + public const string RELATIONSHIP_VALUE_INVALID = 'relationship_value_invalid'; /** Indexes */ - public const INDEX_NOT_FOUND = 'index_not_found'; - public const INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded'; - public const INDEX_ALREADY_EXISTS = 'index_already_exists'; - public const INDEX_INVALID = 'index_invalid'; - public const INDEX_DEPENDENCY = 'index_dependency'; + public const string INDEX_NOT_FOUND = 'index_not_found'; + public const string INDEX_LIMIT_EXCEEDED = 'index_limit_exceeded'; + public const string INDEX_ALREADY_EXISTS = 'index_already_exists'; + public const string INDEX_INVALID = 'index_invalid'; + public const string INDEX_DEPENDENCY = 'index_dependency'; /** Column Indexes */ - public const COLUMN_INDEX_NOT_FOUND = 'column_index_not_found'; - public const COLUMN_INDEX_LIMIT_EXCEEDED = 'column_index_limit_exceeded'; - public const COLUMN_INDEX_ALREADY_EXISTS = 'column_index_already_exists'; - public const COLUMN_INDEX_INVALID = 'column_index_invalid'; - public const COLUMN_INDEX_DEPENDENCY = 'column_index_dependency'; + public const string COLUMN_INDEX_NOT_FOUND = 'column_index_not_found'; + public const string COLUMN_INDEX_LIMIT_EXCEEDED = 'column_index_limit_exceeded'; + public const string COLUMN_INDEX_ALREADY_EXISTS = 'column_index_already_exists'; + public const string COLUMN_INDEX_INVALID = 'column_index_invalid'; + public const string COLUMN_INDEX_DEPENDENCY = 'column_index_dependency'; /** Transactions */ - public const TRANSACTION_NOT_FOUND = 'transaction_not_found'; - public const TRANSACTION_ALREADY_EXISTS = 'transaction_already_exists'; - public const TRANSACTION_INVALID = 'transaction_invalid'; - public const TRANSACTION_FAILED = 'transaction_expired'; - public const TRANSACTION_EXPIRED = 'transaction_expired'; - public const TRANSACTION_CONFLICT = 'transaction_conflict'; - public const TRANSACTION_LIMIT_EXCEEDED = 'transaction_limit_exceeded'; - public const TRANSACTION_NOT_READY = 'transaction_not_ready'; + public const string TRANSACTION_NOT_FOUND = 'transaction_not_found'; + public const string TRANSACTION_ALREADY_EXISTS = 'transaction_already_exists'; + public const string TRANSACTION_INVALID = 'transaction_invalid'; + public const string TRANSACTION_FAILED = 'transaction_failed'; + public const string TRANSACTION_EXPIRED = 'transaction_expired'; + public const string TRANSACTION_CONFLICT = 'transaction_conflict'; + public const string TRANSACTION_LIMIT_EXCEEDED = 'transaction_limit_exceeded'; + public const string TRANSACTION_NOT_READY = 'transaction_not_ready'; /** Projects */ - public const PROJECT_NOT_FOUND = 'project_not_found'; - public const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled'; - public const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported'; - public const PROJECT_ALREADY_EXISTS = 'project_already_exists'; - public const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url'; - public const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url'; - public const PROJECT_RESERVED_PROJECT = 'project_reserved_project'; - public const PROJECT_KEY_EXPIRED = 'project_key_expired'; + public const string PROJECT_NOT_FOUND = 'project_not_found'; + public const string PROJECT_PROVIDER_DISABLED = 'project_provider_disabled'; + public const string PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported'; + public const string PROJECT_ALREADY_EXISTS = 'project_already_exists'; + public const string PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url'; + public const string PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url'; + public const string PROJECT_RESERVED_PROJECT = 'project_reserved_project'; + public const string PROJECT_KEY_EXPIRED = 'project_key_expired'; - public const PROJECT_SMTP_CONFIG_INVALID = 'project_smtp_config_invalid'; + public const string PROJECT_SMTP_CONFIG_INVALID = 'project_smtp_config_invalid'; - public const PROJECT_TEMPLATE_DEFAULT_DELETION = 'project_template_default_deletion'; + public const string PROJECT_TEMPLATE_DEFAULT_DELETION = 'project_template_default_deletion'; - public const PROJECT_REGION_UNSUPPORTED = 'project_region_unsupported'; + public const string PROJECT_REGION_UNSUPPORTED = 'project_region_unsupported'; /** Webhooks */ - public const WEBHOOK_NOT_FOUND = 'webhook_not_found'; + public const string WEBHOOK_NOT_FOUND = 'webhook_not_found'; /** Router */ - public const ROUTER_HOST_NOT_FOUND = 'router_host_not_found'; - public const ROUTER_DOMAIN_NOT_CONFIGURED = 'router_domain_not_configured'; + public const string ROUTER_HOST_NOT_FOUND = 'router_host_not_found'; + public const string ROUTER_DOMAIN_NOT_CONFIGURED = 'router_domain_not_configured'; /** Proxy */ - public const RULE_RESOURCE_NOT_FOUND = 'rule_resource_not_found'; - public const RULE_NOT_FOUND = 'rule_not_found'; - public const RULE_ALREADY_EXISTS = 'rule_already_exists'; - public const RULE_VERIFICATION_FAILED = 'rule_verification_failed'; + public const string RULE_RESOURCE_NOT_FOUND = 'rule_resource_not_found'; + public const string RULE_NOT_FOUND = 'rule_not_found'; + public const string RULE_ALREADY_EXISTS = 'rule_already_exists'; + public const string RULE_VERIFICATION_FAILED = 'rule_verification_failed'; /** Keys */ - public const KEY_NOT_FOUND = 'key_not_found'; + public const string KEY_NOT_FOUND = 'key_not_found'; /** Variables */ - public const VARIABLE_NOT_FOUND = 'variable_not_found'; - public const VARIABLE_ALREADY_EXISTS = 'variable_already_exists'; - public const VARIABLE_CANNOT_UNSET_SECRET = 'variable_cannot_unset_secret'; + public const string VARIABLE_NOT_FOUND = 'variable_not_found'; + public const string VARIABLE_ALREADY_EXISTS = 'variable_already_exists'; + public const string VARIABLE_CANNOT_UNSET_SECRET = 'variable_cannot_unset_secret'; /** Platform */ - public const PLATFORM_NOT_FOUND = 'platform_not_found'; + public const string PLATFORM_NOT_FOUND = 'platform_not_found'; /** GraphqQL */ - public const GRAPHQL_NO_QUERY = 'graphql_no_query'; - public const GRAPHQL_TOO_MANY_QUERIES = 'graphql_too_many_queries'; + public const string GRAPHQL_NO_QUERY = 'graphql_no_query'; + public const string GRAPHQL_TOO_MANY_QUERIES = 'graphql_too_many_queries'; /** Migrations */ - public const MIGRATION_NOT_FOUND = 'migration_not_found'; - public const MIGRATION_ALREADY_EXISTS = 'migration_already_exists'; - public const MIGRATION_IN_PROGRESS = 'migration_in_progress'; - public const MIGRATION_PROVIDER_ERROR = 'migration_provider_error'; + public const string MIGRATION_NOT_FOUND = 'migration_not_found'; + public const string MIGRATION_ALREADY_EXISTS = 'migration_already_exists'; + public const string MIGRATION_IN_PROGRESS = 'migration_in_progress'; + public const string MIGRATION_PROVIDER_ERROR = 'migration_provider_error'; /** Realtime */ - public const REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid'; - public const REALTIME_TOO_MANY_MESSAGES = 'realtime_too_many_messages'; - public const REALTIME_POLICY_VIOLATION = 'realtime_policy_violation'; + public const string REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid'; + public const string REALTIME_TOO_MANY_MESSAGES = 'realtime_too_many_messages'; + public const string REALTIME_POLICY_VIOLATION = 'realtime_policy_violation'; /** Health */ - public const HEALTH_QUEUE_SIZE_EXCEEDED = 'health_queue_size_exceeded'; - public const HEALTH_CERTIFICATE_EXPIRED = 'health_certificate_expired'; - public const HEALTH_INVALID_HOST = 'health_invalid_host'; + public const string HEALTH_QUEUE_SIZE_EXCEEDED = 'health_queue_size_exceeded'; + public const string HEALTH_CERTIFICATE_EXPIRED = 'health_certificate_expired'; + public const string HEALTH_INVALID_HOST = 'health_invalid_host'; /** Provider */ - public const PROVIDER_NOT_FOUND = 'provider_not_found'; - public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists'; - public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type'; - public const PROVIDER_MISSING_CREDENTIALS = 'provider_missing_credentials'; + public const string PROVIDER_NOT_FOUND = 'provider_not_found'; + public const string PROVIDER_ALREADY_EXISTS = 'provider_already_exists'; + public const string PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type'; + public const string PROVIDER_MISSING_CREDENTIALS = 'provider_missing_credentials'; /** Topic */ - public const TOPIC_NOT_FOUND = 'topic_not_found'; - public const TOPIC_ALREADY_EXISTS = 'topic_already_exists'; + public const string TOPIC_NOT_FOUND = 'topic_not_found'; + public const string TOPIC_ALREADY_EXISTS = 'topic_already_exists'; /** Subscriber */ - public const SUBSCRIBER_NOT_FOUND = 'subscriber_not_found'; - public const SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists'; + public const string SUBSCRIBER_NOT_FOUND = 'subscriber_not_found'; + public const string SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists'; /** Message */ - public const MESSAGE_NOT_FOUND = 'message_not_found'; - public const MESSAGE_MISSING_TARGET = 'message_missing_target'; - public const MESSAGE_ALREADY_SENT = 'message_already_sent'; - public const MESSAGE_ALREADY_PROCESSING = 'message_already_processing'; - public const MESSAGE_ALREADY_FAILED = 'message_already_failed'; - public const MESSAGE_ALREADY_SCHEDULED = 'message_already_scheduled'; - public const MESSAGE_TARGET_NOT_EMAIL = 'message_target_not_email'; - public const MESSAGE_TARGET_NOT_SMS = 'message_target_not_sms'; - public const MESSAGE_TARGET_NOT_PUSH = 'message_target_not_push'; - public const MESSAGE_MISSING_SCHEDULE = 'message_missing_schedule'; + public const string MESSAGE_NOT_FOUND = 'message_not_found'; + public const string MESSAGE_MISSING_TARGET = 'message_missing_target'; + public const string MESSAGE_ALREADY_SENT = 'message_already_sent'; + public const string MESSAGE_ALREADY_PROCESSING = 'message_already_processing'; + public const string MESSAGE_ALREADY_FAILED = 'message_already_failed'; + public const string MESSAGE_ALREADY_SCHEDULED = 'message_already_scheduled'; + public const string MESSAGE_TARGET_NOT_EMAIL = 'message_target_not_email'; + public const string MESSAGE_TARGET_NOT_SMS = 'message_target_not_sms'; + public const string MESSAGE_TARGET_NOT_PUSH = 'message_target_not_push'; + public const string MESSAGE_MISSING_SCHEDULE = 'message_missing_schedule'; /** Targets */ - public const TARGET_PROVIDER_INVALID_TYPE = 'target_provider_invalid_type'; + public const string TARGET_PROVIDER_INVALID_TYPE = 'target_provider_invalid_type'; /** Schedules */ - public const SCHEDULE_NOT_FOUND = 'schedule_not_found'; + public const string SCHEDULE_NOT_FOUND = 'schedule_not_found'; /** Tokens */ - public const TOKEN_NOT_FOUND = 'token_not_found'; - public const TOKEN_EXPIRED = 'token_expired'; - public const TOKEN_RESOURCE_TYPE_INVALID = 'token_resource_type_invalid'; + public const string TOKEN_NOT_FOUND = 'token_not_found'; + public const string TOKEN_EXPIRED = 'token_expired'; + public const string TOKEN_RESOURCE_TYPE_INVALID = 'token_resource_type_invalid'; protected string $type = ''; protected array $errors = []; @@ -378,7 +378,13 @@ class Exception extends \Exception private array $ctas = []; private ?string $view = null; - public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int|string $code = null, \Throwable $previous = null, ?string $view = null) + public function __construct( + string $type = Exception::GENERAL_UNKNOWN, + string $message = null, + int|string $code = null, + \Throwable $previous = null, + ?string $view = null + ) { $this->errors = Config::getParam('errors'); $this->type = $type; @@ -388,7 +394,7 @@ class Exception extends \Exception // Mark string errors like HY001 from PDO as 500 errors if (\is_string($this->code)) { if (\is_numeric($this->code)) { - $this->code = (int) $this->code; + $this->code = (int)$this->code; } else { $this->code = 500; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 58ec470bd2..63f34241bd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -270,6 +270,12 @@ class Update extends Action ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); + } catch (NotFoundException $e) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } catch (DuplicateException|ConflictException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', From 5eb7c3a7c83e13ca5899fa494f94aac31fbb87d5 Mon Sep 17 00:00:00 2001 From: Veeresh <75656445+Veera-mulge@users.noreply.github.com> Date: Wed, 3 Sep 2025 19:18:25 +0530 Subject: [PATCH 072/274] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 375b1ad48c..e0b5c5ab4b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> We just announced Opt-in relationship loading for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-opt-in-relationship-loading) +> We just announced time helper queries for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-time-helper-queries) > Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga) From de5df4b72ec27a6113c2512dacc318d2d6bbb9f2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Sep 2025 03:57:03 +1200 Subject: [PATCH 073/274] Add extra tests --- app/config/errors.php | 2 +- composer.lock | 46 +- src/Appwrite/Extend/Exception.php | 3 +- .../Databases/Http/Transactions/Update.php | 25 +- .../{ACIDComplianceTest.php => ACIDTest.php} | 10 +- .../DatabasesTransactionsTest.php | 375 ----- .../Transactions/TransactionsTest.php | 1385 +++++++++++++++++ 7 files changed, 1427 insertions(+), 419 deletions(-) rename tests/e2e/Services/Databases/Transactions/{ACIDComplianceTest.php => ACIDTest.php} (99%) delete mode 100644 tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php create mode 100644 tests/e2e/Services/Databases/Transactions/TransactionsTest.php diff --git a/app/config/errors.php b/app/config/errors.php index 098c87c574..e76ca9ff68 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -989,7 +989,7 @@ return [ Exception::TRANSACTION_LIMIT_EXCEEDED => [ 'name' => Exception::TRANSACTION_LIMIT_EXCEEDED, 'description' => 'The maximum number of operations per transaction has been exceeded.', - 'code' => 422, + 'code' => 400, ], Exception::TRANSACTION_NOT_READY => [ 'name' => Exception::TRANSACTION_NOT_READY, diff --git a/composer.lock b/composer.lock index 90bb2f12f6..ed66af8572 100644 --- a/composer.lock +++ b/composer.lock @@ -1515,16 +1515,16 @@ }, { "name": "open-telemetry/sem-conv", - "version": "1.36.0", + "version": "1.37.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sem-conv.git", - "reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a" + "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/60dd18fd21d45e6f4234ecab89c14021b6e3de9a", - "reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a", + "url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/8da7ec497c881e39afa6657d72586e27efbd29a1", + "reference": "8da7ec497c881e39afa6657d72586e27efbd29a1", "shasum": "" }, "require": { @@ -1568,7 +1568,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-04T03:22:08+00:00" + "time": "2025-09-03T12:08:10+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -3638,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "1.2.4", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "87fb55e86892eecd726635eb1829acb743c2c156" + "reference": "fcd166b715a14cfea11f7a9c47d4c0076bedcecd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/87fb55e86892eecd726635eb1829acb743c2c156", - "reference": "87fb55e86892eecd726635eb1829acb743c2c156", + "url": "https://api.github.com/repos/utopia-php/database/zipball/fcd166b715a14cfea11f7a9c47d4c0076bedcecd", + "reference": "fcd166b715a14cfea11f7a9c47d4c0076bedcecd", "shasum": "" }, "require": { @@ -3688,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.2.4" + "source": "https://github.com/utopia-php/database/tree/1.3.1" }, - "time": "2025-09-01T06:01:09+00:00" + "time": "2025-09-03T15:50:41+00:00" }, { "name": "utopia-php/detector", @@ -3942,16 +3942,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.22", + "version": "0.33.23", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "c01a815cb976c9255e045fc3bcc3f5fcf477e0bc" + "reference": "88e8002365c10a727014ecc56322bcd1d780ceed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/c01a815cb976c9255e045fc3bcc3f5fcf477e0bc", - "reference": "c01a815cb976c9255e045fc3bcc3f5fcf477e0bc", + "url": "https://api.github.com/repos/utopia-php/http/zipball/88e8002365c10a727014ecc56322bcd1d780ceed", + "reference": "88e8002365c10a727014ecc56322bcd1d780ceed", "shasum": "" }, "require": { @@ -3983,9 +3983,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.22" + "source": "https://github.com/utopia-php/http/tree/0.33.23" }, - "time": "2025-08-26T10:29:50+00:00" + "time": "2025-09-03T11:58:14+00:00" }, { "name": "utopia-php/image", @@ -5007,16 +5007,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.1.15", + "version": "1.1.16", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "8e8e39634ba7558704522959d88f3542563a5444" + "reference": "f8fbc4b1ba0e918825338f50cbdea4d887389c41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/8e8e39634ba7558704522959d88f3542563a5444", - "reference": "8e8e39634ba7558704522959d88f3542563a5444", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/f8fbc4b1ba0e918825338f50cbdea4d887389c41", + "reference": "f8fbc4b1ba0e918825338f50cbdea4d887389c41", "shasum": "" }, "require": { @@ -5052,9 +5052,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.1.15" + "source": "https://github.com/appwrite/sdk-generator/tree/1.1.16" }, - "time": "2025-08-27T04:59:35+00:00" + "time": "2025-09-03T06:50:04+00:00" }, { "name": "doctrine/annotations", diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index cfa55c540e..2047cfc044 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -384,8 +384,7 @@ class Exception extends \Exception int|string $code = null, \Throwable $previous = null, ?string $view = null - ) - { + ) { $this->errors = Config::getParam('errors'); $this->type = $type; $this->view = $view; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 63f34241bd..9d4eae41c9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -14,6 +14,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Query; use Utopia\Database\Validator\UID; @@ -167,12 +168,17 @@ class Update extends Action break; case 'update': - $document = new Document($data); - $state[$collectionId][$documentId] = $dbForProject->updateDocument( + $document = $dbForProject->updateDocument( $collectionId, $documentId, - $document, + new Document($data), ); + + if ($document->isEmpty()) { + throw new ConflictException(''); + } + + $state[$collectionId][$documentId] = $document; break; case 'upsert': @@ -258,11 +264,7 @@ class Update extends Action $transaction = $dbForProject->updateDocument( 'transactions', $transactionId, - new Document( - [ - 'status' => 'committed', - ] - ) + new Document(['status' => 'committed']) ); // Clear the transaction logs @@ -270,23 +272,20 @@ class Update extends Action ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); - } catch (NotFoundException $e) { + } catch (NotFoundException) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ])); - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } catch (DuplicateException|ConflictException $e) { + } catch (DuplicateException|ConflictException) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ])); - throw new Exception(Exception::TRANSACTION_CONFLICT); } catch (TransactionException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ])); - throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); } }); diff --git a/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php b/tests/e2e/Services/Databases/Transactions/ACIDTest.php similarity index 99% rename from tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php rename to tests/e2e/Services/Databases/Transactions/ACIDTest.php index 42a2967685..7bf027b828 100644 --- a/tests/e2e/Services/Databases/Transactions/ACIDComplianceTest.php +++ b/tests/e2e/Services/Databases/Transactions/ACIDTest.php @@ -12,7 +12,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -class ACIDComplianceTest extends Scope +class ACIDTest extends Scope { use ProjectCustom; use SideClient; @@ -20,7 +20,7 @@ class ACIDComplianceTest extends Scope /** * Test atomicity - all operations succeed or all fail */ - public function testTransactionAtomicity(): void + public function testAtomicity(): void { // Create database $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ @@ -175,7 +175,7 @@ class ACIDComplianceTest extends Scope /** * Test consistency - schema validation and constraints */ - public function testTransactionConsistency(): void + public function testConsistency(): void { // Create database $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ @@ -306,7 +306,7 @@ class ACIDComplianceTest extends Scope /** * Test isolation - concurrent transactions on same data */ - public function testTransactionIsolation(): void + public function testIsolation(): void { // Create database $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ @@ -463,7 +463,7 @@ class ACIDComplianceTest extends Scope /** * Test durability - committed data persists */ - public function testTransactionDurability(): void + public function testDurability(): void { // Create database $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ diff --git a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php b/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php deleted file mode 100644 index 513051fcc0..0000000000 --- a/tests/e2e/Services/Databases/Transactions/DatabasesTransactionsTest.php +++ /dev/null @@ -1,375 +0,0 @@ -client->call(Client::METHOD_POST, '/databases', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'databaseId' => ID::unique(), - 'name' => 'TransactionTestDatabase' - ]); - - $this->assertEquals(201, $database['headers']['status-code']); - $databaseId = $database['body']['$id']; - - // Test creating a transaction with default TTL - $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); - - $this->assertEquals(201, $response['headers']['status-code']); - $this->assertArrayHasKey('$id', $response['body']); - $this->assertArrayHasKey('status', $response['body']); - $this->assertArrayHasKey('operations', $response['body']); - $this->assertArrayHasKey('expiresAt', $response['body']); - $this->assertEquals('pending', $response['body']['status']); - $this->assertEquals(0, $response['body']['operations']); - - $transactionId1 = $response['body']['$id']; - - // Test creating a transaction with custom TTL - $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'ttl' => 900 - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - $this->assertEquals('pending', $response['body']['status']); - - $expiresAt = new \DateTime($response['body']['expiresAt']); - $now = new \DateTime(); - $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); - $this->assertGreaterThan(800, $diff); - $this->assertLessThan(1000, $diff); - - $transactionId2 = $response['body']['$id']; - - // Test invalid TTL values - $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'ttl' => 30 // Below minimum - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - - $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'ttl' => 4000 // Above maximum - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - - return [ - 'databaseId' => $databaseId, - 'transactionId1' => $transactionId1, - 'transactionId2' => $transactionId2 - ]; - } - - /** - * @depends testCreate - */ - public function testAddOperations(array $data): array - { - $databaseId = $data['databaseId']; - $transactionId = $data['transactionId1']; - - // Create a collection for testing - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'collectionId' => ID::unique(), - 'name' => 'TransactionOperationsTest', - 'documentSecurity' => false, - 'permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - ]); - - $this->assertEquals(201, $collection['headers']['status-code']); - $collectionId = $collection['body']['$id']; - - // Add attributes - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'key' => 'name', - 'size' => 256, - 'required' => true, - ]); - - $this->assertEquals(202, $attribute['headers']['status-code']); - - // Wait for attribute to be created - sleep(2); - - // Add valid operations - $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'doc1', - 'data' => [ - 'name' => 'Test Document 1' - ] - ], - [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'action' => 'create', - 'documentId' => 'doc2', - 'data' => [ - 'name' => 'Test Document 2' - ] - ] - ] - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - $this->assertEquals(2, $response['body']['operations']); - - // Test adding more operations - $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'update', - 'documentId' => 'doc1', - 'data' => [ - 'name' => 'Updated Document 1' - ] - ] - ] - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - $this->assertEquals(3, $response['body']['operations']); - - // Test invalid database ID - $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'operations' => [ - [ - 'databaseId' => 'invalid_database', - 'collectionId' => $collectionId, - 'action' => 'create', - 'documentId' => ID::unique(), - 'data' => ['name' => 'Test'] - ] - ] - ]); - - $this->assertEquals(404, $response['headers']['status-code'], 'Invalid database should return 404. Got: ' . json_encode($response['body'])); - - // Test invalid collection ID - $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'invalid_collection', - 'action' => 'create', - 'documentId' => ID::unique(), - 'data' => ['name' => 'Test'] - ] - ] - ]); - - $this->assertEquals(404, $response['headers']['status-code']); - - return array_merge($data, [ - 'collectionId' => $collectionId - ]); - } - - /** - * @depends testAddOperations - */ - public function testCommit(array $data): void - { - $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; - $transactionId = $data['transactionId1']; - - // Commit the transaction - $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); - $this->assertEquals('committed', $response['body']['status']); - - // Verify documents were created - $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2, $documents['body']['total']); - - // Verify the update was applied - $doc1Found = false; - foreach ($documents['body']['documents'] as $doc) { - if ($doc['$id'] === 'doc1') { - $this->assertEquals('Updated Document 1', $doc['name']); - $doc1Found = true; - } - } - $this->assertTrue($doc1Found, 'Document doc1 should exist with updated name'); - - // Test committing already committed transaction - $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'commit' => true - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - } - - /** - * @depends testCreate - */ - public function testRollback(array $data): void - { - $databaseId = $data['databaseId']; - $transactionId = $data['transactionId2']; - - // Create a collection for rollback test - $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'collectionId' => ID::unique(), - 'name' => 'TransactionRollbackTest', - 'documentSecurity' => false, - 'permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - ]); - - $collectionId = $collection['body']['$id']; - - // Add attribute - $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'key' => 'value', - 'size' => 256, - 'required' => true, - ]); - - sleep(2); - - // Add operations - $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'rollback_doc', - 'data' => [ - 'value' => 'Should not exist' - ] - ] - ] - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - - // Rollback the transaction - $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'rollback' => true - ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('rolledBack', $response['body']['status']); - - // Verify no documents were created - $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(0, $documents['body']['total']); - } -} diff --git a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php new file mode 100644 index 0000000000..cb0f3f3827 --- /dev/null +++ b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php @@ -0,0 +1,1385 @@ +client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test creating a transaction with default TTL + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertArrayHasKey('$id', $response['body']); + $this->assertArrayHasKey('status', $response['body']); + $this->assertArrayHasKey('operations', $response['body']); + $this->assertArrayHasKey('expiresAt', $response['body']); + $this->assertEquals('pending', $response['body']['status']); + $this->assertEquals(0, $response['body']['operations']); + + $transactionId1 = $response['body']['$id']; + + // Test creating a transaction with custom TTL + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'ttl' => 900 + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals('pending', $response['body']['status']); + + $expiresAt = new \DateTime($response['body']['expiresAt']); + $now = new \DateTime(); + $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); + $this->assertGreaterThan(800, $diff); + $this->assertLessThan(1000, $diff); + + $transactionId2 = $response['body']['$id']; + + // Test invalid TTL values + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'ttl' => 30 // Below minimum + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'ttl' => 4000 // Above maximum + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + return [ + 'databaseId' => $databaseId, + 'transactionId1' => $transactionId1, + 'transactionId2' => $transactionId2 + ]; + } + + /** + * @depends testCreate + */ + public function testAddOperations(array $data): array + { + $databaseId = $data['databaseId']; + $transactionId = $data['transactionId1']; + + // Create a collection for testing + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionOperationsTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $collectionId = $collection['body']['$id']; + + // Add attributes + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + // Wait for attribute to be created + sleep(2); + + // Add valid operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'doc1', + 'data' => [ + 'name' => 'Test Document 1' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc2', + 'data' => [ + 'name' => 'Test Document 2' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['operations']); + + // Test adding more operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'update', + 'documentId' => 'doc1', + 'data' => [ + 'name' => 'Updated Document 1' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(3, $response['body']['operations']); + + // Test invalid database ID + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'databaseId' => 'invalid_database', + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code'], 'Invalid database should return 404. Got: ' . json_encode($response['body'])); + + // Test invalid collection ID + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'invalid_collection', + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + return array_merge($data, [ + 'collectionId' => $collectionId + ]); + } + + /** + * @depends testAddOperations + */ + public function testCommit(array $data): void + { + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + $transactionId = $data['transactionId1']; + + // Commit the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + $this->assertEquals('committed', $response['body']['status']); + + // Verify documents were created + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(2, $documents['body']['total']); + + // Verify the update was applied + $doc1Found = false; + foreach ($documents['body']['documents'] as $doc) { + if ($doc['$id'] === 'doc1') { + $this->assertEquals('Updated Document 1', $doc['name']); + $doc1Found = true; + } + } + $this->assertTrue($doc1Found, 'Document doc1 should exist with updated name'); + + // Test committing already committed transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * @depends testCreate + */ + public function testRollback(array $data): void + { + $databaseId = $data['databaseId']; + $transactionId = $data['transactionId2']; + + // Create a collection for rollback test + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionRollbackTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'value', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // Add operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'rollback_doc', + 'data' => [ + 'value' => 'Should not exist' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Rollback the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rollback' => true + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('rolledBack', $response['body']['status']); + + // Verify no documents were created + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(0, $documents['body']['total']); + } + + /** + * Test transaction expiration + */ + public function testTransactionExpiration(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'ExpirationTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'data', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // Create transaction with minimum TTL (60 seconds) + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'ttl' => 60 + ]); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Add operation + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => ['data' => 'Should expire'] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Verify transaction was created with correct expiration + $txnDetails = $this->client->call(Client::METHOD_GET, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $txnDetails['headers']['status-code']); + $this->assertEquals('pending', $txnDetails['body']['status']); + + // Verify expiration time is approximately 60 seconds from now + $expiresAt = new \DateTime($txnDetails['body']['expiresAt']); + $now = new \DateTime(); + $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); + $this->assertGreaterThan(55, $diff); + $this->assertLessThan(65, $diff); + } + + /** + * Test maximum operations per transaction + */ + public function testTransactionSizeLimit(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'SizeLimitTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [Permission::create(Role::any())], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'value', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Try to add operations exceeding the limit (assuming limit is 100) + // We'll add 50 operations twice to test incremental limit + $operations = []; + for ($i = 0; $i < 50; $i++) { + $operations[] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc_' . $i, + 'data' => ['value' => 'Test ' . $i] + ]; + } + + // First batch should succeed + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => $operations + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(50, $response['body']['operations']); + + // Second batch of 50 more operations + $operations = []; + for ($i = 50; $i < 100; $i++) { + $operations[] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => 'doc_' . $i, + 'action' => 'create', + 'data' => ['value' => 'Test ' . $i] + ]; + } + + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => $operations + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(100, $response['body']['operations']); + + // Try to add one more operation - should fail + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'doc_overflow', + 'data' => ['value' => 'This should fail'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * Test concurrent transactions with conflicting operations + */ + public function testConcurrentTransactionConflicts(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'ConflictTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/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 + $doc = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'shared_doc', + 'data' => ['counter' => 100] + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + + // Create two transactions + $txn1 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $txn2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId1 = $txn1['body']['$id']; + $transactionId2 = $txn2['body']['$id']; + + // Both transactions try to update the same document + $this->client->call(Client::METHOD_POST, "/databases/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, + 'action' => 'update', + 'documentId' => 'shared_doc', + 'data' => ['counter' => 200] + ] + ] + ]); + + $this->client->call(Client::METHOD_POST, "/databases/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, + 'action' => 'update', + 'documentId' => 'shared_doc', + 'data' => ['counter' => 300] + ] + ] + ]); + + // Commit first transaction + $response1 = $this->client->call(Client::METHOD_PATCH, "/databases/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 - should fail with conflict + $response2 = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId2}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(409, $response2['headers']['status-code']); // Conflict + + // Verify the document has the value from first transaction + $doc = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/shared_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $doc['body']['counter']); + } + + /** + * Test deleting a document that's being updated in a transaction + */ + public function testDeleteDocumentDuringTransaction(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'DeleteConflictDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'data', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // Create document + $doc = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'target_doc', + 'data' => ['data' => 'Original'] + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add update operation to transaction + $this->client->call(Client::METHOD_POST, "/databases/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' => 'update', + 'documentId' => 'target_doc', + 'data' => ['data' => 'Updated in transaction'] + ] + ] + ]); + + // Delete the document outside of transaction + $response = $this->client->call(Client::METHOD_DELETE, "/databases/{$databaseId}/collections/{$collectionId}/documents/target_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Try to commit transaction - should fail because document no longer exists + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(409, $response['headers']['status-code']); // Conflict + } + + /** + * Test bulk operations in transactions + */ + public function testBulkOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkOpsDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); + + // Create some initial documents + for ($i = 1; $i <= 5; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_' . $i, + 'data' => [ + 'name' => 'Existing ' . $i, + 'category' => 'old' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add bulk operations + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + // Bulk create + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkCreate', + 'data' => [ + ['$id' => 'bulk_1', 'name' => 'Bulk 1', 'category' => 'new'], + ['$id' => 'bulk_2', 'name' => 'Bulk 2', 'category' => 'new'], + ['$id' => 'bulk_3', 'name' => 'Bulk 3', 'category' => 'new'], + ] + ], + // Bulk update + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkUpdate', + 'data' => [ + 'queries' => [Query::equal('category', ['old'])->toString()], + 'data' => ['category' => 'updated'] + ] + ], + // Bulk delete + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => [Query::equal('name', ['Existing 5'])->toString()] + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 results + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Should have 7 documents (5 existing - 1 deleted + 3 new) + $this->assertEquals(7, $documents['body']['total']); + + // Check categories were updated + $oldCategoryCount = 0; + $updatedCategoryCount = 0; + $newCategoryCount = 0; + + foreach ($documents['body']['documents'] as $doc) { + switch ($doc['category']) { + case 'old': + $oldCategoryCount++; + break; + case 'updated': + $updatedCategoryCount++; + break; + case 'new': + $newCategoryCount++; + break; + } + } + + $this->assertEquals(0, $oldCategoryCount); + $this->assertEquals(4, $updatedCategoryCount); // 4 existing docs updated + $this->assertEquals(3, $newCategoryCount); // 3 new docs + } + + /** + * Test transaction with mixed success and failure operations + */ + public function testPartialFailureRollback(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'PartialFailureDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes with constraints + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/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, + ]); + + // Create unique index on email + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'unique_email', + 'type' => 'unique', + 'attributes' => ['email'], + ]); + + sleep(3); + + // Create an existing document + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => ID::unique(), + 'data' => ['email' => 'existing@example.com'] + ]); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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 - mix of valid and invalid + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'valid1@example.com'] // Valid + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['email' => 'valid2@example.com'] // Valid + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['email' => 'existing@example.com'] // Will fail - duplicate + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['email' => 'valid3@example.com'] // Would be valid but should rollback + ], + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Try to commit - should fail and rollback all operations + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(409, $response['headers']['status-code']); // Conflict due to duplicate + + // Verify NO new documents were created (atomicity) + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", 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 double commit/rollback attempts + */ + public function testDoubleCommitRollback(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'DoubleCommitDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [Permission::create(Role::any())], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'data', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // Test double commit + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add operation + $this->client->call(Client::METHOD_POST, "/databases/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' => ['data' => 'Test'] + ] + ] + ]); + + // First commit + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Second commit attempt - should fail + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); // Bad request - already committed + + // Test double rollback + $transaction2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId2 = $transaction2['body']['$id']; + + // First rollback + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId2}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rollback' => true + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Second rollback attempt - should fail + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId2}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rollback' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); // Bad request - already rolled back + } + + /** + * Test operations on non-existent documents + */ + public function testOperationsOnNonExistentDocuments(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'NonExistentDocDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'data', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Try to update non-existent document + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'update', + 'documentId' => 'non_existent_doc', + 'data' => ['data' => 'Should fail'] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); // Operation added + + // Commit should fail + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(404, $response['headers']['status-code']); // Document not found + + // Test delete non-existent document + $transaction2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId2 = $transaction2['body']['$id']; + + $response = $this->client->call(Client::METHOD_POST, "/databases/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, + 'action' => 'delete', + 'documentId' => 'non_existent_doc', + 'data' => [] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit should fail + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId2}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(404, $response['headers']['status-code']); // Document not found + } +} From ba97cfb440332d8f735d8cb373a86f0d787893db Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:59:20 +0000 Subject: [PATCH 074/274] chore: create workflow to auto add labels to issues --- .github/labeler.yml | 83 ++++++++++++++++++++++++++ .github/workflows/auto-label-issue.yml | 22 +++++++ 2 files changed, 105 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/auto-label-issue.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..fb46eb5ba1 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,83 @@ +# Fixes and upgrades for the Appwrite Auth / Users / Teams services. +"product / auth": + - "(auth|session|login|logout|register|2fa|mfa|users|teams|memberships|invite|oauth|oauth2|sso|jwt)" + +# Fixes and upgrades for the Appwrite Realtime API. +"api / realtime": + - "(realtime|subscribe|websockets)" + +# Console, UI and UX issues +"product / console": + - "(console)" + +# Fixes and upgrades for the Appwrite Storage. +"product / storage": + - "(storage|bucket|file|image|preview|download)" + +# Fixes and upgrades for the Appwrite Database. +"product / databases": + - "(database|collection|tables|attribute|column|document|row|query|queries|indexes|search|filter|sort|pagination)" + +# Fixes and upgrades for the Appwrite Functions. +"product / functions": + - "(function|runtime|deployment|execution|trigger|cron|schedule)" + +# Fixes and upgrades for the Appwrite Docs. +# "product / docs": +# - + +# Fixes and upgrades for the Appwrite Migrations. +"product / migrations": + - "(migrate|migration)" + +# Fixes and upgrades for the Appwrite Messaging. +"product / messaging": + - "(messaging|email|sms|push|provider|topic|target|notification)" + +# Fixes and upgrades for the Appwrite Platform. +# "product / platform": +# - + +# Fixes and upgrades for database relationships +"feature / relationships": + - "(relationship)" + +# Issues found only on Appwrite Cloud +# "product / cloud": +# - + +# Fixes and upgrades for the Appwrite VCS. +"product / vcs": + - "(repo|push|vcs|repository)" + +# Fixes and upgrades for the Appwrite GraphQL API. +"api / graphql": + - "(graphql|gql|mutation)" + +# Fixes and upgrades for the Appwrite Assistant. +"product / assistant": + - "(assistant)" + +# Fixes and upgrades for the Appwrite Domains. +"product / domains": + - "(domain|dns|ssl|certificate)" + +# Fixes and upgrades for the Appwrite Locale. +"product / locale": + - "(locale|i18n|internationalization|localization|l10n|translation|timezone|country)" + +# Fixes and upgrades for the Appwrite Avatars. +"product / avatars": + - "(avatar|initial|flag|icon)" + +# Fixes and upgrades for Appwrite Sites. +"product / sites": + - "(site|web|hosting|domain|ssl|certificate|nextjs|nuxt|react|angular|vue|svelte|astro)" + +# Fixes and upgrades for the Appwrite CLI. +"sdk / cli": + - "(cli|command line)" + +# Issues only found when self-hosting Appwrite +"product / self-hosted": + - "(self-host|self host)" diff --git a/.github/workflows/auto-label-issue.yml b/.github/workflows/auto-label-issue.yml new file mode 100644 index 0000000000..e0eb0de98d --- /dev/null +++ b/.github/workflows/auto-label-issue.yml @@ -0,0 +1,22 @@ +name: Auto Label Issue + +on: + issues: + types: [opened] + +permissions: + issues: write + contents: read + +jobs: + labeler: + runs-on: ubuntu-latest + steps: + - name: Issue Labeler + uses: github/issue-labeler@v3.4 + with: + configuration-path: .github/labeler.yml + enable-versioned-regex: false + include-title: 1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 7eba6582c6128040cb1a5674ec4f09c486610790 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Sep 2025 20:32:37 +1200 Subject: [PATCH 075/274] Fix bulk payloads --- .../Databases/Http/Transactions/Update.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index 9d4eae41c9..b8b77d79ef 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -223,10 +223,9 @@ class Update extends Action case 'bulkCreate': $dbForProject->createDocuments( $collectionId, - array_map(fn ($data) => new Document($data), $data), + $data, onNext: function (Document $document) use (&$state, $collectionId) { $state[$collectionId][$document->getId()] = $document; - } ); break; @@ -234,7 +233,7 @@ class Update extends Action case 'bulkUpdate': $dbForProject->updateDocuments( $collectionId, - $data['data'] ?? null, + new Document($data['data']), Query::parseQueries($data['queries'] ?? []) ); break; @@ -242,10 +241,9 @@ class Update extends Action case 'bulkUpsert': $dbForProject->createOrUpdateDocuments( $collectionId, - array_map(fn ($data) => new Document($data), $data), + $data, onNext: function (Document $document) use (&$state, $collectionId) { $state[$collectionId][$document->getId()] = $document; - } ); break; @@ -272,16 +270,16 @@ class Update extends Action ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); - } catch (NotFoundException) { + } catch (NotFoundException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ])); - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } catch (DuplicateException|ConflictException) { + throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); + } catch (DuplicateException|ConflictException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ])); - throw new Exception(Exception::TRANSACTION_CONFLICT); + throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); } catch (TransactionException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', From d28643bd2bdc0f9dfaed5985f1c7ba83b37540d5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 5 Sep 2025 01:02:30 +1200 Subject: [PATCH 076/274] Update DB --- composer.json | 2 +- composer.lock | 68 +-- .../Collections/Documents/Bulk/Upsert.php | 2 +- .../Collections/Documents/Upsert.php | 2 +- .../Databases/Http/Transactions/Update.php | 539 +++++++++++++----- .../Platform/Workers/StatsResources.php | 2 +- src/Appwrite/Platform/Workers/StatsUsage.php | 4 +- .../Transactions/TransactionsTest.php | 4 +- 8 files changed, 433 insertions(+), 190 deletions(-) diff --git a/composer.json b/composer.json index 0c662c775f..7d9176d2aa 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "1.*", + "utopia-php/database": "2.*", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", "utopia-php/dns": "0.3.*", diff --git a/composer.lock b/composer.lock index ed66af8572..bf76f13a3e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0da713ee5642eba1d30bc51c1a04a723", + "content-hash": "3565fcc2471b5d18a159b6da1c8fad31", "packages": [ { "name": "adhocore/jwt", @@ -3296,16 +3296,16 @@ }, { "name": "utopia-php/abuse", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be" + "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/c5e2232033b507a07f72180dc56d37e1872ee7be", - "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd591568791556d246d901d6aaf9935ab02c3f9a", + "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a", "shasum": "" }, "require": { @@ -3313,7 +3313,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "1.*" + "utopia-php/database": "2.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3341,9 +3341,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/1.0.0" + "source": "https://github.com/utopia-php/abuse/tree/1.0.1" }, - "time": "2025-08-13T09:12:54+00:00" + "time": "2025-09-04T12:46:54+00:00" }, { "name": "utopia-php/analytics", @@ -3393,21 +3393,21 @@ }, { "name": "utopia-php/audit", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb" + "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c0ed75f4d068f1f6c2e7149a909490d4214e72bb", - "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", + "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "1.*" + "utopia-php/database": "2.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3434,9 +3434,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/1.0.0" + "source": "https://github.com/utopia-php/audit/tree/1.0.1" }, - "time": "2025-08-13T09:09:00+00:00" + "time": "2025-09-04T12:46:43+00:00" }, { "name": "utopia-php/cache", @@ -3638,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "1.3.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "fcd166b715a14cfea11f7a9c47d4c0076bedcecd" + "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/fcd166b715a14cfea11f7a9c47d4c0076bedcecd", - "reference": "fcd166b715a14cfea11f7a9c47d4c0076bedcecd", + "url": "https://api.github.com/repos/utopia-php/database/zipball/e4a03ba543abc4e436ec1b450750a14bd36011d5", + "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5", "shasum": "" }, "require": { @@ -3688,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.3.1" + "source": "https://github.com/utopia-php/database/tree/2.0.0" }, - "time": "2025-09-03T15:50:41+00:00" + "time": "2025-09-04T12:36:53+00:00" }, { "name": "utopia-php/detector", @@ -3942,16 +3942,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.23", + "version": "0.33.24", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "88e8002365c10a727014ecc56322bcd1d780ceed" + "reference": "5112b1023342163e3fbedec99f38fc32c8700aa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/88e8002365c10a727014ecc56322bcd1d780ceed", - "reference": "88e8002365c10a727014ecc56322bcd1d780ceed", + "url": "https://api.github.com/repos/utopia-php/http/zipball/5112b1023342163e3fbedec99f38fc32c8700aa0", + "reference": "5112b1023342163e3fbedec99f38fc32c8700aa0", "shasum": "" }, "require": { @@ -3983,9 +3983,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.23" + "source": "https://github.com/utopia-php/http/tree/0.33.24" }, - "time": "2025-09-03T11:58:14+00:00" + "time": "2025-09-04T04:18:39+00:00" }, { "name": "utopia-php/image", @@ -4190,16 +4190,16 @@ }, { "name": "utopia-php/migration", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "38171023efd3abe650d2abc5ac65f5df52311da6" + "reference": "eb60a61934be1d6f2f4fdabd9903a841ba078bc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/38171023efd3abe650d2abc5ac65f5df52311da6", - "reference": "38171023efd3abe650d2abc5ac65f5df52311da6", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/eb60a61934be1d6f2f4fdabd9903a841ba078bc9", + "reference": "eb60a61934be1d6f2f4fdabd9903a841ba078bc9", "shasum": "" }, "require": { @@ -4207,7 +4207,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "1.*", + "utopia-php/database": "2.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4240,9 +4240,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.0.1" + "source": "https://github.com/utopia-php/migration/tree/1.0.2" }, - "time": "2025-08-28T13:41:25+00:00" + "time": "2025-09-04T12:47:05+00:00" }, { "name": "utopia-php/orchestration", diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 2debdfcf6d..618d640d95 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -161,7 +161,7 @@ class Upsert extends Action try { $modified = $dbForProject->withPreserveDates(function () use ($dbForProject, $database, $collection, $documents, $plan, &$upserted) { - return $dbForProject->createOrUpdateDocuments( + return $dbForProject->upsertDocuments( 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documents, onNext: function (Document $document) use ($plan, &$upserted) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index c117691348..7a6c7e960b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -299,7 +299,7 @@ class Upsert extends Action $upserted = []; try { $dbForProject->withPreserveDates(function () use (&$upserted, $dbForProject, $database, $collection, $newDocument) { - return $dbForProject->createOrUpdateDocuments( + return $dbForProject->upsertDocuments( 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), [$newDocument], onNext: function (Document $document) use (&$upserted) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index b8b77d79ef..ed277e7b2f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -12,9 +12,11 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Structure; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Query; use Utopia\Database\Validator\UID; @@ -65,6 +67,23 @@ class Update extends Action ->callback($this->action(...)); } + /** + * @param string $transactionId + * @param bool $commit + * @param bool $rollback + * @param UtopiaResponse $response + * @param Database $dbForProject + * @param Delete $queueForDeletes + * @return void + * @throws ConflictException + * @throws Exception + * @throws \DateMalformedStringException + * @throws \Throwable + * @throws \Utopia\Database\Exception + * @throws Authorization + * @throws Structure + * @throws \Utopia\Exception + */ public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Delete $queueForDeletes): void { if (!$commit && !$rollback) { @@ -113,149 +132,38 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; - // Check if this operation depends on documents created in same transaction - $dependent = \in_array($action, ['update', 'increment', 'decrement']) - && isset($state[$collectionId][$documentId]); - - if ($dependent) { - // Don't use timestamp wrapper for dependent operations - switch ($action) { - case 'update': - // Update the state document directly - $existing = $state[$collectionId][$documentId]; - foreach ($data as $key => $value) { - $existing->setAttribute($key, $value); - } - $state[$collectionId][$documentId] = $dbForProject->updateDocument( - $collectionId, - $documentId, - $existing - ); - break; - - case 'increment': - $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( - collection: $collectionId, - id: $documentId, - attribute: $data['attribute'], - value: $data['value'] ?? 1, - max: $data['max'] ?? null - ); - break; - - case 'decrement': - $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( - collection: $collectionId, - id: $documentId, - attribute: $data['attribute'], - value: $data['value'] ?? 1, - min: $data['min'] ?? null - ); - break; - } - } else { - // Use timestamp wrapper for independent operations - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $queueForDeletes, $action, $collectionId, $documentId, $data, &$state) { - switch ($action) { - case 'create': - if ($documentId && !isset($data['$id'])) { - $data['$id'] = $documentId; - } - $state[$collectionId][$documentId] = $dbForProject->createDocument( - $collectionId, - new Document($data), - ); - break; - - case 'update': - $document = $dbForProject->updateDocument( - $collectionId, - $documentId, - new Document($data), - ); - - if ($document->isEmpty()) { - throw new ConflictException(''); - } - - $state[$collectionId][$documentId] = $document; - break; - - case 'upsert': - $document = new Document($data); - $dbForProject->createOrUpdateDocuments( - $collectionId, - [$document], - onNext: function (Document $document) use (&$state, $collectionId) { - $state[$collectionId][$document->getId()] = $document; - } - ); - break; - - case 'delete': - $dbForProject->deleteDocument($collectionId, $documentId); - - if (isset($state[$collectionId][$documentId])) { - unset($state[$collectionId][$documentId]); - } - break; - - case 'increment': - $dbForProject->increaseDocumentAttribute( - collection: $collectionId, - id: $documentId, - attribute: $data['attribute'], - value: $data['value'] ?? 1, - max: $data['max'] ?? null - ); - break; - - case 'decrement': - $dbForProject->decreaseDocumentAttribute( - collection: $collectionId, - id: $documentId, - attribute: $data['attribute'], - value: $data['value'] ?? 1, - min: $data['min'] ?? null - ); - break; - - case 'bulkCreate': - $dbForProject->createDocuments( - $collectionId, - $data, - onNext: function (Document $document) use (&$state, $collectionId) { - $state[$collectionId][$document->getId()] = $document; - } - ); - break; - - case 'bulkUpdate': - $dbForProject->updateDocuments( - $collectionId, - new Document($data['data']), - Query::parseQueries($data['queries'] ?? []) - ); - break; - - case 'bulkUpsert': - $dbForProject->createOrUpdateDocuments( - $collectionId, - $data, - onNext: function (Document $document) use (&$state, $collectionId) { - $state[$collectionId][$document->getId()] = $document; - } - ); - break; - - case 'bulkDelete': - $dbForProject->deleteDocuments( - $collectionId, - Query::parseQueries($data['queries'] ?? []) - ); - break; - } - }); + // Execute the operation based on its type + switch ($action) { + case 'create': + $this->handleCreateOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); + break; + case 'update': + $this->handleUpdateOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); + break; + case 'upsert': + $this->handleUpsertOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); + break; + case 'delete': + $this->handleDeleteOperation($dbForProject, $collectionId, $documentId, $createdAt, $state); + break; + case 'increment': + $this->handleIncrementOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); + break; + case 'decrement': + $this->handleDecrementOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); + break; + case 'bulkCreate': + $this->handleBulkCreateOperation($dbForProject, $collectionId, $data, $createdAt, $state); + break; + case 'bulkUpdate': + $this->handleBulkUpdateOperation($dbForProject, $collectionId, $data, $createdAt, $state); + break; + case 'bulkUpsert': + $this->handleBulkUpsertOperation($dbForProject, $collectionId, $data, $createdAt, $state); + break; + case 'bulkDelete': + $this->handleBulkDeleteOperation($dbForProject, $collectionId, $data, $createdAt, $state); + break; } } @@ -269,7 +177,6 @@ class Update extends Action $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); - } catch (NotFoundException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', @@ -290,17 +197,351 @@ class Update extends Action } if ($rollback) { - $queueForDeletes - ->setType(DELETE_TYPE_DOCUMENT) - ->setDocument($transaction); - $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'rolledBack', ])); + + $queueForDeletes + ->setType(DELETE_TYPE_DOCUMENT) + ->setDocument($transaction); } $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) ->dynamic($transaction, UtopiaResponse::MODEL_TRANSACTION); } -} + + /** + * Handle create operation + * @throws \Utopia\Database\Exception + */ + private function handleCreateOperation( + Database $dbForProject, + string $collectionId, + ?string $documentId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + if ($documentId && !isset($data['$id'])) { + $data['$id'] = $documentId; + } + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { + $state[$collectionId][$data['$id']] = $dbForProject->createDocument( + $collectionId, + new Document($data), + ); + }); + } + + /** + * Handle update operation + * @throws ConflictException + * @throws \Utopia\Database\Exception + */ + private function handleUpdateOperation( + Database $dbForProject, + string $collectionId, + string $documentId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $dependent = isset($state[$collectionId][$documentId]); + + if ($dependent) { + // Update the state document directly without timestamp wrapper + $state[$collectionId][$documentId] = $dbForProject->updateDocument( + $collectionId, + $documentId, + new Document($data), + ); + return; + } + + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { + $document = $dbForProject->updateDocument( + $collectionId, + $documentId, + new Document($data), + ); + if ($document->isEmpty()) { + throw new ConflictException(''); + } + $state[$collectionId][$documentId] = $document; + }); + } + + /** + * Handle upsert operation + * @throws \Utopia\Database\Exception + */ + private function handleUpsertOperation( + Database $dbForProject, + string $collectionId, + ?string $documentId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $dependent = isset($state[$collectionId][$documentId]); + + if ($dependent) { + // Upsert the state document directly without timestamp wrapper + $state[$collectionId][$documentId] = $dbForProject->upsertDocument( + $collectionId, + new Document($data), + ); + return; + } + + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { + $state[$collectionId][$documentId] = $dbForProject->upsertDocument( + $collectionId, + new Document($data), + ); + }); + } + + /** + * Handle delete operation + */ + private function handleDeleteOperation( + Database $dbForProject, + string $collectionId, + string $documentId, + \DateTime $createdAt, + array &$state + ): void + { + $dependent = isset($state[$collectionId][$documentId]); + + if ($dependent) { + // Delete without timestamp wrapper + $dbForProject->deleteDocument($collectionId, $documentId); + unset($state[$collectionId][$documentId]); + return; + } + + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, &$state) { + $dbForProject->deleteDocument($collectionId, $documentId); + if (isset($state[$collectionId][$documentId])) { + unset($state[$collectionId][$documentId]); + } + }); + } + + /** + * Handle increment operation + */ + private function handleIncrementOperation( + Database $dbForProject, + string $collectionId, + string $documentId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $dependent = isset($state[$collectionId][$documentId]); + + if ($dependent) { + // Increment without timestamp wrapper + $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( + collection: $collectionId, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + max: $data['max'] ?? null + ); + return; + } + + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data) { + $dbForProject->increaseDocumentAttribute( + collection: $collectionId, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + max: $data['max'] ?? null + ); + }); + } + + /** + * Handle decrement operation + */ + private function handleDecrementOperation( + Database $dbForProject, + string $collectionId, + string $documentId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $dependent = isset($state[$collectionId][$documentId]); + + if ($dependent) { + // Decrement without timestamp wrapper + $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( + collection: $collectionId, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + min: $data['min'] ?? null + ); + return; + } + + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data) { + $dbForProject->decreaseDocumentAttribute( + collection: $collectionId, + id: $documentId, + attribute: $data['attribute'], + value: $data['value'] ?? 1, + min: $data['min'] ?? null + ); + }); + } + + /** + * Handle bulk create operation + */ + private function handleBulkCreateOperation( + Database $dbForProject, + string $collectionId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { + $dbForProject->createDocuments( + $collectionId, + $data, + onNext: function (Document $document) use (&$state, $collectionId) { + $state[$collectionId][$document->getId()] = $document; + } + ); + }); + } + + /** + * Handle bulk update operation with manual timestamp checking + * @throws \Utopia\Database\Exception + * @throws \Utopia\Database\Exception\Query + * @throws ConflictException + */ + private function handleBulkUpdateOperation( + Database $dbForProject, + string $collectionId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $queries = Query::parseQueries($data['queries'] ?? []); + + $dbForProject->updateDocuments( + $collectionId, + new Document($data['data']), + $queries, + onNext: function (Document $updated, Document $old) use (&$state, $collectionId, $createdAt) { + // Check if this document was created/modified in this transaction + $dependent = isset($state[$collectionId][$updated->getId()]); + + // If not in transaction state, check for timestamp conflicts + if (!$dependent) { + $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); + if ($oldUpdatedAt > $createdAt) { + throw new ConflictException('Document was updated after the request timestamp'); + } + } + + $state[$collectionId][$updated->getId()] = $updated; + } + ); + } + + /** + * Handle bulk upsert operation with manual timestamp checking + * @throws ConflictException + */ + private function handleBulkUpsertOperation( + Database $dbForProject, + string $collectionId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + // Run bulk upsert without timestamp wrapper, checking manually in callback + $dbForProject->upsertDocuments( + $collectionId, + $data, + onNext: function (Document $upserted, ?Document $old) use (&$state, $collectionId, $createdAt) { + if ($old !== null) { + // This is an update - check if document was created/modified in this transaction + $dependent = isset($state[$collectionId][$upserted->getId()]); + + // If not in transaction state, check for timestamp conflicts + if (!$dependent) { + $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); + if ($oldUpdatedAt > $createdAt) { + throw new ConflictException('Document was updated after the request timestamp'); + } + } + } + + // If $old is null, this is a create operation - no timestamp check needed + $state[$collectionId][$upserted->getId()] = $upserted; + } + ); + } + + /** + * Handle bulk delete operation with manual timestamp checking + * @throws \Utopia\Database\Exception\Query + * @throws ConflictException + */ + private function handleBulkDeleteOperation( + Database $dbForProject, + string $collectionId, + array $data, + \DateTime $createdAt, + array &$state + ): void + { + $queries = Query::parseQueries($data['queries'] ?? []); + + $dbForProject->deleteDocuments( + $collectionId, + $queries, + onNext: function (Document $deleted, Document $old) use (&$state, $collectionId, $createdAt) { + $dependent = isset($state[$collectionId][$deleted->getId()]); + + // If not in transaction state, check for timestamp conflicts + if (!$dependent) { + $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); + if ($oldUpdatedAt > $createdAt) { + throw new ConflictException('Document was updated after the transaction operation'); + } + } + + // Remove from state after successful deletion + if (isset($state[$collectionId][$deleted->getId()])) { + unset($state[$collectionId][$deleted->getId()]); + } + } + ); + } +} \ No newline at end of file diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 98c9d01a87..6f334437b0 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -432,7 +432,7 @@ class StatsResources extends Action protected function writeDocuments(Database $dbForLogs, Document $project): void { - $dbForLogs->createOrUpdateDocuments( + $dbForLogs->upsertDocuments( 'stats', $this->documents ); diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 3610381d5a..3a615072df 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -424,7 +424,7 @@ class StatsUsage extends Action try { $dbForProject = $getProjectDB($projectStats['project']); Console::log('Processing batch with ' . count($projectStats['stats']) . ' stats'); - $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); + $dbForProject->upsertDocumentsWithIncrease('stats', 'value', $projectStats['stats']); Console::success('Batch successfully written to DB'); unset($this->projects[$sequence]); @@ -468,7 +468,7 @@ class StatsUsage extends Action try { Console::log('Processing batch with ' . count($this->statDocuments) . ' stats'); - $dbForLogs->createOrUpdateDocumentsWithIncrease( + $dbForLogs->upsertDocumentsWithIncrease( 'stats', 'value', $this->statDocuments diff --git a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php index cb0f3f3827..f1e8b64d58 100644 --- a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php @@ -1044,6 +1044,8 @@ class TransactionsTest extends Scope 'required' => true, ]); + sleep(2); + // Create unique index on email $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", array_merge([ 'content-type' => 'application/json', @@ -1055,7 +1057,7 @@ class TransactionsTest extends Scope 'attributes' => ['email'], ]); - sleep(3); + sleep(2); // Create an existing document $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ From c2916c7a2dd7103f15c1babd2a0544cc291667c5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 5 Sep 2025 01:04:48 +1200 Subject: [PATCH 077/274] Lint --- .../Databases/Http/Transactions/Update.php | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index ed277e7b2f..edb0e2cbaa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -222,8 +222,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { if ($documentId && !isset($data['$id'])) { $data['$id'] = $documentId; } @@ -247,8 +246,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -286,8 +284,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -317,8 +314,7 @@ class Update extends Action string $documentId, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -347,8 +343,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -385,8 +380,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -422,8 +416,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { $dbForProject->createDocuments( $collectionId, @@ -447,8 +440,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->updateDocuments( @@ -458,7 +450,7 @@ class Update extends Action onNext: function (Document $updated, Document $old) use (&$state, $collectionId, $createdAt) { // Check if this document was created/modified in this transaction $dependent = isset($state[$collectionId][$updated->getId()]); - + // If not in transaction state, check for timestamp conflicts if (!$dependent) { $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); @@ -466,7 +458,7 @@ class Update extends Action throw new ConflictException('Document was updated after the request timestamp'); } } - + $state[$collectionId][$updated->getId()] = $updated; } ); @@ -482,8 +474,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { // Run bulk upsert without timestamp wrapper, checking manually in callback $dbForProject->upsertDocuments( $collectionId, @@ -492,7 +483,7 @@ class Update extends Action if ($old !== null) { // This is an update - check if document was created/modified in this transaction $dependent = isset($state[$collectionId][$upserted->getId()]); - + // If not in transaction state, check for timestamp conflicts if (!$dependent) { $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); @@ -519,8 +510,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->deleteDocuments( @@ -536,7 +526,7 @@ class Update extends Action throw new ConflictException('Document was updated after the transaction operation'); } } - + // Remove from state after successful deletion if (isset($state[$collectionId][$deleted->getId()])) { unset($state[$collectionId][$deleted->getId()]); @@ -544,4 +534,4 @@ class Update extends Action } ); } -} \ No newline at end of file +} From 01586653a1a62c37123b38c1fd8f43d8a317d4c7 Mon Sep 17 00:00:00 2001 From: Veeresh <75656445+Veera-mulge@users.noreply.github.com> Date: Thu, 4 Sep 2025 20:36:03 +0530 Subject: [PATCH 078/274] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0b5c5ab4b..89390515eb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> We just announced time helper queries for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-time-helper-queries) +> We just announced inversion queries for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-inversion-queries) > Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga) From 391942ba72d5afc38407c8db936ff4d683bdef9d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 6 Sep 2025 02:37:17 +1200 Subject: [PATCH 079/274] Fix bulk logged queries --- .../Collections/Documents/Bulk/Delete.php | 4 +- .../Collections/Documents/Bulk/Update.php | 7 +- .../Transactions/TransactionsTest.php | 1620 ++++++++++++++++- 3 files changed, 1603 insertions(+), 28 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index b57c047a8c..82f940bb62 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -104,6 +104,8 @@ class Delete extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); } + $originalQueries = $queries; + try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -135,7 +137,7 @@ class Delete extends Action 'transactionInternalId' => $transaction->getSequence(), 'action' => 'bulkDelete', 'data' => [ - 'queries' => $queries, + 'queries' => $originalQueries, ], ]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 41b8bbda74..1b9559d4b9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -116,13 +116,15 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk update is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); } + $originalQueries = $queries; + try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - if ($data['$permissions']) { + if (isset($data['$permissions'])) { $validator = new Permissions(); if (!$validator->isValid($data['$permissions'])) { throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription()); @@ -157,7 +159,7 @@ class Update extends Action 'action' => 'bulkUpdate', 'data' => [ 'data' => $data, - 'queries' => $queries, + 'queries' => $originalQueries, ], ]); @@ -167,7 +169,6 @@ class Update extends Action 'transactions', $transactionId, 'operations', - 1 ); }); diff --git a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php index f1e8b64d58..9409834be0 100644 --- a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php @@ -19,7 +19,7 @@ class TransactionsTest extends Scope /** * Test creating a transaction */ - public function testCreate(): array + public function testCreate(): void { // Create database first $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ @@ -91,21 +91,35 @@ class TransactionsTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); - - return [ - 'databaseId' => $databaseId, - 'transactionId1' => $transactionId1, - 'transactionId2' => $transactionId2 - ]; } /** - * @depends testCreate + * Test adding operations to a transaction */ - public function testAddOperations(array $data): array + public function testAddOperations(): void { - $databaseId = $data['databaseId']; - $transactionId = $data['transactionId1']; + // Create database first + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionOperationsTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; // Create a collection for testing $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ @@ -233,20 +247,109 @@ class TransactionsTest extends Scope ]); $this->assertEquals(404, $response['headers']['status-code']); - - return array_merge($data, [ - 'collectionId' => $collectionId - ]); } /** - * @depends testAddOperations + * Test committing a transaction */ - public function testCommit(array $data): void + public function testCommit(): void { - $databaseId = $data['databaseId']; - $collectionId = $data['collectionId']; - $transactionId = $data['transactionId1']; + // Create database first + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionCommitTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Create collection + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TransactionCommitTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $collectionId = $collection['body']['$id']; + + // Add attributes + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; + + // Add operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'doc1', + 'data' => [ + 'name' => 'Test Document 1' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc2', + 'data' => [ + 'name' => 'Test Document 2' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'update', + 'documentId' => 'doc1', + 'data' => [ + 'name' => 'Updated Document 1' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(3, $response['body']['operations']); // Commit the transaction $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ @@ -292,12 +395,32 @@ class TransactionsTest extends Scope } /** - * @depends testCreate + * Test rolling back a transaction */ - public function testRollback(array $data): void + public function testRollback(): void { - $databaseId = $data['databaseId']; - $transactionId = $data['transactionId2']; + // Create database first + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'TransactionRollbackTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; // Create a collection for rollback test $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ @@ -1384,4 +1507,1453 @@ class TransactionsTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); // Document not found } + + /** + * Test createDocument with transactionId via normal route + */ + public function testCreateDocument(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'WriteRoutesTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $attributes = [ + ['key' => 'name', 'type' => 'string', 'size' => 256, 'required' => true], + ['key' => 'counter', 'type' => 'integer', 'required' => false, 'min' => 0, 'max' => 10000], + ['key' => 'category', 'type' => 'string', 'size' => 256, 'required' => false], + ['key' => 'data', 'type' => 'string', 'size' => 256, 'required' => false], + ]; + + foreach ($attributes as $attr) { + $type = $attr['type']; + unset($attr['type']); + + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/{$type}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), $attr); + + $this->assertEquals(202, $response['headers']['status-code']); + } + + sleep(3); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; + + // Create document via normal route with transactionId + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_from_route', + 'data' => [ + 'name' => 'Created via normal route', + 'counter' => 100, + 'category' => 'test' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Document should not exist outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_from_route", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Document should now exist + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_from_route", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Created via normal route', $response['body']['name']); + } + + /** + * Test updateDocument with transactionId via normal route + */ + public function testUpdateDocument(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'UpdateRouteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'min' => 0, + 'max' => 10000, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create document outside transaction + $doc = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_to_update', + 'data' => [ + 'name' => 'Original name', + 'counter' => 50, + 'category' => 'original' + ] + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Update document via normal route with transactionId + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_to_update", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'name' => 'Updated via normal route', + 'counter' => 150, + 'category' => 'updated' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Document should still have original values outside transaction + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_to_update", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Original name', $response['body']['name']); + $this->assertEquals(50, $response['body']['counter']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Document should now have updated values + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_to_update", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Updated via normal route', $response['body']['name']); + $this->assertEquals(150, $response['body']['counter']); + } + + /** + * Test upsertDocument with transactionId via normal route + */ + public function testUpsertDocument(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'UpsertRouteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'min' => 0, + 'max' => 10000, + ]); + + sleep(3); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Upsert document (create) via normal route with transactionId + $response = $this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_upsert', + 'data' => [ + 'name' => 'Created by upsert', + 'counter' => 25 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Document should not exist outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Upsert same document (update) in same transaction + $response = $this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_upsert', + 'data' => [ + 'name' => 'Updated by upsert', + 'counter' => 75 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); // Upsert in transaction returns 201 + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Document should now exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Updated by upsert', $response['body']['name']); + $this->assertEquals(75, $response['body']['counter']); + } + + /** + * Test deleteDocument with transactionId via normal route + */ + public function testDeleteDocument(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'DeleteRouteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // Create document outside transaction + $doc = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_to_delete', + 'data' => ['name' => 'Will be deleted'] + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Delete document via normal route with transactionId + $response = $this->client->call(Client::METHOD_DELETE, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_to_delete", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'transactionId' => $transactionId + ]); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Document should still exist outside transaction + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_to_delete", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Document should no longer exist + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_to_delete", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * Test bulkCreate with transactionId via normal route + */ + public function testBulkCreate(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkCreateTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Bulk create via normal route with transactionId + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documents' => [ + [ + '$id' => 'bulk_create_1', + 'name' => 'Bulk created 1', + 'category' => 'bulk_created' + ], + [ + '$id' => 'bulk_create_2', + 'name' => 'Bulk created 2', + 'category' => 'bulk_created' + ], + [ + '$id' => 'bulk_create_3', + 'name' => 'Bulk created 3', + 'category' => 'bulk_created' + ] + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); // Bulk operations return 200 + + // Documents should not exist outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('category', ['bulk_created'])->toString()] + ]); + + $this->assertEquals(0, $response['body']['total']); + + // Individual document check + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/bulk_create_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Documents should now exist + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('category', ['bulk_created'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + + // Verify individual documents + for ($i = 1; $i <= 3; $i++) { + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/bulk_create_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals("Bulk created {$i}", $response['body']['name']); + $this->assertEquals('bulk_created', $response['body']['category']); + } + } + + /** + * Test bulkUpdate with transactionId via normal route + */ + public function testBulkUpdate(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkUpdateTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create documents for bulk testing + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'bulk_update_' . $i, + 'data' => [ + 'name' => 'Bulk doc ' . $i, + 'category' => 'bulk_test' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Bulk update via normal route with transactionId + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('category', ['bulk_test'])->toString()], + 'data' => ['category' => 'bulk_updated'], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Documents should still have original category outside transaction + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('category', ['bulk_test'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Documents should now have updated category + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('category', ['bulk_updated'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + } + + /** + * Test bulkUpsert with transactionId via normal route + */ + public function testBulkUpsert(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkUpsertTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'min' => 0, + 'max' => 10000, + ]); + + sleep(3); + + // Create one document outside transaction + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'bulk_upsert_existing', + 'data' => [ + 'name' => 'Existing doc', + 'counter' => 10 + ] + ]); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Bulk upsert via normal route with transactionId (updates existing, creates new) + $response = $this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documents' => [ + [ + '$id' => 'bulk_upsert_existing', + 'name' => 'Updated existing', + 'counter' => 20 + ], + [ + '$id' => 'bulk_upsert_new', + 'name' => 'New doc', + 'counter' => 30 + ] + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Original document should be unchanged, new document shouldn't exist outside transaction + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/bulk_upsert_existing", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Existing doc', $response['body']['name']); + $this->assertEquals(10, $response['body']['counter']); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/bulk_upsert_new", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Check both documents exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/bulk_upsert_existing", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Updated existing', $response['body']['name']); + $this->assertEquals(20, $response['body']['counter']); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/bulk_upsert_new", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('New doc', $response['body']['name']); + $this->assertEquals(30, $response['body']['counter']); + } + + /** + * Test bulkDelete with transactionId via normal route + */ + public function testBulkDelete(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkDeleteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create documents for bulk testing + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'bulk_delete_' . $i, + 'data' => [ + 'name' => 'Delete doc ' . $i, + 'category' => 'bulk_delete_test' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Bulk delete via normal route with transactionId + $response = $this->client->call(Client::METHOD_DELETE, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('category', ['bulk_delete_test'])->toString()], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); // Bulk delete with transaction returns 200 + + // Documents should still exist outside transaction + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('category', ['bulk_delete_test'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Documents should now be deleted + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [Query::equal('category', ['bulk_delete_test'])->toString()] + ]); + + $this->assertEquals(0, $response['body']['total']); + } + + /** + * Test multiple single route operations in one transaction + */ + public function testMixedSingleOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'MultipleSingleRoutesDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'status', + 'size' => 256, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'priority', + 'required' => false, + 'min' => 1, + 'max' => 10, + ]); + + sleep(3); + + // Create an existing document outside transaction for testing + $existingDoc = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_doc', + 'data' => [ + 'name' => 'Existing Document', + 'status' => 'active', + 'priority' => 5 + ] + ]); + + $this->assertEquals(201, $existingDoc['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + $this->assertEquals(201, $transaction['headers']['status-code']); + + // 1. Create new document via normal route with transactionId + $response1 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'new_doc_1', + 'data' => [ + 'name' => 'New Document 1', + 'status' => 'pending', + 'priority' => 1 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response1['headers']['status-code']); + + // 2. Create another document via normal route with transactionId + $response2 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'new_doc_2', + 'data' => [ + 'name' => 'New Document 2', + 'status' => 'pending', + 'priority' => 2 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response2['headers']['status-code']); + + // 3. Update existing document via normal route with transactionId + $response3 = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/existing_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'updated', + 'priority' => 10 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response3['headers']['status-code']); + + // 4. Update the first new document (created in same transaction) + $response4 = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/new_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'active', + 'priority' => 8 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response4['headers']['status-code']); + + // 5. Delete the second new document (created in same transaction) + $response5 = $this->client->call(Client::METHOD_DELETE, "/databases/{$databaseId}/collections/{$collectionId}/documents/new_doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'transactionId' => $transactionId + ]); + + $this->assertEquals(204, $response5['headers']['status-code']); + + // 6. Upsert a new document via normal route with transactionId + $response6 = $this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$collectionId}/documents/upserted_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'upserted_doc', + 'data' => [ + 'name' => 'Upserted Document', + 'status' => 'new', + 'priority' => 3 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response6['headers']['status-code']); + + // Check transaction has correct number of operations + $txnDetails = $this->client->call(Client::METHOD_GET, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $txnDetails['headers']['status-code']); + $this->assertEquals(6, $txnDetails['body']['operations']); // 6 operations total + + // Verify nothing exists outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/new_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/upserted_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Existing doc should still have original values + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/existing_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('active', $response['body']['status']); + $this->assertEquals(5, $response['body']['priority']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + $this->assertEquals('committed', $response['body']['status']); + + // Verify final state after commit + // new_doc_1 should exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/new_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('New Document 1', $response['body']['name']); + $this->assertEquals('active', $response['body']['status']); + $this->assertEquals(8, $response['body']['priority']); + + // new_doc_2 should not exist (was deleted in transaction) + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/new_doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // existing_doc should have updated values + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/existing_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('updated', $response['body']['status']); + $this->assertEquals(10, $response['body']['priority']); + + // upserted_doc should exist + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/upserted_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Upserted Document', $response['body']['name']); + $this->assertEquals('new', $response['body']['status']); + $this->assertEquals(3, $response['body']['priority']); + + // Verify total document count + $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(3, $documents['body']['total']); // existing_doc, new_doc_1, upserted_doc + } + + /** + * Test mixed operations with transactions + */ + public function testMixedOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'MixedOpsTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add operation via Operations\Add endpoint + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'mixed_doc1', + 'data' => ['name' => 'Via Operations Add'] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['operations']); + + // Add operation via normal route with transactionId + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'mixed_doc2', + 'data' => ['name' => 'Via normal route'], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Check transaction now has 2 operations + $txnDetails = $this->client->call(Client::METHOD_GET, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(2, $txnDetails['body']['operations']); + + // Both documents shouldn't exist yet + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/mixed_doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/mixed_doc2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Both documents should now exist + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/mixed_doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Via Operations Add', $response['body']['name']); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/mixed_doc2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Via normal route', $response['body']['name']); + } } From 098b0cd02838d7baa64df61f74bff2070f3717b2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 6 Sep 2025 02:37:52 +1200 Subject: [PATCH 080/274] Fix array as doc --- .../Modules/Databases/Http/Transactions/Update.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php index edb0e2cbaa..299d0f2abf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php @@ -132,6 +132,10 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; + if ($data instanceof Document) { + $data = $data->getArrayCopy(); + } + // Execute the operation based on its type switch ($action) { case 'create': @@ -197,9 +201,11 @@ class Update extends Action } if ($rollback) { - $transaction = $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'rolledBack', - ])); + $transaction = $dbForProject->updateDocument( + 'transactions', + $transactionId, + new Document(['status' => 'rolledBack']) + ); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) @@ -441,6 +447,8 @@ class Update extends Action \DateTime $createdAt, array &$state ): void { + \var_dump($data); + $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->updateDocuments( From 812a09f5564a51a3d1f86a25bdab020a5611c120 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 6 Sep 2025 04:26:49 +1200 Subject: [PATCH 081/274] Add read-your-write support --- app/init/resources.php | 5 + src/Appwrite/Databases/TransactionManager.php | 231 ++++++++++++++++++ .../Collections/Documents/Delete.php | 26 +- .../Databases/Collections/Documents/Get.php | 15 +- .../Collections/Documents/Update.php | 14 +- .../Collections/Documents/Upsert.php | 20 +- .../Databases/Collections/Documents/XList.php | 20 +- .../Transactions/TransactionsTest.php | 16 +- 8 files changed, 320 insertions(+), 27 deletions(-) create mode 100644 src/Appwrite/Databases/TransactionManager.php diff --git a/app/init/resources.php b/app/init/resources.php index e4e8fbef5e..e3955540df 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -4,6 +4,7 @@ use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; use Appwrite\Auth\Key; +use Appwrite\Databases\TransactionManager; use Appwrite\Event\Audit; use Appwrite\Event\Build; use Appwrite\Event\Certificate; @@ -1030,3 +1031,7 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef $referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : ''); return $referrer; }, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']); + +App::setResource('transactionManager', function (Database $dbForProject) { + return new TransactionManager($dbForProject); +}, ['dbForProject']); diff --git a/src/Appwrite/Databases/TransactionManager.php b/src/Appwrite/Databases/TransactionManager.php new file mode 100644 index 0000000000..533e0e489e --- /dev/null +++ b/src/Appwrite/Databases/TransactionManager.php @@ -0,0 +1,231 @@ +dbForProject = $dbForProject; + } + + /** + * Get the current state of a transaction by replaying its operations + */ + private function getTransactionState(string $transactionId): array + { + $transaction = $this->dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') { + return []; + } + + // Fetch operations ordered by sequence to replay in exact order + $operations = $this->dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc('$createdAt'), // Ensure operations are processed in order + ]); + + $state = []; + + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; + $documentId = $operation['documentId']; + $action = $operation['action']; + $data = $operation['data']; + + if ($data instanceof Document) { + $data = $data->getArrayCopy(); + } + + switch ($action) { + case 'create': + if ($documentId) { + $state[$collectionId][$documentId] = [ + 'action' => 'create', + 'document' => new Document($data), + 'exists' => true + ]; + } + break; + + case 'update': + if (isset($state[$collectionId][$documentId])) { + // Update existing document in transaction state + $existingDocument = $state[$collectionId][$documentId]['document']; + foreach ($data as $key => $value) { + if ($key !== '$id') { + $existingDocument->setAttribute($key, $value); + } + } + $state[$collectionId][$documentId]['action'] = 'update'; + } else { + // Document doesn't exist in transaction state, will be merged with committed version + $state[$collectionId][$documentId] = [ + 'action' => 'update', + 'document' => new Document($data), + 'exists' => true + ]; + } + break; + + case 'upsert': + $state[$collectionId][$documentId] = [ + 'action' => 'upsert', + 'document' => new Document($data), + 'exists' => true + ]; + break; + + case 'delete': + $state[$collectionId][$documentId] = [ + 'action' => 'delete', + 'exists' => false + ]; + break; + + case 'bulkCreate': + if (is_array($data)) { + foreach ($data as $doc) { + if ($doc instanceof Document) { + $doc = $doc->getArrayCopy(); + } + $state[$collectionId][$doc['$id']] = [ + 'action' => 'create', + 'document' => new Document($doc), + 'exists' => true + ]; + } + } + break; + } + } + + return $state; + } + + /** + * Get a document with transaction-aware logic + */ + public function getDocument( + string $collectionId, + string $documentId, + ?string $transactionId = null, + array $queries = [] + ): Document { + // If no transaction, use normal database retrieval + if ($transactionId === null) { + return $this->dbForProject->getDocument($collectionId, $documentId, $queries); + } + + $state = $this->getTransactionState($transactionId); + + + // Check if document exists in transaction state + if (isset($state[$collectionId][$documentId])) { + $docState = $state[$collectionId][$documentId]; + + if (!$docState['exists']) { + // Document was deleted in transaction + return new Document(); + } + + if ($docState['action'] === 'create') { + // Document was created in transaction, return the created version + return $docState['document']; + } + + if ($docState['action'] === 'update' || $docState['action'] === 'upsert') { + // This is an update to an existing document, merge with committed version + $committedDoc = $this->dbForProject->getDocument($collectionId, $documentId, $queries); + if (!$committedDoc->isEmpty()) { + // Apply the updates from transaction + foreach ($docState['document']->getAttributes() as $key => $value) { + if ($key !== '$id') { + $committedDoc->setAttribute($key, $value); + } + } + return $committedDoc; + } elseif ($docState['action'] === 'upsert') { + // Upsert created a new document since committed doc doesn't exist + return $docState['document']; + } + } + } + + // Document not affected by transaction, return committed version + return $this->dbForProject->getDocument($collectionId, $documentId, $queries); + } + + /** + * List documents with transaction-aware logic + */ + public function listDocuments( + string $collectionId, + ?string $transactionId = null, + array $queries = [] + ): array { + // If no transaction, use normal database retrieval + if ($transactionId === null) { + return $this->dbForProject->find($collectionId, $queries); + } + + $state = $this->getTransactionState($transactionId); + $committedDocs = $this->dbForProject->find($collectionId, $queries); + $documentMap = []; + + // Build map of committed documents + foreach ($committedDocs as $doc) { + $documentMap[$doc->getId()] = $doc; + } + + // Apply transaction state changes + if (isset($state[$collectionId])) { + foreach ($state[$collectionId] as $docId => $docState) { + if (!$docState['exists']) { + // Document was deleted, remove from results + unset($documentMap[$docId]); + } elseif ($docState['action'] === 'create') { + // Document was created, add to results + $documentMap[$docId] = $docState['document']; + } elseif ($docState['action'] === 'update' || $docState['action'] === 'upsert') { + if (isset($documentMap[$docId])) { + // Update existing document + foreach ($docState['document']->getAttributes() as $key => $value) { + if ($key !== '$id') { + $documentMap[$docId]->setAttribute($key, $value); + } + } + } elseif ($docState['action'] === 'upsert') { + // Upsert created a new document + $documentMap[$docId] = $docState['document']; + } + } + } + } + + return array_values($documentMap); + } + + /** + * Check if a document exists with transaction-aware logic + */ + public function documentExists( + string $collectionId, + string $documentId, + ?string $transactionId = null + ): bool { + $doc = $this->getDocument($collectionId, $documentId, $transactionId); + return !$doc->isEmpty(); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 7ce1d411ce..20a7b98e61 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; +use Appwrite\Databases\TransactionManager; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -79,12 +80,24 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('transactionManager') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void - { + public function action( + string $databaseId, + string $collectionId, + string $documentId, + ?string $transactionId, + ?\DateTime $requestTimestamp, + UtopiaResponse $response, + Database $dbForProject, + Event $queueForEvents, + StatsUsage $queueForStatsUsage, + TransactionManager $transactionManager, + array $plan + ): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -101,7 +114,14 @@ class Delete extends Action } // Read permission should not be required for delete - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); + $collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); + + if ($transactionId !== null) { + // Use transaction-aware document retrieval to see changes from same transaction + $document = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + } else { + $document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); + } if ($document->isEmpty()) { throw new Exception($this->getNotFoundException()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index 7f621fb33a..c69181b30d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; +use Appwrite\Databases\TransactionManager; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; @@ -63,13 +64,15 @@ class Get extends Action ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('documentId', '', new UID(), 'Document ID.') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') + ->inject('transactionManager') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager): void { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -93,13 +96,17 @@ class Get extends Action try { $selects = Query::groupByType($queries)['selections'] ?? []; + $collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); - if (! empty($selects)) { + // Use transaction-aware document retrieval if transactionId is provided + if ($transactionId !== null) { + $document = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId, $queries); + } elseif (! empty($selects)) { // has selects, allow relationship on documents! - $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries); + $document = $dbForProject->getDocument($collectionTableId, $documentId, $queries); } else { // has no selects, disable relationship looping on documents! - $document = $dbForProject->skipRelationships(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries)); + $document = $dbForProject->skipRelationships(fn () => $dbForProject->getDocument($collectionTableId, $documentId, $queries)); } } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index ba3ad4d8b5..16f9dab0d7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; +use Appwrite\Databases\TransactionManager; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -83,13 +84,13 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('transactionManager') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager, array $plan): void { - $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && \is_null($permissions)) { @@ -113,7 +114,14 @@ class Update extends Action // Read permission should not be required for update /** @var Document $document */ - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); + $collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); + + if ($transactionId !== null) { + // Use transaction-aware document retrieval to see changes from same transaction + $document = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + } else { + $document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); + } if ($document->isEmpty()) { throw new Exception($this->getNotFoundException()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 7a6c7e960b..da801db618 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; +use Appwrite\Databases\TransactionManager; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -87,11 +88,12 @@ class Upsert extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('transactionManager') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager, array $plan): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -124,9 +126,16 @@ class Upsert extends Action $permissions = Permission::aggregate($permissions, $allowedPermissions); + $collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); + // If no permission, upsert permission from the old document if present (update scenario) else add default permission (create scenario) if (\is_null($permissions)) { - $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); + if ($transactionId !== null) { + // Use transaction-aware document retrieval to see changes from same transaction + $oldDocument = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + } else { + $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); + } if ($oldDocument->isEmpty()) { if (!empty($user->getId())) { $defaultPermissions = []; @@ -320,7 +329,12 @@ class Upsert extends Action $collectionsCache = []; if (empty($upserted[0])) { - $upserted[0] = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId); + if ($transactionId !== null) { + // For transactions, get the document with transaction changes applied + $upserted[0] = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + } else { + $upserted[0] = $dbForProject->getDocument($collectionTableId, $documentId); + } } $document = $upserted[0]; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 9c8405cf18..78f91033a9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; +use Appwrite\Databases\TransactionManager; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; @@ -65,13 +66,15 @@ class XList extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') + ->inject('transactionManager') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage): void + public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager): void { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -121,17 +124,22 @@ class XList extends Action try { $selectQueries = Query::groupByType($queries)['selections'] ?? []; + $collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); - if (! empty($selectQueries)) { + // Use transaction-aware document retrieval if transactionId is provided + if ($transactionId !== null) { + $documents = $transactionManager->listDocuments($collectionTableId, $transactionId, $queries); + $total = count($documents); // For transaction-aware queries, we count the actual results + } elseif (! empty($selectQueries)) { // has selects, allow relationship on documents - $documents = $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries); + $documents = $dbForProject->find($collectionTableId, $queries); + $total = $dbForProject->count($collectionTableId, $queries, APP_LIMIT_COUNT); } else { // has no selects, disable relationship loading on documents /* @type Document[] $documents */ - $documents = $dbForProject->skipRelationships(fn () => $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries)); + $documents = $dbForProject->skipRelationships(fn () => $dbForProject->find($collectionTableId, $queries)); + $total = $dbForProject->count($collectionTableId, $queries, APP_LIMIT_COUNT); } - - $total = $dbForProject->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; $attribute = $this->isCollectionsAPI() ? 'attribute' : 'column'; diff --git a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php index 9409834be0..daba81e6ec 100644 --- a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php @@ -1554,7 +1554,7 @@ class TransactionsTest extends Scope foreach ($attributes as $attr) { $type = $attr['type']; unset($attr['type']); - + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/{$type}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1874,7 +1874,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Document should now exist with updated values @@ -1982,7 +1982,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Document should no longer exist @@ -2112,7 +2112,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Documents should now exist @@ -2249,7 +2249,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Documents should now have updated category @@ -2389,7 +2389,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Check both documents exist with updated values @@ -2520,7 +2520,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Documents should now be deleted @@ -2936,7 +2936,7 @@ class TransactionsTest extends Scope ]), [ 'commit' => true ]); - + $this->assertEquals(200, $response['headers']['status-code']); // Both documents should now exist From abf6c0a0b8f3be38b5ba924862907ddebc94b0cc Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Sep 2025 19:20:26 +1200 Subject: [PATCH 082/274] Add more bulk tests --- .../Transactions/TransactionsTest.php | 679 ++++++++++++++++++ 1 file changed, 679 insertions(+) diff --git a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php index daba81e6ec..e2d39d75ec 100644 --- a/tests/e2e/Services/Databases/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Transactions/TransactionsTest.php @@ -2956,4 +2956,683 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('Via normal route', $response['body']['name']); } + + /** + * Test bulk update with queries that should match documents created in the same transaction + */ + public function testBulkUpdateWithTransactionAwareQueries(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkTxnAwareDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'age', + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/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, + ]); + + sleep(3); // Wait for attributes to be created + + // Create some existing documents + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_' . $i, + 'data' => [ + 'name' => 'Existing ' . $i, + 'age' => 20 + $i, + 'status' => 'inactive' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Step 1: Create new documents with age > 25 in transaction + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'txn_doc_1', + 'data' => [ + 'name' => 'Transaction Doc 1', + 'age' => 30, + 'status' => 'inactive' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'txn_doc_2', + 'data' => [ + 'name' => 'Transaction Doc 2', + 'age' => 35, + 'status' => 'inactive' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Step 2: Bulk update all documents with age > 25 to have status 'active' + // This should match both existing_3 (age=23 doesn't match, age=24 doesn't match, but existing documents have age 21,22,23) + // Wait, let me fix the ages - existing docs have ages 21, 22, 23, so only txn docs should match + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'active' + ], + 'queries' => [Query::greaterThan('age', 25)->toString()], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 that documents created in the transaction were updated by the bulk update + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/txn_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('active', $response['body']['status'], 'Document created in transaction should be updated by bulk update query'); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/txn_doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('active', $response['body']['status'], 'Document created in transaction should be updated by bulk update query'); + + // Verify existing documents were not affected + for ($i = 1; $i <= 3; $i++) { + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/existing_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('inactive', $response['body']['status'], "Existing document {$i} should remain inactive (age <= 25)"); + } + } + + /** + * Test bulk update with queries that should match documents updated in the same transaction + */ + public function testBulkUpdateMatchingUpdatedDocuments(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkUpdateTxnDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'priority', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); // Wait for attributes to be created + + // Create existing documents + for ($i = 1; $i <= 4; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_' . $i, + 'data' => [ + 'name' => 'Document ' . $i, + 'category' => 'normal', + 'priority' => 'low' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Step 1: Update some documents to have category 'special' in transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'category' => 'special' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'category' => 'special' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Step 2: Bulk update all documents with category 'special' to have priority 'high' + // This should match the documents we just updated in the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'priority' => 'high' + ], + 'queries' => [Query::equal('category', ['special'])->toString()], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 that the updated documents were matched by bulk update + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('special', $response['body']['category']); + $this->assertEquals('high', $response['body']['priority'], 'Document updated in transaction should be matched by bulk update query'); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('special', $response['body']['category']); + $this->assertEquals('high', $response['body']['priority'], 'Document updated in transaction should be matched by bulk update query'); + + // Verify other documents were not affected + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_3", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('normal', $response['body']['category']); + $this->assertEquals('low', $response['body']['priority']); + } + + /** + * Test bulk delete with queries that should match documents created in the same transaction + */ + public function testBulkDeleteMatchingCreatedDocuments(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkDeleteTxnDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'type', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); // Wait for attributes to be created + + // Create existing documents + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_' . $i, + 'data' => [ + 'name' => 'Existing ' . $i, + 'type' => 'permanent' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Step 1: Create temporary documents in transaction + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'temp_1', + 'data' => [ + 'name' => 'Temporary 1', + 'type' => 'temporary' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'temp_2', + 'data' => [ + 'name' => 'Temporary 2', + 'type' => 'temporary' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Step 2: Bulk delete all documents with type 'temporary' + // This should delete the documents we just created in the transaction + $response = $this->client->call(Client::METHOD_DELETE, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('type', ['temporary'])->toString()], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 temporary documents were deleted (should not exist) + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/temp_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Temporary document created and deleted in transaction should not exist'); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/temp_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Temporary document created and deleted in transaction should not exist'); + + // Verify existing documents were not affected + for ($i = 1; $i <= 3; $i++) { + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/existing_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code'], "Permanent document {$i} should still exist"); + $this->assertEquals('permanent', $response['body']['type']); + } + } + + /** + * Test bulk delete with queries that should match documents updated in the same transaction + */ + public function testBulkDeleteMatchingUpdatedDocuments(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkDeleteUpdateTxnDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/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, + ]); + + sleep(3); // Wait for attributes to be created + + // Create existing documents + for ($i = 1; $i <= 5; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_' . $i, + 'data' => [ + 'name' => 'Document ' . $i, + 'status' => 'active' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Step 1: Mark some documents for deletion by updating their status + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'marked_for_deletion' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_4", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'marked_for_deletion' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Step 2: Bulk delete all documents with status 'marked_for_deletion' + // This should delete the documents we just updated in the transaction + $response = $this->client->call(Client::METHOD_DELETE, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('status', ['marked_for_deletion'])->toString()], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 marked documents were deleted + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Document marked for deletion should have been deleted'); + + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_4", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Document marked for deletion should have been deleted'); + + // Verify other documents still exist + foreach ([1, 3, 5] as $i) { + $response = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/doc_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code'], "Document {$i} should still exist"); + $this->assertEquals('active', $response['body']['status']); + } + } } From 7c3ba7471062aac5dd8a76a325654669ed86f232 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Sep 2025 19:21:59 +1200 Subject: [PATCH 083/274] Rename --- app/init/resources.php | 6 +++--- src/Appwrite/Databases/TransactionManager.php | 2 +- .../Http/Databases/Collections/Documents/Delete.php | 8 ++++---- .../Http/Databases/Collections/Documents/Get.php | 8 ++++---- .../Http/Databases/Collections/Documents/Update.php | 8 ++++---- .../Http/Databases/Collections/Documents/Upsert.php | 10 +++++----- .../Http/Databases/Collections/Documents/XList.php | 8 ++++---- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index e3955540df..2f158a7cdf 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -4,7 +4,7 @@ use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; use Appwrite\Auth\Key; -use Appwrite\Databases\TransactionManager; +use Appwrite\Databases\TransactionState; use Appwrite\Event\Audit; use Appwrite\Event\Build; use Appwrite\Event\Certificate; @@ -1032,6 +1032,6 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef return $referrer; }, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']); -App::setResource('transactionManager', function (Database $dbForProject) { - return new TransactionManager($dbForProject); +App::setResource('transactionState', function (Database $dbForProject) { + return new TransactionState($dbForProject); }, ['dbForProject']); diff --git a/src/Appwrite/Databases/TransactionManager.php b/src/Appwrite/Databases/TransactionManager.php index 533e0e489e..7d99d61d01 100644 --- a/src/Appwrite/Databases/TransactionManager.php +++ b/src/Appwrite/Databases/TransactionManager.php @@ -9,7 +9,7 @@ use Utopia\Database\Query; /** * Service for managing transaction state and providing transaction-aware document operations */ -class TransactionManager +class TransactionState { private Database $dbForProject; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 20a7b98e61..8459e06c43 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; -use Appwrite\Databases\TransactionManager; +use Appwrite\Databases\TransactionState; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -80,7 +80,7 @@ class Delete extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('transactionManager') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } @@ -95,7 +95,7 @@ class Delete extends Action Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, - TransactionManager $transactionManager, + TransactionState $transactionState, array $plan ): void { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -118,7 +118,7 @@ class Delete extends Action if ($transactionId !== null) { // Use transaction-aware document retrieval to see changes from same transaction - $document = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + $document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { $document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index c69181b30d..cced1440a5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; -use Appwrite\Databases\TransactionManager; +use Appwrite\Databases\TransactionState; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; @@ -68,11 +68,11 @@ class Get extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->inject('transactionManager') + ->inject('transactionState') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager): void + public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -100,7 +100,7 @@ class Get extends Action // Use transaction-aware document retrieval if transactionId is provided if ($transactionId !== null) { - $document = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId, $queries); + $document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId, $queries); } elseif (! empty($selects)) { // has selects, allow relationship on documents! $document = $dbForProject->getDocument($collectionTableId, $documentId, $queries); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 16f9dab0d7..a2376b44a2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; -use Appwrite\Databases\TransactionManager; +use Appwrite\Databases\TransactionState; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -84,12 +84,12 @@ class Update extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('transactionManager') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -118,7 +118,7 @@ class Update extends Action if ($transactionId !== null) { // Use transaction-aware document retrieval to see changes from same transaction - $document = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + $document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { $document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index da801db618..6690e0214f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; -use Appwrite\Databases\TransactionManager; +use Appwrite\Databases\TransactionState; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -88,12 +88,12 @@ class Upsert extends Action ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') - ->inject('transactionManager') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager, array $plan): void + public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan): void { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -132,7 +132,7 @@ class Upsert extends Action if (\is_null($permissions)) { if ($transactionId !== null) { // Use transaction-aware document retrieval to see changes from same transaction - $oldDocument = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + $oldDocument = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId)); } @@ -331,7 +331,7 @@ class Upsert extends Action if (empty($upserted[0])) { if ($transactionId !== null) { // For transactions, get the document with transaction changes applied - $upserted[0] = $transactionManager->getDocument($collectionTableId, $documentId, $transactionId); + $upserted[0] = $transactionState->getDocument($collectionTableId, $documentId, $transactionId); } else { $upserted[0] = $dbForProject->getDocument($collectionTableId, $documentId); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 78f91033a9..7e6931395e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; use Appwrite\Auth\Auth; -use Appwrite\Databases\TransactionManager; +use Appwrite\Databases\TransactionState; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; @@ -70,11 +70,11 @@ class XList extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') - ->inject('transactionManager') + ->inject('transactionState') ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionManager $transactionManager): void + public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void { $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -128,7 +128,7 @@ class XList extends Action // Use transaction-aware document retrieval if transactionId is provided if ($transactionId !== null) { - $documents = $transactionManager->listDocuments($collectionTableId, $transactionId, $queries); + $documents = $transactionState->listDocuments($collectionTableId, $transactionId, $queries); $total = count($documents); // For transaction-aware queries, we count the actual results } elseif (! empty($selectQueries)) { // has selects, allow relationship on documents From 495eeeb9296a3e00ea2714c42aab92deba16747c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 9 Sep 2025 00:50:48 +1200 Subject: [PATCH 084/274] Support legacy --- .github/workflows/tests.yml | 1 - ...actionManager.php => TransactionState.php} | 0 .../{ => Databases}/Transactions/Create.php | 2 +- .../{ => Databases}/Transactions/Delete.php | 2 +- .../Http/{ => Databases}/Transactions/Get.php | 2 +- .../Transactions/Operations/Create.php | 4 +- .../{ => Databases}/Transactions/Update.php | 4 +- .../{ => Databases}/Transactions/XList.php | 2 +- .../Http/TablesDB/Transactions/Create.php | 54 + .../Http/TablesDB/Transactions/Delete.php | 54 + .../Http/TablesDB/Transactions/Get.php | 54 + .../Transactions/Operations/Create.php | 58 + .../Http/TablesDB/Transactions/Update.php | 58 + .../Http/TablesDB/Transactions/XList.php | 54 + .../Modules/Databases/Services/Http.php | 1 - .../Databases/Services/Registry/Legacy.php | 17 + .../Databases/Services/Registry/TablesDB.php | 17 + .../Services/Registry/Transactions.php | 27 - .../Utopia/Database/Validator/Operation.php | 26 +- .../DatabasesPermissionsGuestTest.php | 2 +- .../DatabasesPermissionsMemberTest.php | 2 +- .../DatabasesPermissionsScope.php | 2 +- .../DatabasesPermissionsTeamTest.php | 2 +- .../{ => Legacy}/Transactions/ACIDTest.php | 3 +- .../Transactions/TransactionsTest.php | 2 +- .../DatabasesPermissionsGuestTest.php | 2 +- .../DatabasesPermissionsMemberTest.php | 2 +- .../DatabasesPermissionsScope.php | 2 +- .../DatabasesPermissionsTeamTest.php | 2 +- .../TablesDB/Transactions/ACIDTest.php | 625 +++ .../Transactions/TransactionsTest.php | 3638 +++++++++++++++++ 31 files changed, 4670 insertions(+), 51 deletions(-) rename src/Appwrite/Databases/{TransactionManager.php => TransactionState.php} (100%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Transactions/Create.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Transactions/Delete.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Transactions/Get.php (96%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Transactions/Operations/Create.php (97%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Transactions/Update.php (99%) rename src/Appwrite/Platform/Modules/Databases/Http/{ => Databases}/Transactions/XList.php (97%) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php delete mode 100644 src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php rename tests/e2e/Services/Databases/Legacy/{ => Permissions}/DatabasesPermissionsGuestTest.php (99%) rename tests/e2e/Services/Databases/Legacy/{ => Permissions}/DatabasesPermissionsMemberTest.php (99%) rename tests/e2e/Services/Databases/{TablesDB => Legacy/Permissions}/DatabasesPermissionsScope.php (97%) rename tests/e2e/Services/Databases/Legacy/{ => Permissions}/DatabasesPermissionsTeamTest.php (99%) rename tests/e2e/Services/Databases/{ => Legacy}/Transactions/ACIDTest.php (99%) rename tests/e2e/Services/Databases/{ => Legacy}/Transactions/TransactionsTest.php (99%) rename tests/e2e/Services/Databases/TablesDB/{ => Permissions}/DatabasesPermissionsGuestTest.php (99%) rename tests/e2e/Services/Databases/TablesDB/{ => Permissions}/DatabasesPermissionsMemberTest.php (99%) rename tests/e2e/Services/Databases/{Legacy => TablesDB/Permissions}/DatabasesPermissionsScope.php (97%) rename tests/e2e/Services/Databases/TablesDB/{ => Permissions}/DatabasesPermissionsTeamTest.php (99%) create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2ca0c58db8..7b1a243da1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -169,7 +169,6 @@ jobs: Console, Databases/Legacy, Databases/TablesDB, - Databases/Transactions, Functions, FunctionsSchedule, GraphQL, diff --git a/src/Appwrite/Databases/TransactionManager.php b/src/Appwrite/Databases/TransactionState.php similarity index 100% rename from src/Appwrite/Databases/TransactionManager.php rename to src/Appwrite/Databases/TransactionState.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php similarity index 97% rename from src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php rename to src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index 2c98565568..d2f114a888 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -1,6 +1,6 @@ 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') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php similarity index 99% rename from src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php rename to src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 299d0f2abf..e854306a47 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -1,6 +1,6 @@ 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(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php new file mode 100644 index 0000000000..9440b586c5 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php @@ -0,0 +1,54 @@ +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(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php new file mode 100644 index 0000000000..a5e9c374a6 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php @@ -0,0 +1,54 @@ +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(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php new file mode 100644 index 0000000000..eac524682c --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -0,0 +1,58 @@ +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(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php new file mode 100644 index 0000000000..d5b0e737a0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php @@ -0,0 +1,58 @@ +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(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php new file mode 100644 index 0000000000..e04d28f200 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php @@ -0,0 +1,54 @@ +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(...)); + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index e5a2d92b40..86f48c42bb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -19,7 +19,6 @@ class Http extends Service foreach ([ LegacyRegistry::class, TablesDBRegistry::class, - TransactionsRegistry::class, ] as $registrar) { new $registrar($this); } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php index 9db5e00f8f..3286aecf93 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php @@ -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()); + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php index 11be613629..4a02ac684e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/TablesDB.php @@ -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()); + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php deleted file mode 100644 index b41d6adcdb..0000000000 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Transactions.php +++ /dev/null @@ -1,27 +0,0 @@ -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()); - } -} diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 594648db3e..ec2d4f0d49 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -11,7 +11,6 @@ class Operation extends Validator /** @var array */ 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; } diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/Legacy/Permissions/DatabasesPermissionsGuestTest.php similarity index 99% rename from tests/e2e/Services/Databases/Legacy/DatabasesPermissionsGuestTest.php rename to tests/e2e/Services/Databases/Legacy/Permissions/DatabasesPermissionsGuestTest.php index abeef6c222..6496aa285a 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/Legacy/Permissions/DatabasesPermissionsGuestTest.php @@ -1,6 +1,6 @@ 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']); + } +} diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php new file mode 100644 index 0000000000..7d19a65648 --- /dev/null +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -0,0 +1,3638 @@ +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' => 'TransactionTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test creating a transaction with default TTL + $response = $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, $response['headers']['status-code']); + $this->assertArrayHasKey('$id', $response['body']); + $this->assertArrayHasKey('status', $response['body']); + $this->assertArrayHasKey('operations', $response['body']); + $this->assertArrayHasKey('expiresAt', $response['body']); + $this->assertEquals('pending', $response['body']['status']); + $this->assertEquals(0, $response['body']['operations']); + + $transactionId1 = $response['body']['$id']; + + // Test creating a transaction with custom TTL + $response = $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'] + ]), [ + 'ttl' => 900 + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals('pending', $response['body']['status']); + + $expiresAt = new \DateTime($response['body']['expiresAt']); + $now = new \DateTime(); + $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); + $this->assertGreaterThan(800, $diff); + $this->assertLessThan(1000, $diff); + + $transactionId2 = $response['body']['$id']; + + // Test invalid TTL values + $response = $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'] + ]), [ + 'ttl' => 30 // Below minimum + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $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'] + ]), [ + 'ttl' => 4000 // Above maximum + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * Test adding operations to a transaction + */ + public function testAddOperations(): void + { + // Create database first + $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' => 'TransactionOperationsTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // 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']); + $transactionId = $transaction['body']['$id']; + + // Create a collection for testing + $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' => 'TransactionOperationsTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $collectionId = $collection['body']['$id']; + + // Add columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $column['headers']['status-code']); + + // Wait for column to be created + sleep(2); + + // Add valid operations + $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' => 'doc1', + 'data' => [ + 'name' => 'Test Document 1' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc2', + 'data' => [ + 'name' => 'Test Document 2' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['operations']); + + // Test adding more operations + $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' => 'update', + 'documentId' => 'doc1', + 'data' => [ + 'name' => 'Updated Document 1' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(3, $response['body']['operations']); + + // Test invalid database ID + $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' => 'invalid_database', + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code'], 'Invalid database should return 404. Got: ' . json_encode($response['body'])); + + // Test invalid collection ID + $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' => 'invalid_collection', + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * Test committing a transaction + */ + public function testCommit(): void + { + // Create database first + $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' => 'TransactionCommitTestDB' + ]); + + $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' => 'TransactionCommitTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $collectionId = $collection['body']['$id']; + + // Add columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $column['headers']['status-code']); + sleep(2); + + // 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']); + $transactionId = $transaction['body']['$id']; + + // Add operations + $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' => 'doc1', + 'data' => [ + 'name' => 'Test Document 1' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc2', + 'data' => [ + 'name' => 'Test Document 2' + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'update', + 'documentId' => 'doc1', + 'data' => [ + 'name' => 'Updated Document 1' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(3, $response['body']['operations']); + + // Commit the 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']); + $this->assertEquals('committed', $response['body']['status']); + + // Verify 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(200, $documents['headers']['status-code']); + $this->assertEquals(2, $documents['body']['total']); + + // Verify the update was applied + $doc1Found = false; + foreach ($documents['body']['documents'] as $doc) { + if ($doc['$id'] === 'doc1') { + $this->assertEquals('Updated Document 1', $doc['name']); + $doc1Found = true; + } + } + $this->assertTrue($doc1Found, 'Document doc1 should exist with updated name'); + + // Test committing already committed 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(400, $response['headers']['status-code']); + } + + /** + * Test rolling back a transaction + */ + public function testRollback(): void + { + // Create database first + $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' => 'TransactionRollbackTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // 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']); + $transactionId = $transaction['body']['$id']; + + // Create a collection for rollback test + $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' => 'TransactionRollbackTest', + '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' => 'value', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // Add operations + $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' => 'rollback_doc', + 'data' => [ + 'value' => 'Should not exist' + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Rollback the 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'] + ]), [ + 'rollback' => true + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('rolledBack', $response['body']['status']); + + // 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(200, $documents['headers']['status-code']); + $this->assertEquals(0, $documents['body']['total']); + } + + /** + * Test transaction expiration + */ + public function testTransactionExpiration(): void + { + // Create database and collection + $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' => 'ExpirationTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => false, + ]); + + sleep(2); + + // Create transaction with minimum TTL (60 seconds) + $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'] + ]), [ + 'ttl' => 60 + ]); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Add operation + $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' => ['data' => 'Should expire'] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Verify transaction was created with correct expiration + $txnDetails = $this->client->call(Client::METHOD_GET, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $txnDetails['headers']['status-code']); + $this->assertEquals('pending', $txnDetails['body']['status']); + + // Verify expiration time is approximately 60 seconds from now + $expiresAt = new \DateTime($txnDetails['body']['expiresAt']); + $now = new \DateTime(); + $diff = $expiresAt->getTimestamp() - $now->getTimestamp(); + $this->assertGreaterThan(55, $diff); + $this->assertLessThan(65, $diff); + } + + /** + * Test maximum operations per transaction + */ + public function testTransactionSizeLimit(): void + { + // Create database and collection + $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' => 'SizeLimitTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [Permission::create(Role::any())], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => 'value', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // 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']; + + // Try to add operations exceeding the limit (assuming limit is 100) + // We'll add 50 operations twice to test incremental limit + $operations = []; + for ($i = 0; $i < 50; $i++) { + $operations[] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => 'doc_' . $i, + 'data' => ['value' => 'Test ' . $i] + ]; + } + + // First batch should succeed + $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' => $operations + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(50, $response['body']['operations']); + + // Second batch of 50 more operations + $operations = []; + for ($i = 50; $i < 100; $i++) { + $operations[] = [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => 'doc_' . $i, + 'action' => 'create', + 'data' => ['value' => 'Test ' . $i] + ]; + } + + $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' => $operations + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(100, $response['body']['operations']); + + // Try to add one more operation - should fail + $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' => 'doc_overflow', + 'data' => ['value' => 'This should fail'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * Test concurrent transactions with conflicting operations + */ + public function testConcurrentTransactionConflicts(): void + { + // Create database and collection + $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' => 'ConflictTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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 + $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'shared_doc', + 'data' => ['counter' => 100] + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + + // Create two transactions + $txn1 = $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'] + ])); + + $txn2 = $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'] + ])); + + $transactionId1 = $txn1['body']['$id']; + $transactionId2 = $txn2['body']['$id']; + + // Both transactions try to update the same document + $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, + 'action' => 'update', + 'documentId' => 'shared_doc', + 'data' => ['counter' => 200] + ] + ] + ]); + + $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, + 'action' => 'update', + 'documentId' => 'shared_doc', + 'data' => ['counter' => 300] + ] + ] + ]); + + // 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 - should fail with conflict + $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(409, $response2['headers']['status-code']); // Conflict + + // Verify the document has the value from first transaction + $doc = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/shared_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $doc['body']['counter']); + } + + /** + * Test deleting a document that's being updated in a transaction + */ + public function testDeleteDocumentDuringTransaction(): void + { + // Create database and collection + $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' => 'DeleteConflictDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => false, + ]); + + sleep(2); + + // Create document + $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'target_doc', + 'data' => ['data' => 'Original'] + ]); + + $this->assertEquals(201, $doc['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'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add update operation to transaction + $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' => 'update', + 'documentId' => 'target_doc', + 'data' => ['data' => 'Updated in transaction'] + ] + ] + ]); + + // Delete the document outside of transaction + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/target_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Try to commit transaction - should fail because document no longer exists + $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(409, $response['headers']['status-code']); // Conflict + } + + /** + * Test bulk operations in transactions + */ + public function testBulkOperations(): void + { + // Create database and collection + $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' => 'BulkOpsDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'category', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); + + // Create some initial documents + for ($i = 1; $i <= 5; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_' . $i, + 'data' => [ + 'name' => 'Existing ' . $i, + 'category' => 'old' + ] + ]); + } + + // 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 bulk operations + $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' => [ + // Bulk create + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkCreate', + 'data' => [ + ['$id' => 'bulk_1', 'name' => 'Bulk 1', 'category' => 'new'], + ['$id' => 'bulk_2', 'name' => 'Bulk 2', 'category' => 'new'], + ['$id' => 'bulk_3', 'name' => 'Bulk 3', 'category' => 'new'], + ] + ], + // Bulk update + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkUpdate', + 'data' => [ + 'queries' => [Query::equal('category', ['old'])->toString()], + 'data' => ['category' => 'updated'] + ] + ], + // Bulk delete + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => [Query::equal('name', ['Existing 5'])->toString()] + ] + ] + ] + ]); + + $this->assertEquals(201, $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 results + $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 7 documents (5 existing - 1 deleted + 3 new) + $this->assertEquals(7, $documents['body']['total']); + + // Check categories were updated + $oldCategoryCount = 0; + $updatedCategoryCount = 0; + $newCategoryCount = 0; + + foreach ($documents['body']['documents'] as $doc) { + switch ($doc['category']) { + case 'old': + $oldCategoryCount++; + break; + case 'updated': + $updatedCategoryCount++; + break; + case 'new': + $newCategoryCount++; + break; + } + } + + $this->assertEquals(0, $oldCategoryCount); + $this->assertEquals(4, $updatedCategoryCount); // 4 existing docs updated + $this->assertEquals(3, $newCategoryCount); // 3 new docs + } + + /** + * Test transaction with mixed success and failure operations + */ + public function testPartialFailureRollback(): void + { + // Create database and collection + $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' => 'PartialFailureDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns with constraints + $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, + ]); + + sleep(2); + + // Create unique index on email + $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' => 'unique', + 'columns' => ['email'], + ]); + + sleep(2); + + // Create an existing document + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => ID::unique(), + 'data' => ['email' => 'existing@example.com'] + ]); + + // 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 - mix of valid and invalid + $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' => 'valid1@example.com'] // Valid + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['email' => 'valid2@example.com'] // Valid + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['email' => 'existing@example.com'] // Will fail - duplicate + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'create', + 'documentId' => ID::unique(), + 'data' => ['email' => 'valid3@example.com'] // Would be valid but should rollback + ], + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Try to commit - should fail and rollback all operations + $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(409, $response['headers']['status-code']); // Conflict due to duplicate + + // 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 double commit/rollback attempts + */ + public function testDoubleCommitRollback(): void + { + // Create database and collection + $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' => 'DoubleCommitDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [Permission::create(Role::any())], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => false, + ]); + + sleep(2); + + // Test double commit + $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 operation + $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' => ['data' => 'Test'] + ] + ] + ]); + + // First commit + $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']); + + // Second commit attempt - should fail + $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(400, $response['headers']['status-code']); // Bad request - already committed + + // Test double rollback + $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'] + ])); + + $transactionId2 = $transaction2['body']['$id']; + + // First rollback + $response = $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'] + ]), [ + 'rollback' => true + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Second rollback attempt - should fail + $response = $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'] + ]), [ + 'rollback' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); // Bad request - already rolled back + } + + /** + * Test operations on non-existent documents + */ + public function testOperationsOnNonExistentDocuments(): void + { + // Create database and collection + $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' => 'NonExistentDocDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => false, + ]); + + sleep(2); + + // 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']; + + // Try to update non-existent document + $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' => 'update', + 'documentId' => 'non_existent_doc', + 'data' => ['data' => 'Should fail'] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); // Operation added + + // Commit should fail + $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(404, $response['headers']['status-code']); // Document not found + + // Test delete non-existent document + $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'] + ])); + + $transactionId2 = $transaction2['body']['$id']; + + $response = $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, + 'action' => 'delete', + 'documentId' => 'non_existent_doc', + 'data' => [] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit should fail + $response = $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(404, $response['headers']['status-code']); // Document not found + } + + /** + * Test createDocument with transactionId via normal route + */ + public function testCreateDocument(): void + { + // Create database and collection + $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' => 'WriteRoutesTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $columns = [ + ['key' => 'name', 'type' => 'string', 'size' => 256, 'required' => true], + ['key' => 'counter', 'type' => 'integer', 'required' => false, 'min' => 0, 'max' => 10000], + ['key' => 'category', 'type' => 'string', 'size' => 256, 'required' => false], + ['key' => 'data', 'type' => 'string', 'size' => 256, 'required' => false], + ]; + + foreach ($columns as $attr) { + $type = $attr['type']; + unset($attr['type']); + + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/{$type}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), $attr); + + $this->assertEquals(202, $response['headers']['status-code']); + } + + 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'] + ])); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Create document via normal route with transactionId + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_from_route', + 'data' => [ + 'name' => 'Created via normal route', + 'counter' => 100, + 'category' => 'test' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Document should not exist outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_from_route", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $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']); + + // Document should now exist + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_from_route", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Created via normal route', $response['body']['name']); + } + + /** + * Test updateDocument with transactionId via normal route + */ + public function testUpdateDocument(): void + { + // Create database and collection + $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' => 'UpdateRouteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => false, + 'min' => 0, + 'max' => 10000, + ]); + + $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' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create document outside transaction + $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_to_update', + 'data' => [ + 'name' => 'Original name', + 'counter' => 50, + 'category' => 'original' + ] + ]); + + $this->assertEquals(201, $doc['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'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Update document via normal route with transactionId + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_update", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'name' => 'Updated via normal route', + 'counter' => 150, + 'category' => 'updated' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Document should still have original values outside transaction + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_update", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Original name', $response['body']['name']); + $this->assertEquals(50, $response['body']['counter']); + + // 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']); + + // Document should now have updated values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_update", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Updated via normal route', $response['body']['name']); + $this->assertEquals(150, $response['body']['counter']); + } + + /** + * Test upsertDocument with transactionId via normal route + */ + public function testUpsertDocument(): void + { + // Create database and collection + $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' => 'UpsertRouteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => false, + 'min' => 0, + 'max' => 10000, + ]); + + 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']; + + // Upsert document (create) via normal route with transactionId + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_upsert', + 'data' => [ + 'name' => 'Created by upsert', + 'counter' => 25 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Document should not exist outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Upsert same document (update) in same transaction + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_upsert', + 'data' => [ + 'name' => 'Updated by upsert', + 'counter' => 75 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); // Upsert in transaction returns 201 + + // 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']); + + // Document should now exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Updated by upsert', $response['body']['name']); + $this->assertEquals(75, $response['body']['counter']); + } + + /** + * Test deleteDocument with transactionId via normal route + */ + public function testDeleteDocument(): void + { + // Create database and collection + $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' => 'DeleteRouteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // Create document outside transaction + $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_to_delete', + 'data' => ['name' => 'Will be deleted'] + ]); + + $this->assertEquals(201, $doc['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'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Delete document via normal route with transactionId + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_delete", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'transactionId' => $transactionId + ]); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Document should still exist outside transaction + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_delete", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $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']); + + // Document should no longer exist + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_delete", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + } + + /** + * Test bulkCreate with transactionId via normal route + */ + public function testBulkCreate(): void + { + // Create database and collection + $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' => 'BulkCreateTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'category', + 'size' => 256, + 'required' => false, + ]); + + 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']; + + // Bulk create via normal route with transactionId + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documents' => [ + [ + '$id' => 'bulk_create_1', + 'name' => 'Bulk created 1', + 'category' => 'bulk_created' + ], + [ + '$id' => 'bulk_create_2', + 'name' => 'Bulk created 2', + 'category' => 'bulk_created' + ], + [ + '$id' => 'bulk_create_3', + 'name' => 'Bulk created 3', + 'category' => 'bulk_created' + ] + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); // Bulk operations return 200 + + // Documents should not exist outside transaction yet + $response = $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()), [ + 'queries' => [Query::equal('category', ['bulk_created'])->toString()] + ]); + + $this->assertEquals(0, $response['body']['total']); + + // Individual document check + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_create_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $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']); + + // Documents should now exist + $response = $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()), [ + 'queries' => [Query::equal('category', ['bulk_created'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + + // Verify individual documents + for ($i = 1; $i <= 3; $i++) { + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_create_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals("Bulk created {$i}", $response['body']['name']); + $this->assertEquals('bulk_created', $response['body']['category']); + } + } + + /** + * Test bulkUpdate with transactionId via normal route + */ + public function testBulkUpdate(): void + { + // Create database and collection + $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' => 'BulkUpdateTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create documents for bulk testing + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'bulk_update_' . $i, + 'data' => [ + 'name' => 'Bulk doc ' . $i, + 'category' => 'bulk_test' + ] + ]); + } + + // 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']; + + // Bulk update via normal route with transactionId + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('category', ['bulk_test'])->toString()], + 'data' => ['category' => 'bulk_updated'], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Documents should still have original category outside transaction + $response = $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()), [ + 'queries' => [Query::equal('category', ['bulk_test'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + + // 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']); + + // Documents should now have updated category + $response = $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()), [ + 'queries' => [Query::equal('category', ['bulk_updated'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + } + + /** + * Test bulkUpsert with transactionId via normal route + */ + public function testBulkUpsert(): void + { + // Create database and collection + $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' => 'BulkUpsertTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => false, + 'min' => 0, + 'max' => 10000, + ]); + + sleep(3); + + // Create one document outside transaction + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'bulk_upsert_existing', + 'data' => [ + 'name' => 'Existing doc', + 'counter' => 10 + ] + ]); + + // 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']; + + // Bulk upsert via normal route with transactionId (updates existing, creates new) + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documents' => [ + [ + '$id' => 'bulk_upsert_existing', + 'name' => 'Updated existing', + 'counter' => 20 + ], + [ + '$id' => 'bulk_upsert_new', + 'name' => 'New doc', + 'counter' => 30 + ] + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Original document should be unchanged, new document shouldn't exist outside transaction + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_existing", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Existing doc', $response['body']['name']); + $this->assertEquals(10, $response['body']['counter']); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_new", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $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']); + + // Check both documents exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_existing", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Updated existing', $response['body']['name']); + $this->assertEquals(20, $response['body']['counter']); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_new", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('New doc', $response['body']['name']); + $this->assertEquals(30, $response['body']['counter']); + } + + /** + * Test bulkDelete with transactionId via normal route + */ + public function testBulkDelete(): void + { + // Create database and collection + $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' => 'BulkDeleteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'category', + 'size' => 256, + 'required' => false, + ]); + + sleep(3); + + // Create documents for bulk testing + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'bulk_delete_' . $i, + 'data' => [ + 'name' => 'Delete doc ' . $i, + 'category' => 'bulk_delete_test' + ] + ]); + } + + // 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']; + + // Bulk delete via normal route with transactionId + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('category', ['bulk_delete_test'])->toString()], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); // Bulk delete with transaction returns 200 + + // Documents should still exist outside transaction + $response = $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()), [ + 'queries' => [Query::equal('category', ['bulk_delete_test'])->toString()] + ]); + + $this->assertEquals(3, $response['body']['total']); + + // 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']); + + // Documents should now be deleted + $response = $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()), [ + 'queries' => [Query::equal('category', ['bulk_delete_test'])->toString()] + ]); + + $this->assertEquals(0, $response['body']['total']); + } + + /** + * Test multiple single route operations in one transaction + */ + public function testMixedSingleOperations(): void + { + // Create database and collection + $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' => 'MultipleSingleRoutesDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'status', + 'size' => 256, + 'required' => false, + ]); + + $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' => 'priority', + 'required' => false, + 'min' => 1, + 'max' => 10, + ]); + + sleep(3); + + // Create an existing document outside transaction for testing + $existingDoc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_doc', + 'data' => [ + 'name' => 'Existing Document', + 'status' => 'active', + 'priority' => 5 + ] + ]); + + $this->assertEquals(201, $existingDoc['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'] + ])); + + $transactionId = $transaction['body']['$id']; + $this->assertEquals(201, $transaction['headers']['status-code']); + + // 1. Create new document via normal route with transactionId + $response1 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'new_doc_1', + 'data' => [ + 'name' => 'New Document 1', + 'status' => 'pending', + 'priority' => 1 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response1['headers']['status-code']); + + // 2. Create another document via normal route with transactionId + $response2 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'new_doc_2', + 'data' => [ + 'name' => 'New Document 2', + 'status' => 'pending', + 'priority' => 2 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response2['headers']['status-code']); + + // 3. Update existing document via normal route with transactionId + $response3 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'updated', + 'priority' => 10 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response3['headers']['status-code']); + + // 4. Update the first new document (created in same transaction) + $response4 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'active', + 'priority' => 8 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response4['headers']['status-code']); + + // 5. Delete the second new document (created in same transaction) + $response5 = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'transactionId' => $transactionId + ]); + + $this->assertEquals(204, $response5['headers']['status-code']); + + // 6. Upsert a new document via normal route with transactionId + $response6 = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/upserted_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'upserted_doc', + 'data' => [ + 'name' => 'Upserted Document', + 'status' => 'new', + 'priority' => 3 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response6['headers']['status-code']); + + // Check transaction has correct number of operations + $txnDetails = $this->client->call(Client::METHOD_GET, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $txnDetails['headers']['status-code']); + $this->assertEquals(6, $txnDetails['body']['operations']); // 6 operations total + + // Verify nothing exists outside transaction yet + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/upserted_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Existing doc should still have original values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('active', $response['body']['status']); + $this->assertEquals(5, $response['body']['priority']); + + // 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']); + $this->assertEquals('committed', $response['body']['status']); + + // Verify final state after commit + // new_doc_1 should exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('New Document 1', $response['body']['name']); + $this->assertEquals('active', $response['body']['status']); + $this->assertEquals(8, $response['body']['priority']); + + // new_doc_2 should not exist (was deleted in transaction) + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + // existing_doc should have updated values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('updated', $response['body']['status']); + $this->assertEquals(10, $response['body']['priority']); + + // upserted_doc should exist + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/upserted_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Upserted Document', $response['body']['name']); + $this->assertEquals('new', $response['body']['status']); + $this->assertEquals(3, $response['body']['priority']); + + // Verify total document 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(3, $documents['body']['total']); // existing_doc, new_doc_1, upserted_doc + } + + /** + * Test mixed operations with transactions + */ + public function testMixedOperations(): void + { + // Create database and collection + $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' => 'MixedOpsTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create 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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + // 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 operation via Operations\Add endpoint + $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' => 'mixed_doc1', + 'data' => ['name' => 'Via Operations Add'] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['operations']); + + // Add operation via normal route with transactionId + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'mixed_doc2', + 'data' => ['name' => 'Via normal route'], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Check transaction now has 2 operations + $txnDetails = $this->client->call(Client::METHOD_GET, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(2, $txnDetails['body']['operations']); + + // Both documents shouldn't exist yet + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $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']); + + // Both documents should now exist + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Via Operations Add', $response['body']['name']); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('Via normal route', $response['body']['name']); + } + + /** + * Test bulk update with queries that should match documents created in the same transaction + */ + public function testBulkUpdateWithTransactionAwareQueries(): void + { + // Create database and collection + $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' => 'BulkTxnAwareDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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, + ]); + + $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' => 'status', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); // Wait for columns to be created + + // Create some existing documents + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_' . $i, + 'data' => [ + 'name' => 'Existing ' . $i, + 'age' => 20 + $i, + 'status' => 'inactive' + ] + ]); + } + + // 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']; + + // Step 1: Create new documents with age > 25 in transaction + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'txn_doc_1', + 'data' => [ + 'name' => 'Transaction Doc 1', + 'age' => 30, + 'status' => 'inactive' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'txn_doc_2', + 'data' => [ + 'name' => 'Transaction Doc 2', + 'age' => 35, + 'status' => 'inactive' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Step 2: Bulk update all documents with age > 25 to have status 'active' + // This should match both existing_3 (age=23 doesn't match, age=24 doesn't match, but existing documents have age 21,22,23) + // Wait, let me fix the ages - existing docs have ages 21, 22, 23, so only txn docs should match + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'active' + ], + 'queries' => [Query::greaterThan('age', 25)->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 that documents created in the transaction were updated by the bulk update + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/txn_doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('active', $response['body']['status'], 'Document created in transaction should be updated by bulk update query'); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/txn_doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('active', $response['body']['status'], 'Document created in transaction should be updated by bulk update query'); + + // Verify existing documents were not affected + for ($i = 1; $i <= 3; $i++) { + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('inactive', $response['body']['status'], "Existing document {$i} should remain inactive (age <= 25)"); + } + } + + /** + * Test bulk update with queries that should match documents updated in the same transaction + */ + public function testBulkUpdateMatchingUpdatedDocuments(): void + { + // Create database and collection + $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' => 'BulkUpdateTxnDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'category', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'priority', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); // Wait for columns to be created + + // Create existing documents + for ($i = 1; $i <= 4; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_' . $i, + 'data' => [ + 'name' => 'Document ' . $i, + 'category' => 'normal', + 'priority' => 'low' + ] + ]); + } + + // 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']; + + // Step 1: Update some documents to have category 'special' in transaction + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'category' => 'special' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'category' => 'special' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Step 2: Bulk update all documents with category 'special' to have priority 'high' + // This should match the documents we just updated in the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'priority' => 'high' + ], + 'queries' => [Query::equal('category', ['special'])->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 that the updated documents were matched by bulk update + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('special', $response['body']['category']); + $this->assertEquals('high', $response['body']['priority'], 'Document updated in transaction should be matched by bulk update query'); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('special', $response['body']['category']); + $this->assertEquals('high', $response['body']['priority'], 'Document updated in transaction should be matched by bulk update query'); + + // Verify other documents were not affected + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_3", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('normal', $response['body']['category']); + $this->assertEquals('low', $response['body']['priority']); + } + + /** + * Test bulk delete with queries that should match documents created in the same transaction + */ + public function testBulkDeleteMatchingCreatedDocuments(): void + { + // Create database and collection + $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' => 'BulkDeleteTxnDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'type', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); // Wait for columns to be created + + // Create existing documents + for ($i = 1; $i <= 3; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'existing_' . $i, + 'data' => [ + 'name' => 'Existing ' . $i, + 'type' => 'permanent' + ] + ]); + } + + // 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']; + + // Step 1: Create temporary documents in transaction + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'temp_1', + 'data' => [ + 'name' => 'Temporary 1', + 'type' => 'temporary' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'temp_2', + 'data' => [ + 'name' => 'Temporary 2', + 'type' => 'temporary' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Step 2: Bulk delete all documents with type 'temporary' + // This should delete the documents we just created in the transaction + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('type', ['temporary'])->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 temporary documents were deleted (should not exist) + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/temp_1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Temporary document created and deleted in transaction should not exist'); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/temp_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Temporary document created and deleted in transaction should not exist'); + + // Verify existing documents were not affected + for ($i = 1; $i <= 3; $i++) { + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code'], "Permanent document {$i} should still exist"); + $this->assertEquals('permanent', $response['body']['type']); + } + } + + /** + * Test bulk delete with queries that should match documents updated in the same transaction + */ + public function testBulkDeleteMatchingUpdatedDocuments(): void + { + // Create database and collection + $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' => 'BulkDeleteUpdateTxnDB' + ]); + + $databaseId = $database['body']['$id']; + + $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' => 'TestCollection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $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' => 'status', + 'size' => 256, + 'required' => true, + ]); + + sleep(3); // Wait for columns to be created + + // Create existing documents + for ($i = 1; $i <= 5; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'doc_' . $i, + 'data' => [ + 'name' => 'Document ' . $i, + 'status' => 'active' + ] + ]); + } + + // 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']; + + // Step 1: Mark some documents for deletion by updating their status + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'marked_for_deletion' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_4", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'status' => 'marked_for_deletion' + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Step 2: Bulk delete all documents with status 'marked_for_deletion' + // This should delete the documents we just updated in the transaction + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'queries' => [Query::equal('status', ['marked_for_deletion'])->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 marked documents were deleted + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Document marked for deletion should have been deleted'); + + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_4", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $response['headers']['status-code'], 'Document marked for deletion should have been deleted'); + + // Verify other documents still exist + foreach ([1, 3, 5] as $i) { + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_{$i}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code'], "Document {$i} should still exist"); + $this->assertEquals('active', $response['body']['status']); + } + } +} From 95f49559d08eb50b06267f5cd3ee805336c50ed4 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 Sep 2025 12:43:37 +0300 Subject: [PATCH 085/274] add finally --- src/Appwrite/Platform/Workers/StatsResources.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index da8c086bf4..655ebfbd52 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -206,6 +206,8 @@ class StatsResources extends Action $this->writeDocuments($dbForLogs, $project); } catch (Throwable $th) { call_user_func_array($this->logError, [$th, "StatsResources", "count_for_project_{$project->getId()}"]); + } finally { + $this->documents = []; } Console::info('End of count for: ' . $project->getId()); From de9e177bc84cb5ddc1a31fb10ede3d5260161e6d Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 Sep 2025 17:23:33 +0300 Subject: [PATCH 086/274] sort and hardcode bachSize --- .../Platform/Workers/StatsResources.php | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 655ebfbd52..d26f4c06f3 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -206,8 +206,6 @@ class StatsResources extends Action $this->writeDocuments($dbForLogs, $project); } catch (Throwable $th) { call_user_func_array($this->logError, [$th, "StatsResources", "count_for_project_{$project->getId()}"]); - } finally { - $this->documents = []; } Console::info('End of count for: ' . $project->getId()); @@ -436,18 +434,32 @@ class StatsResources extends Action { $message = 'Stats writeDocuments project: ' . $project->getId() . '(' . $project->getSequence() . ')'; + // sort by unique index key to make + usort($this->documents, function($a, $b) { + // metric DESC + $cmp = strcmp($b['metric'], $a['metric']); + if ($cmp !== 0) return $cmp; + + // period ASC + $cmp = strcmp($a['period'], $b['period']); + if ($cmp !== 0) return $cmp; + + // time ASC + if ($a['time'] === $b['time']) return 0; + return ($a['time'] < $b['time']) ? -1 : 1; + }); + try { $dbForLogs->createOrUpdateDocuments( 'stats', - $this->documents + $this->documents, + 10 // See if this make an effect ); Console::success($message . ' | Documents: ' . count($this->documents)); } catch (\Throwable $e) { Console::error('Error: ' . $message . ' | Exception: ' . $e->getMessage()); throw $e; - } finally { - $this->documents = []; } } } From 494fe274b08f3ee3e5c5856aa7598f251149c891 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 Sep 2025 17:43:37 +0300 Subject: [PATCH 087/274] sort as strings --- src/Appwrite/Platform/Workers/StatsResources.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index d26f4c06f3..932b5072f9 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -444,9 +444,8 @@ class StatsResources extends Action $cmp = strcmp($a['period'], $b['period']); if ($cmp !== 0) return $cmp; - // time ASC - if ($a['time'] === $b['time']) return 0; - return ($a['time'] < $b['time']) ? -1 : 1; + // time ASC (string comparison is fine) + return strcmp($a['time'], $b['time']); }); try { From b44051e0a2377a8a1647096adf3213cfea65e30f Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 Sep 2025 17:48:58 +0300 Subject: [PATCH 088/274] Respect time nulls --- src/Appwrite/Platform/Workers/StatsResources.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 932b5072f9..f7ee999a94 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -434,7 +434,7 @@ class StatsResources extends Action { $message = 'Stats writeDocuments project: ' . $project->getId() . '(' . $project->getSequence() . ')'; - // sort by unique index key to make + // sort by unique index key reduce locks usort($this->documents, function($a, $b) { // metric DESC $cmp = strcmp($b['metric'], $a['metric']); @@ -444,10 +444,15 @@ class StatsResources extends Action $cmp = strcmp($a['period'], $b['period']); if ($cmp !== 0) return $cmp; - // time ASC (string comparison is fine) + // time ASC, NULLs first + if ($a['time'] === null && $b['time'] === null) return 0; + if ($a['time'] === null) return -1; + if ($b['time'] === null) return 1; + return strcmp($a['time'], $b['time']); }); +var_dump($this->documents); try { $dbForLogs->createOrUpdateDocuments( 'stats', From 6ed4590e0f2b522188fb85cf1b5e0e9c2e0fd248 Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 10 Sep 2025 18:04:35 +0300 Subject: [PATCH 089/274] Respect time nulls --- .../Platform/Workers/StatsResources.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index f7ee999a94..daecabf16a 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -434,25 +434,26 @@ class StatsResources extends Action { $message = 'Stats writeDocuments project: ' . $project->getId() . '(' . $project->getSequence() . ')'; - // sort by unique index key reduce locks - usort($this->documents, function($a, $b) { - // metric DESC + /** + * sort by unique index key reduce locks/deadlocks + */ + usort($this->documents, function ($a, $b) { + // Metric DESC $cmp = strcmp($b['metric'], $a['metric']); if ($cmp !== 0) return $cmp; - // period ASC + // Period ASC $cmp = strcmp($a['period'], $b['period']); if ($cmp !== 0) return $cmp; - // time ASC, NULLs first - if ($a['time'] === null && $b['time'] === null) return 0; - if ($a['time'] === null) return -1; + // Time ASC, NULLs first + if ($a['time'] === null) return ($b['time'] === null) ? 0 : -1; if ($b['time'] === null) return 1; return strcmp($a['time'], $b['time']); }); -var_dump($this->documents); + var_dump($this->documents); try { $dbForLogs->createOrUpdateDocuments( 'stats', From 28a2b577e74af4c945ef26f80080c0b987c822df Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 16:36:09 +1200 Subject: [PATCH 090/274] Update lock --- composer.json | 2 +- composer.lock | 72 +++++++++---------- .../Modules/Databases/Services/Http.php | 1 - 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/composer.json b/composer.json index 9042cb088f..1dc7288441 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "2.*", + "utopia-php/database": "1.*", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", "utopia-php/dns": "0.3.*", diff --git a/composer.lock b/composer.lock index e134913475..f82ba2aced 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3565fcc2471b5d18a159b6da1c8fad31", + "content-hash": "7553e976312b0423cc31544abb91caec", "packages": [ { "name": "adhocore/jwt", @@ -3296,16 +3296,16 @@ }, { "name": "utopia-php/abuse", - "version": "1.0.1", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a" + "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd591568791556d246d901d6aaf9935ab02c3f9a", - "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/c5e2232033b507a07f72180dc56d37e1872ee7be", + "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be", "shasum": "" }, "require": { @@ -3313,7 +3313,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "2.*" + "utopia-php/database": "1.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3341,9 +3341,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/1.0.1" + "source": "https://github.com/utopia-php/abuse/tree/1.0.0" }, - "time": "2025-09-04T12:46:54+00:00" + "time": "2025-08-13T09:12:54+00:00" }, { "name": "utopia-php/analytics", @@ -3393,21 +3393,21 @@ }, { "name": "utopia-php/audit", - "version": "1.0.1", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22" + "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", - "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/c0ed75f4d068f1f6c2e7149a909490d4214e72bb", + "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "2.*" + "utopia-php/database": "1.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3434,9 +3434,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/1.0.1" + "source": "https://github.com/utopia-php/audit/tree/1.0.0" }, - "time": "2025-09-04T12:46:43+00:00" + "time": "2025-08-13T09:09:00+00:00" }, { "name": "utopia-php/cache", @@ -3638,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "2.0.0", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5" + "reference": "1bac5c66567cd0718b4c133c8550b3c6076ff908" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/e4a03ba543abc4e436ec1b450750a14bd36011d5", - "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1bac5c66567cd0718b4c133c8550b3c6076ff908", + "reference": "1bac5c66567cd0718b4c133c8550b3c6076ff908", "shasum": "" }, "require": { @@ -3688,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.0.0" + "source": "https://github.com/utopia-php/database/tree/1.4.5" }, - "time": "2025-09-04T12:36:53+00:00" + "time": "2025-09-11T03:10:29+00:00" }, { "name": "utopia-php/detector", @@ -4190,16 +4190,16 @@ }, { "name": "utopia-php/migration", - "version": "1.0.2", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "eb60a61934be1d6f2f4fdabd9903a841ba078bc9" + "reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/eb60a61934be1d6f2f4fdabd9903a841ba078bc9", - "reference": "eb60a61934be1d6f2f4fdabd9903a841ba078bc9", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/c42935a6a4ee3701c68d24244e82ecb39e945ec4", + "reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4", "shasum": "" }, "require": { @@ -4207,7 +4207,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "2.*", + "utopia-php/database": "1.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4240,9 +4240,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.0.2" + "source": "https://github.com/utopia-php/migration/tree/1.1.1" }, - "time": "2025-09-04T12:47:05+00:00" + "time": "2025-09-10T06:17:20+00:00" }, { "name": "utopia-php/orchestration", @@ -5007,16 +5007,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.3.2", + "version": "1.3.4", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "375a6c9b168db6fdf58fbe0d49d2261d80700b4a" + "reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/375a6c9b168db6fdf58fbe0d49d2261d80700b4a", - "reference": "375a6c9b168db6fdf58fbe0d49d2261d80700b4a", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac", + "reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac", "shasum": "" }, "require": { @@ -5052,9 +5052,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.3.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.3.4" }, - "time": "2025-09-05T15:50:35+00:00" + "time": "2025-09-08T11:56:04+00:00" }, { "name": "doctrine/annotations", @@ -8512,7 +8512,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8536,5 +8536,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Http.php b/src/Appwrite/Platform/Modules/Databases/Services/Http.php index 86f48c42bb..f683f537bc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Http.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Databases\Services; use Appwrite\Platform\Modules\Databases\Http\Init\Timeout; use Appwrite\Platform\Modules\Databases\Services\Registry\Legacy as LegacyRegistry; use Appwrite\Platform\Modules\Databases\Services\Registry\TablesDB as TablesDBRegistry; -use Appwrite\Platform\Modules\Databases\Services\Registry\Transactions as TransactionsRegistry; use Utopia\Platform\Service; class Http extends Service From f9b92ddead46751765a7f8316aafdc60425da9df Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 16:37:43 +1200 Subject: [PATCH 091/274] Fix namespace --- tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php | 2 +- .../Databases/TablesDB/Transactions/TransactionsTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php index 90282568e4..89e095cb53 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php @@ -1,6 +1,6 @@ Date: Thu, 11 Sep 2025 17:54:55 +1200 Subject: [PATCH 092/274] Legacy DB support --- composer.json | 2 +- composer.lock | 56 +- .../Http/Databases/Collections/Action.php | 4 +- .../Collections/Attributes/Action.php | 4 +- .../Collections/Attributes/Boolean/Create.php | 4 +- .../Collections/Attributes/Boolean/Update.php | 4 +- .../Attributes/Datetime/Create.php | 4 +- .../Attributes/Datetime/Update.php | 4 +- .../Collections/Attributes/Delete.php | 4 +- .../Collections/Attributes/Email/Create.php | 4 +- .../Collections/Attributes/Email/Update.php | 4 +- .../Collections/Attributes/Enum/Create.php | 4 +- .../Collections/Attributes/Enum/Update.php | 4 +- .../Collections/Attributes/Float/Create.php | 4 +- .../Collections/Attributes/Float/Update.php | 4 +- .../Databases/Collections/Attributes/Get.php | 4 +- .../Collections/Attributes/IP/Create.php | 4 +- .../Collections/Attributes/IP/Update.php | 4 +- .../Collections/Attributes/Integer/Create.php | 4 +- .../Collections/Attributes/Integer/Update.php | 4 +- .../Collections/Attributes/Line/Create.php | 4 +- .../Collections/Attributes/Line/Update.php | 4 +- .../Collections/Attributes/Point/Create.php | 4 +- .../Collections/Attributes/Point/Update.php | 4 +- .../Collections/Attributes/Polygon/Create.php | 4 +- .../Collections/Attributes/Polygon/Update.php | 4 +- .../Attributes/Relationship/Create.php | 4 +- .../Attributes/Relationship/Update.php | 4 +- .../Collections/Attributes/String/Create.php | 6 +- .../Collections/Attributes/String/Update.php | 4 +- .../Collections/Attributes/URL/Create.php | 4 +- .../Collections/Attributes/URL/Update.php | 4 +- .../Collections/Attributes/XList.php | 6 +- .../Http/Databases/Collections/Create.php | 2 +- .../Http/Databases/Collections/Delete.php | 2 +- .../Collections/Documents/Action.php | 4 +- .../Documents/Attribute/Decrement.php | 8 +- .../Documents/Attribute/Increment.php | 8 +- .../Collections/Documents/Bulk/Delete.php | 10 +- .../Collections/Documents/Bulk/Update.php | 10 +- .../Collections/Documents/Bulk/Upsert.php | 10 +- .../Collections/Documents/Create.php | 20 +- .../Collections/Documents/Delete.php | 4 +- .../Databases/Collections/Documents/Get.php | 4 +- .../Collections/Documents/Logs/XList.php | 2 +- .../Collections/Documents/Update.php | 4 +- .../Collections/Documents/Upsert.php | 4 +- .../Databases/Collections/Documents/XList.php | 6 +- .../Http/Databases/Collections/Get.php | 2 +- .../Databases/Collections/Indexes/Action.php | 4 +- .../Databases/Collections/Indexes/Create.php | 4 +- .../Databases/Collections/Indexes/Delete.php | 4 +- .../Databases/Collections/Indexes/Get.php | 4 +- .../Databases/Collections/Indexes/XList.php | 4 +- .../Http/Databases/Collections/Logs/XList.php | 2 +- .../Http/Databases/Collections/Update.php | 2 +- .../Http/Databases/Collections/XList.php | 4 +- .../Http/Databases/Transactions/Action.php | 67 ++ .../Http/Databases/Transactions/Create.php | 3 +- .../Http/Databases/Transactions/Delete.php | 3 +- .../Http/Databases/Transactions/Get.php | 3 +- .../Transactions/Operations/Create.php | 10 +- .../Http/Databases/Transactions/Update.php | 16 +- .../Http/Databases/Transactions/XList.php | 3 +- .../Tables/Columns/Boolean/Create.php | 4 +- .../Tables/Columns/Boolean/Update.php | 4 +- .../Tables/Columns/Datetime/Create.php | 4 +- .../Tables/Columns/Datetime/Update.php | 4 +- .../Http/TablesDB/Tables/Columns/Delete.php | 4 +- .../TablesDB/Tables/Columns/Email/Create.php | 4 +- .../TablesDB/Tables/Columns/Email/Update.php | 4 +- .../TablesDB/Tables/Columns/Enum/Create.php | 4 +- .../TablesDB/Tables/Columns/Enum/Update.php | 4 +- .../TablesDB/Tables/Columns/Float/Create.php | 4 +- .../TablesDB/Tables/Columns/Float/Update.php | 4 +- .../Http/TablesDB/Tables/Columns/Get.php | 4 +- .../TablesDB/Tables/Columns/IP/Create.php | 4 +- .../TablesDB/Tables/Columns/IP/Update.php | 4 +- .../Tables/Columns/Integer/Create.php | 4 +- .../Tables/Columns/Integer/Update.php | 4 +- .../TablesDB/Tables/Columns/Line/Create.php | 4 +- .../TablesDB/Tables/Columns/Line/Update.php | 4 +- .../TablesDB/Tables/Columns/Point/Create.php | 4 +- .../TablesDB/Tables/Columns/Point/Update.php | 4 +- .../Tables/Columns/Polygon/Create.php | 4 +- .../Tables/Columns/Polygon/Update.php | 4 +- .../Tables/Columns/Relationship/Create.php | 4 +- .../Tables/Columns/Relationship/Update.php | 4 +- .../TablesDB/Tables/Columns/String/Create.php | 4 +- .../TablesDB/Tables/Columns/String/Update.php | 4 +- .../TablesDB/Tables/Columns/URL/Create.php | 4 +- .../TablesDB/Tables/Columns/URL/Update.php | 4 +- .../Http/TablesDB/Tables/Columns/XList.php | 4 +- .../Databases/Http/TablesDB/Tables/Create.php | 2 +- .../Databases/Http/TablesDB/Tables/Delete.php | 2 +- .../Databases/Http/TablesDB/Tables/Get.php | 2 +- .../Http/TablesDB/Tables/Indexes/Create.php | 4 +- .../Http/TablesDB/Tables/Indexes/Delete.php | 4 +- .../Http/TablesDB/Tables/Indexes/Get.php | 4 +- .../Http/TablesDB/Tables/Indexes/XList.php | 4 +- .../Http/TablesDB/Tables/Logs/XList.php | 4 +- .../Http/TablesDB/Tables/Rows/Bulk/Delete.php | 4 +- .../Http/TablesDB/Tables/Rows/Bulk/Update.php | 4 +- .../Http/TablesDB/Tables/Rows/Bulk/Upsert.php | 4 +- .../TablesDB/Tables/Rows/Column/Decrement.php | 4 +- .../TablesDB/Tables/Rows/Column/Increment.php | 4 +- .../Http/TablesDB/Tables/Rows/Create.php | 8 +- .../Http/TablesDB/Tables/Rows/Delete.php | 5 +- .../Http/TablesDB/Tables/Rows/Get.php | 6 +- .../Http/TablesDB/Tables/Rows/Logs/XList.php | 2 +- .../Http/TablesDB/Tables/Rows/Update.php | 5 +- .../Http/TablesDB/Tables/Rows/Upsert.php | 5 +- .../Http/TablesDB/Tables/Rows/XList.php | 6 +- .../Databases/Http/TablesDB/Tables/Update.php | 2 +- .../Http/TablesDB/Tables/Usage/Get.php | 2 +- .../Databases/Http/TablesDB/Tables/XList.php | 2 +- .../Databases/Services/Registry/Legacy.php | 1 + .../TablesDB/Transactions/ACIDTest.php | 166 ++-- .../Transactions/TransactionsTest.php | 802 +++++++++--------- 119 files changed, 831 insertions(+), 762 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php diff --git a/composer.json b/composer.json index 1dc7288441..9042cb088f 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "1.*", + "utopia-php/database": "2.*", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", "utopia-php/dns": "0.3.*", diff --git a/composer.lock b/composer.lock index f82ba2aced..a1c8862168 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7553e976312b0423cc31544abb91caec", + "content-hash": "773efb29b9b584b1249790e0016c823a", "packages": [ { "name": "adhocore/jwt", @@ -3296,16 +3296,16 @@ }, { "name": "utopia-php/abuse", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be" + "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/c5e2232033b507a07f72180dc56d37e1872ee7be", - "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd591568791556d246d901d6aaf9935ab02c3f9a", + "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a", "shasum": "" }, "require": { @@ -3313,7 +3313,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "1.*" + "utopia-php/database": "2.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3341,9 +3341,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/1.0.0" + "source": "https://github.com/utopia-php/abuse/tree/1.0.1" }, - "time": "2025-08-13T09:12:54+00:00" + "time": "2025-09-04T12:46:54+00:00" }, { "name": "utopia-php/analytics", @@ -3393,21 +3393,21 @@ }, { "name": "utopia-php/audit", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb" + "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c0ed75f4d068f1f6c2e7149a909490d4214e72bb", - "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", + "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "1.*" + "utopia-php/database": "2.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3434,9 +3434,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/1.0.0" + "source": "https://github.com/utopia-php/audit/tree/1.0.1" }, - "time": "2025-08-13T09:09:00+00:00" + "time": "2025-09-04T12:46:43+00:00" }, { "name": "utopia-php/cache", @@ -3638,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "1.4.5", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "1bac5c66567cd0718b4c133c8550b3c6076ff908" + "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/1bac5c66567cd0718b4c133c8550b3c6076ff908", - "reference": "1bac5c66567cd0718b4c133c8550b3c6076ff908", + "url": "https://api.github.com/repos/utopia-php/database/zipball/e4a03ba543abc4e436ec1b450750a14bd36011d5", + "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5", "shasum": "" }, "require": { @@ -3688,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.4.5" + "source": "https://github.com/utopia-php/database/tree/2.0.0" }, - "time": "2025-09-11T03:10:29+00:00" + "time": "2025-09-04T12:36:53+00:00" }, { "name": "utopia-php/detector", @@ -4190,16 +4190,16 @@ }, { "name": "utopia-php/migration", - "version": "1.1.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4" + "reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/c42935a6a4ee3701c68d24244e82ecb39e945ec4", - "reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/6fb6f8f032cd34c3c65728a55d494adeac2ff038", + "reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038", "shasum": "" }, "require": { @@ -4207,7 +4207,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "1.*", + "utopia-php/database": "2.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4240,9 +4240,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.1.1" + "source": "https://github.com/utopia-php/migration/tree/1.1.0" }, - "time": "2025-09-10T06:17:20+00:00" + "time": "2025-09-10T05:45:30+00:00" }, { "name": "utopia-php/orchestration", diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php index 0939376c49..b22119fad1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php @@ -53,7 +53,7 @@ abstract class Action extends UtopiaAction /** * Get the SDK group name for the current action. */ - protected function getSdkGroup(): string + protected function getSDKGroup(): string { return $this->isCollectionsAPI() ? 'collections' : 'tables'; } @@ -61,7 +61,7 @@ abstract class Action extends UtopiaAction /** * Get the SDK namespace for the current action. */ - protected function getSdkNamespace(): string + protected function getSDKNamespace(): string { return $this->isCollectionsAPI() ? 'databases' : 'tablesDB'; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index f3903d91a7..ae97d1b7cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -66,7 +66,7 @@ abstract class Action extends UtopiaAction * * Can be used for XList operations as well! */ - protected function getSdkGroup(): string + protected function getSDKGroup(): string { return $this->isCollectionsAPI() ? 'attributes' : 'columns'; } @@ -74,7 +74,7 @@ abstract class Action extends UtopiaAction /** * Get the SDK namespace for the current action. */ - protected function getSdkNamespace(): string + protected function getSDKNamespace(): string { return $this->isCollectionsAPI() ? 'databases' : 'tablesDB'; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php index 6c4be252a0..6c8e5dcf3d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php @@ -42,8 +42,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-boolean-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php index 752e8c6c59..d4724ea551 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php @@ -42,8 +42,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-boolean-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php index 5d5b8fe4bf..1f2098e7af 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php @@ -43,8 +43,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-datetime-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php index e1d1d40f75..cb4d0d924b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-datetime-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php index 8dec7530bf..eb51044323 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Delete.php @@ -43,8 +43,8 @@ class Delete extends Action ->label('audits.event', 'attribute.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/delete-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php index bc58ca10af..cbfd66e4d9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php @@ -43,8 +43,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-email-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php index 53300725d7..2446722f7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-email-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php index cd22e87d1c..98ed83861d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php @@ -45,8 +45,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-enum-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php index 951e43effe..23dc807360 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php @@ -44,8 +44,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-enum-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php index 2a8253c22c..83deb92edc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php @@ -45,8 +45,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-float-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php index 327a766cee..7f295a1a94 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-float-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php index 28ee584778..91fa3582f7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Get.php @@ -47,8 +47,8 @@ class Get extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/get-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php index aa699b4a49..6e6264466c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php @@ -43,8 +43,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-ip-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php index f61cf21732..6cedf10760 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-ip-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php index 81a11b0471..090d63c403 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php @@ -45,8 +45,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-integer-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php index 11cfb24943..b6ae79bd8a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-integer-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Create.php index 4eb3345823..f691fc29cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Create.php @@ -44,8 +44,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-line-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php index 8fcb867923..52f04ba5bc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-line-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Create.php index 47c88497ba..aae715ba1e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Create.php @@ -44,8 +44,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-point-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php index bffe802927..73964ab461 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-point-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Create.php index 6cc74254ae..6fbbd46d2c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Create.php @@ -44,8 +44,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-polygon-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php index d78bfa8ef0..23eb06f45d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-polygon-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index a062816329..75471b826c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -45,8 +45,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-relationship-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php index c19c4ff046..897cbd434f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php @@ -41,8 +41,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-relationship-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php index 592aa8ee93..1527c4d1d9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php @@ -47,8 +47,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-string-attribute.md', auth: [AuthType::KEY], @@ -95,7 +95,7 @@ class Create extends Action array $plan ): void { if (!App::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string ' . $this->getSdkGroup() . ' are not available on your plan. Please upgrade to create encrypted string ' . $this->getSdkGroup() . '.'); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted string ' . $this->getSDKGroup() . '.'); } if ($encrypt && $size < APP_DATABASE_ENCRYPT_SIZE_MIN) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php index 03efac1430..8614dfb202 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php @@ -45,8 +45,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-string-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php index 39153b3cb8..ce0175966b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php @@ -43,8 +43,8 @@ class Create extends Action ->label('audits.event', 'attribute.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-url-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php index 7ace4d0683..7ba12ad98a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php @@ -43,8 +43,8 @@ class Update extends Action ->label('audits.event', 'attribute.update') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-url-attribute.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php index 1852290f76..6daa180df9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/XList.php @@ -41,8 +41,8 @@ class XList extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/list-attributes.md', auth: [AuthType::KEY], @@ -141,7 +141,7 @@ class XList extends Action $response->dynamic(new Document([ 'total' => $total, - $this->getSdkGroup() => $attributes, + $this->getSDKGroup() => $attributes, ]), $this->getResponseModel()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index e06dae89bb..b810ce602f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -52,7 +52,7 @@ class Create extends Action ->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}') ->label('sdk', new Method( namespace: 'databases', - group: $this->getSdkGroup(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php index d20570fb43..d124a47289 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Delete.php @@ -42,7 +42,7 @@ class Delete extends Action ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( namespace: 'databases', - group: $this->getSdkGroup(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/delete-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index d1d0738990..871d1c4cbb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -76,7 +76,7 @@ abstract class Action extends AppwriteAction * * Can be used for XList operations as well! */ - protected function getSdkGroup(): string + protected function getSDKGroup(): string { return $this->isCollectionsAPI() ? 'documents' : 'rows'; } @@ -84,7 +84,7 @@ abstract class Action extends AppwriteAction /** * Get the SDK namespace for the current action. */ - protected function getSdkNamespace(): string + protected function getSDKNamespace(): string { return $this->isCollectionsAPI() ? 'databases' : 'tablesDB'; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 39bb5b1f97..cbe0ddceaf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -54,8 +54,8 @@ class Decrement extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/decrement-document-attribute.md', auth: [AuthType::SESSION, AuthType::JWT, AuthType::ADMIN, AuthType::KEY], @@ -166,9 +166,9 @@ class Decrement extends Action } catch (NotFoundException) { throw new Exception($this->getStructureNotFoundException()); } catch (LimitException) { - throw new Exception($this->getLimitException(), $this->getSdkNamespace() . ' "' . $attribute . '" has reached the minimum value of ' . $min); + throw new Exception($this->getLimitException(), $this->getSDKNamespace() . ' "' . $attribute . '" has reached the minimum value of ' . $min); } catch (TypeException) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, $this->getSdkNamespace() . ' "' . $attribute . '" is not a number'); + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, $this->getSDKNamespace() . ' "' . $attribute . '" is not a number'); } catch (InvalidArgumentException $e) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $e->getMessage()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 0fa19f8370..22e19c69a5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -54,8 +54,8 @@ class Increment extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/increment-document-attribute.md', auth: [AuthType::SESSION, AuthType::JWT, AuthType::ADMIN, AuthType::KEY], @@ -166,9 +166,9 @@ class Increment extends Action } catch (NotFoundException) { throw new Exception($this->getStructureNotFoundException()); } catch (LimitException) { - throw new Exception($this->getLimitException(), $this->getSdkNamespace() . ' "' . $attribute . '" has reached the maximum value of ' . $max); + throw new Exception($this->getLimitException(), $this->getSDKNamespace() . ' "' . $attribute . '" has reached the maximum value of ' . $max); } catch (TypeException) { - throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, $this->getSdkNamespace() . ' "' . $attribute . '" is not a number'); + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, $this->getSDKNamespace() . ' "' . $attribute . '" is not a number'); } catch (InvalidArgumentException $e) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $e->getMessage()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index 82f940bb62..57e81c1f3c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -51,8 +51,8 @@ class Delete extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/delete-documents.md', auth: [AuthType::ADMIN, AuthType::KEY], @@ -101,7 +101,7 @@ class Delete extends Action ); if ($hasRelationships) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for ' . $this->getSDKNamespace() . ' with relationship attributes'); } $originalQueries = $queries; @@ -152,7 +152,7 @@ class Delete extends Action // Return successful response without actually deleting documents $response->dynamic(new Document([ - $this->getSdkGroup() => [], + $this->getSDKGroup() => [], 'total' => 0, // Can't predict how many would be deleted ]), $this->getResponseModel()); return; @@ -187,7 +187,7 @@ class Delete extends Action $response->dynamic(new Document([ 'total' => $modified, - $this->getSdkGroup() => $documents, + $this->getSDKGroup() => $documents, ]), $this->getResponseModel()); $this->triggerBulk( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 1b9559d4b9..c2d1bb06fc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -54,8 +54,8 @@ class Update extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-documents.md', auth: [AuthType::ADMIN, AuthType::KEY], @@ -113,7 +113,7 @@ class Update extends Action ); if ($hasRelationships) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk update is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk update is not supported for ' . $this->getSDKNamespace() . ' with relationship attributes'); } $originalQueries = $queries; @@ -174,7 +174,7 @@ class Update extends Action // Return successful response without actually updating documents $response->dynamic(new Document([ - $this->getSdkGroup() => [], + $this->getSDKGroup() => [], 'total' => 0, // Can't predict how many would be updated ]), $this->getResponseModel()); return; @@ -214,7 +214,7 @@ class Update extends Action $response->dynamic(new Document([ 'total' => $modified, - $this->getSdkGroup() => $documents + $this->getSDKGroup() => $documents ]), $this->getResponseModel()); $this->triggerBulk( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 618d640d95..371e0c7360 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -52,8 +52,8 @@ class Upsert extends Action ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/upsert-documents.md', auth: [AuthType::ADMIN, AuthType::KEY], @@ -103,7 +103,7 @@ class Upsert extends Action ); if ($hasRelationships) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes'); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for ' . $this->getSDKNamespace() . ' with relationship attributes'); } foreach ($documents as $key => $document) { @@ -150,7 +150,7 @@ class Upsert extends Action // Return successful response without actually upserting documents $response->dynamic(new Document([ - $this->getSdkGroup() => [], + $this->getSDKGroup() => [], 'total' => \count($documents), ]), $this->getResponseModel()); @@ -192,7 +192,7 @@ class Upsert extends Action $response->dynamic(new Document([ 'total' => $modified, - $this->getSdkGroup() => $upserted + $this->getSDKGroup() => $upserted ]), $this->getResponseModel()); $this->triggerBulk( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index afc5036aa3..7fd29ba6ef 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -63,8 +63,8 @@ class Create extends Action ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), desc: 'Create document', description: '/docs/references/databases/create-document.md', @@ -90,8 +90,8 @@ class Create extends Action ), ), new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: $this->getBulkActionName(self::getName()), desc: 'Create documents', description: '/docs/references/databases/create-documents.md', @@ -148,7 +148,7 @@ class Create extends Action } if (!empty($data) && !empty($documents)) { // Both single and bulk documents provided - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'You can only send one of the following parameters: data, ' . $this->getSdkGroup()); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'You can only send one of the following parameters: data, ' . $this->getSDKGroup()); } if (!empty($data) && empty($documentId)) { // Single document provided without document ID @@ -161,12 +161,12 @@ class Create extends Action $documentId = $this->isCollectionsAPI() ? 'documentId' : 'rowId'; throw new Exception( Exception::GENERAL_BAD_REQUEST, - "Param \"$documentId\" is not allowed when creating multiple " . $this->getSdkGroup() . ', set "$id" on each instead.' + "Param \"$documentId\" is not allowed when creating multiple " . $this->getSDKGroup() . ', set "$id" on each instead.' ); } if (!empty($documents) && !empty($permissions)) { // Bulk documents provided with permissions - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Param "permissions" is disallowed when creating multiple ' . $this->getSdkGroup() . ', set "$permissions" on each instead'); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Param "permissions" is disallowed when creating multiple ' . $this->getSDKGroup() . ', set "$permissions" on each instead'); } $isBulk = true; @@ -200,7 +200,7 @@ class Create extends Action ); if ($isBulk && $hasRelationships) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for ' . $this->getSdkNamespace() .' with relationship ' . $this->getStructureContext()); + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for ' . $this->getSDKNamespace() .' with relationship ' . $this->getStructureContext()); } $setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk) { @@ -407,7 +407,7 @@ class Create extends Action // Return successful response without actually creating documents if ($isBulk) { $response->dynamic(new Document([ - $this->getSdkGroup() => [], + $this->getSDKGroup() => [], 'total' => \count($documents), ]), $this->getBulkResponseModel()); } else { @@ -468,7 +468,7 @@ class Create extends Action if ($isBulk) { $response->dynamic(new Document([ 'total' => count($documents), - $this->getSdkGroup() => $documents + $this->getSDKGroup() => $documents ]), $this->getBulkResponseModel()); $this->triggerBulk( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 8459e06c43..be73068c06 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -54,8 +54,8 @@ class Delete extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/delete-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index cced1440a5..3d8cd9198c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -43,8 +43,8 @@ class Get extends Action ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/get-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 93c8639520..ac7602ff4c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -48,7 +48,7 @@ class XList extends Action ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'logs', name: self::getName(), description: '/docs/references/databases/get-document-logs.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index a2376b44a2..d144ab9194 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -56,8 +56,8 @@ class Update extends Action ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 6690e0214f..f93aea88c8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -58,8 +58,8 @@ class Upsert extends Action ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/upsert-document.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 7e6931395e..d2f90c0951 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -46,8 +46,8 @@ class XList extends Action ->label('scope', 'documents.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/list-documents.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], @@ -206,7 +206,7 @@ class XList extends Action $response->dynamic(new Document([ 'total' => $total, // rows or documents - $this->getSdkGroup() => $documents, + $this->getSDKGroup() => $documents, ]), $this->getResponseModel()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php index af4b6bf733..89739570c7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Get.php @@ -37,7 +37,7 @@ class Get extends Action ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: $this->getSdkGroup(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/get-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php index f136fdd29b..400d716e41 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Action.php @@ -52,7 +52,7 @@ abstract class Action extends UtopiaAction /** * Get the SDK group name for the current action. */ - final protected function getSdkGroup(): string + final protected function getSDKGroup(): string { return 'indexes'; } @@ -60,7 +60,7 @@ abstract class Action extends UtopiaAction /** * Get the SDK namespace for the current action. */ - final protected function getSdkNamespace(): string + final protected function getSDKNamespace(): string { return $this->isCollectionsAPI() ? 'databases' : 'tablesDB'; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php index 733259f091..0c6ef8bb23 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php @@ -51,8 +51,8 @@ class Create extends Action ->label('audits.event', 'index.create') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/create-index.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php index 9ba4c98046..2bccfdfb52 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Delete.php @@ -46,8 +46,8 @@ class Delete extends Action ->label('audits.event', 'index.delete') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/delete-index.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php index 9e05cc79c7..3d118d1922 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Get.php @@ -37,8 +37,8 @@ class Get extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/get-index.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php index c0aa9457e7..60e52f883a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/XList.php @@ -42,8 +42,8 @@ class XList extends Action ->label('scope', 'collections.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/list-indexes.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 1309793234..fa95d375c1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -49,7 +49,7 @@ class XList extends Action ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: $this->getSdkGroup(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/get-collection-logs.md', auth: [AuthType::ADMIN], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php index 5bf740d7d1..49870002ce 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php @@ -45,7 +45,7 @@ class Update extends Action ->label('audits.resource', 'database/{request.databaseId}/collections/{request.collectionId}') ->label('sdk', new Method( namespace: 'databases', - group: $this->getSdkGroup(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/update-collection.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php index f2f7a2233a..b4cc5470d9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/XList.php @@ -44,7 +44,7 @@ class XList extends Action ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', - group: $this->getSdkGroup(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/databases/list-collections.md', auth: [AuthType::KEY], @@ -121,7 +121,7 @@ class XList extends Action $response->dynamic(new Document([ 'total' => $total, - $this->getSdkGroup() => $collections, + $this->getSDKGroup() => $collections, ]), $this->getResponseModel()); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php new file mode 100644 index 0000000000..512deb8fc8 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php @@ -0,0 +1,67 @@ +context = TABLES; + } + return parent::setHttpPath($path); + } + + /** + * Get the current API context. + */ + protected function getContext(): string + { + return $this->context; + } + + /** + * Determine if the current action is for the Collections API. + */ + protected function isCollectionsAPI(): bool + { + return $this->getContext() === COLLECTIONS; + } + + /** + * Get the key used in event parameters (e.g., 'collectionId' or 'tableId'). + */ + protected function getGroupId(): string + { + return $this->getContext() . 'Id'; + } + + /** + * Get the resource type for the current action (either 'document' or 'row'). + */ + protected function getResource(): string + { + return $this->isCollectionsAPI() ? 'document' : 'row'; + } + + /** + * Get the resource ID key for the current action. + */ + protected function getResourceId(): string + { + return $this->getResource() . 'Id'; + } + + protected function getAttributeKey(): string + { + return $this->isCollectionsAPI() ? 'attribute' : 'column'; + } +} diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index d2f114a888..77589e848a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; -use Appwrite\Platform\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -19,7 +18,7 @@ class Create extends Action { public static function getName(): string { - return 'createTransaction'; + return 'createDatabasesTransaction'; } protected function getResponseModel(): string diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php index 77dde44bf6..81315498f6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; use Appwrite\Extend\Exception; -use Appwrite\Platform\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -18,7 +17,7 @@ class Delete extends Action { public static function getName(): string { - return 'deleteTransaction'; + return 'deleteDatabasesTransaction'; } protected function getResponseModel(): string diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php index 3c7eec7741..6afe28d626 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; use Appwrite\Extend\Exception; -use Appwrite\Platform\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -17,7 +16,7 @@ class Get extends Action { public static function getName(): string { - return 'getTransaction'; + return 'getDatabasesTransaction'; } protected function getResponseModel(): string diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 8216d96d5d..58505591ff 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations; use Appwrite\Extend\Exception; -use Appwrite\Platform\Action; +use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -21,7 +21,7 @@ class Create extends Action { public static function getName(): string { - return 'createOperations'; + return 'createDatabasesTransactionOperations'; } protected function getResponseModel(): string @@ -84,7 +84,9 @@ class Create extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $collections[$operation['collectionId']] ??= $dbForProject->getDocument('database_' . $database->getSequence(), $operation['collectionId']); + $collection = $collections[$operation[$this->getGroupId()]] ??= + $dbForProject->getDocument('database_' . $database->getSequence(), $operation[$this->getGroupId()]); + if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -94,7 +96,7 @@ class Create extends Action 'databaseInternalId' => $database->getSequence(), 'collectionInternalId' => $collection->getSequence(), 'transactionInternalId' => $transaction->getSequence(), - 'documentId' => $operation['documentId'] ?? null, + 'documentId' => $operation[$this->getResourceId()] ?? null, 'action' => $operation['action'], 'data' => $operation['data'] ?? [], ]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index e854306a47..3e504822f6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; use Appwrite\Event\Delete; use Appwrite\Extend\Exception; -use Appwrite\Platform\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -27,7 +26,7 @@ class Update extends Action { public static function getName(): string { - return 'updateTransaction'; + return 'updateDatabasesTransaction'; } protected function getResponseModel(): string @@ -73,11 +72,10 @@ class Update extends Action * @param bool $rollback * @param UtopiaResponse $response * @param Database $dbForProject - * @param \Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Delete $queueForDeletes + * @param Delete $queueForDeletes * @return void * @throws ConflictException * @throws Exception - * @throws \DateMalformedStringException * @throws \Throwable * @throws \Utopia\Database\Exception * @throws Authorization @@ -357,7 +355,7 @@ class Update extends Action $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data['attribute'], + attribute: $data[$this->getAttributeKey()], value: $data['value'] ?? 1, max: $data['max'] ?? null ); @@ -369,7 +367,7 @@ class Update extends Action $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data['attribute'], + attribute: $data[$this->getAttributeKey()], value: $data['value'] ?? 1, max: $data['max'] ?? null ); @@ -394,7 +392,7 @@ class Update extends Action $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data['attribute'], + attribute: $data[$this->getAttributeKey()], value: $data['value'] ?? 1, min: $data['min'] ?? null ); @@ -406,7 +404,7 @@ class Update extends Action $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data['attribute'], + attribute: $data[$this->getAttributeKey()], value: $data['value'] ?? 1, min: $data['min'] ?? null ); @@ -447,8 +445,6 @@ class Update extends Action \DateTime $createdAt, array &$state ): void { - \var_dump($data); - $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->updateDocuments( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php index e3bd8144a9..b13ca8c52b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; use Appwrite\Extend\Exception; -use Appwrite\Platform\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -20,7 +19,7 @@ class XList extends Action { public static function getName(): string { - return 'listTransactions'; + return 'listDatabasesTransactions'; } protected function getResponseModel(): string diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php index 5222d2e133..8f94ba01ae 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php @@ -37,8 +37,8 @@ class Create extends BooleanCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-boolean-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php index 3c6ef50813..3122227351 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php @@ -39,8 +39,8 @@ class Update extends BooleanUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-boolean-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php index 9598278ffc..db5a3059f1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php @@ -39,8 +39,8 @@ class Create extends DatetimeCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-datetime-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php index d7b5ec2448..151422af75 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php @@ -41,8 +41,8 @@ class Update extends DatetimeUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-datetime-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Delete.php index 50a148ce19..26f4ffa898 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Delete.php @@ -38,8 +38,8 @@ class Delete extends AttributesDelete ->label('audits.event', 'column.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/delete-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php index e28a216fff..3a8d492e4e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php @@ -38,8 +38,8 @@ class Create extends EmailCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-email-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php index 0fb856acb9..4d32489357 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php @@ -40,8 +40,8 @@ class Update extends EmailUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-email-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php index 5b9d89c7e9..68dc2f8e08 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php @@ -40,8 +40,8 @@ class Create extends EnumCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-enum-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php index 0c00e3f268..3b611a5fde 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php @@ -42,8 +42,8 @@ class Update extends EnumUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-enum-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php index 5967b00196..9fe6987cab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php @@ -38,8 +38,8 @@ class Create extends FloatCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-float-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php index 9486b3a75c..023e2e834e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php @@ -40,8 +40,8 @@ class Update extends FloatUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-float-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Get.php index d536a7aaf2..c20ef58a39 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Get.php @@ -44,8 +44,8 @@ class Get extends AttributesGet ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/get-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php index 325a9382e5..81ca8da81f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php @@ -38,8 +38,8 @@ class Create extends IPCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-ip-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php index b9e6368307..0db95b0206 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php @@ -40,8 +40,8 @@ class Update extends IPUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-ip-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php index bd6fec3f53..dfca51a6c5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php @@ -38,8 +38,8 @@ class Create extends IntegerCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-integer-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php index be92811d1b..a1568d069b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php @@ -40,8 +40,8 @@ class Update extends IntegerUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-integer-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php index f60d4dd5b8..9f3dc3b01e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php @@ -40,8 +40,8 @@ class Create extends LineCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-line-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php index 19c6df202d..cbf0a50de2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php @@ -41,8 +41,8 @@ class Update extends LineUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-line-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php index 47d97e8077..71e51425d6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php @@ -40,8 +40,8 @@ class Create extends PointCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-point-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php index 2e98bf2cf9..ef3e7f6331 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php @@ -41,8 +41,8 @@ class Update extends PointUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-point-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php index 371d5f8fd5..985c264393 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php @@ -40,8 +40,8 @@ class Create extends PolygonCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-polygon-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php index c5654b77d4..d749335e2c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php @@ -41,8 +41,8 @@ class Update extends PolygonUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-polygon-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php index b6f9663f77..0a6c76d8c5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php @@ -39,8 +39,8 @@ class Create extends RelationshipCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-relationship-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php index 421e11af91..b645454be1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php @@ -39,8 +39,8 @@ class Update extends RelationshipUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-relationship-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php index 14f0c8321e..b6cf5fa8c9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php @@ -40,8 +40,8 @@ class Create extends StringCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-string-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php index fc45557f3b..4f3989ac37 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php @@ -42,8 +42,8 @@ class Update extends StringUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-string-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php index bc53ad5250..99ec36b721 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php @@ -38,8 +38,8 @@ class Create extends URLCreate ->label('audits.event', 'column.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/create-url-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php index 36bd7dc054..51168b0383 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php @@ -40,8 +40,8 @@ class Update extends URLUpdate ->label('audits.event', 'column.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-url-column.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/XList.php index ca41bb024d..13bf3257e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/XList.php @@ -33,8 +33,8 @@ class XList extends AttributesXList ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/list-columns.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php index 3965e12a74..4f62200d7c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php @@ -40,7 +40,7 @@ class Create extends CollectionCreate ->label('audits.event', 'table.create') ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'tables', name: self::getName(), description: '/docs/references/tablesdb/create-table.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Delete.php index 9bfdf42cef..de068d5b29 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Delete.php @@ -36,7 +36,7 @@ class Delete extends CollectionDelete ->label('audits.event', 'table.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'tables', name: self::getName(), description: '/docs/references/tablesdb/delete-table.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Get.php index a7d33478f7..be6ec5d9e7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Get.php @@ -33,7 +33,7 @@ class Get extends CollectionGet ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'tables', name: self::getName(), description: '/docs/references/tablesdb/get-table.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php index a2a5c8b453..5b61a4bacc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php @@ -42,8 +42,8 @@ class Create extends IndexCreate ->label('audits.event', 'index.create') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: 'createIndex', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/tablesdb/create-index.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php index 586bad78f4..9b1583585b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php @@ -41,8 +41,8 @@ class Delete extends IndexDelete ->label('audits.event', 'index.delete') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: 'deleteIndex', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/tablesdb/delete-index.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php index 3f2978b547..313260aaee 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php @@ -34,8 +34,8 @@ class Get extends IndexGet ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: 'getIndex', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/tablesdb/get-index.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php index c275fd2771..b12cb43f2d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php @@ -34,8 +34,8 @@ class XList extends IndexXList ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: 'listIndexes', // getName needs to be different from parent action to avoid conflict in path name description: '/docs/references/tablesdb/list-indexes.md', auth: [AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php index 6d386df4f6..0680649544 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Logs/XList.php @@ -30,8 +30,8 @@ class XList extends CollectionLogXList ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/get-table-logs.md', auth: [AuthType::ADMIN], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php index a46ffe6cba..7d4396b785 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php @@ -40,8 +40,8 @@ class Delete extends DocumentsDelete ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/delete-rows.md', auth: [AuthType::ADMIN, AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php index 1fc8ba031b..5005ab4f41 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php @@ -41,8 +41,8 @@ class Update extends DocumentsUpdate ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-rows.md', auth: [AuthType::ADMIN, AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php index 3f1349e2f7..d0a1f08003 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php @@ -41,8 +41,8 @@ class Upsert extends DocumentsUpsert ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/upsert-rows.md', auth: [AuthType::ADMIN, AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php index 06cfdb5150..d42cf5e9eb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php @@ -41,8 +41,8 @@ class Decrement extends DecrementDocumentAttribute ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/decrement-row-column.md', auth: [AuthType::SESSION, AuthType::JWT, AuthType::ADMIN, AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php index bddda28b25..c58e16c8e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php @@ -41,8 +41,8 @@ class Increment extends IncrementDocumentAttribute ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/increment-row-column.md', auth: [AuthType::SESSION, AuthType::JWT, AuthType::ADMIN, AuthType::KEY], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php index 88fc3c5096..9742208ed6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php @@ -50,8 +50,8 @@ class Create extends DocumentCreate ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), desc: 'Create row', description: '/docs/references/tablesdb/create-row.md', @@ -73,8 +73,8 @@ class Create extends DocumentCreate ] ), new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: $this->getBulkActionName(self::getName()), desc: 'Create rows', description: '/docs/references/tablesdb/create-rows.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php index 0695573ee1..c45029aaab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php @@ -45,8 +45,8 @@ class Delete extends DocumentDelete ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/delete-row.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], @@ -67,6 +67,7 @@ class Delete extends DocumentDelete ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php index 5704f75d82..11faf443d3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php @@ -35,8 +35,8 @@ class Get extends DocumentGet ->label('scope', ['rows.read', 'documents.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/get-row.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], @@ -52,9 +52,11 @@ class Get extends DocumentGet ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') ->param('rowId', '', new UID(), 'Row ID.') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') + ->inject('transactionState') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php index a80249070b..5f1efa2953 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Logs/XList.php @@ -30,7 +30,7 @@ class XList extends DocumentLogXList ->label('scope', ['rows.read', 'documents.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'logs', name: self::getName(), description: '/docs/references/tablesdb/get-row-logs.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php index 6d60cceb22..cb1d5888cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php @@ -42,8 +42,8 @@ class Update extends DocumentUpdate ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/update-row.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], @@ -66,6 +66,7 @@ class Update extends DocumentUpdate ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php index e16fa67e97..0bc373cc93 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php @@ -43,8 +43,8 @@ class Upsert extends DocumentUpsert ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) ->label('sdk', [ new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/upsert-row.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], @@ -69,6 +69,7 @@ class Upsert extends DocumentUpsert ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForStatsUsage') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php index 5d503f1c59..6b7fc699fd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php @@ -35,8 +35,8 @@ class XList extends DocumentXList ->label('scope', ['rows.read', 'documents.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), - group: $this->getSdkGroup(), + namespace: $this->getSDKNamespace(), + group: $this->getSDKGroup(), name: self::getName(), description: '/docs/references/tablesdb/list-rows.md', auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], @@ -51,9 +51,11 @@ class XList extends DocumentXList ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TableDB service [server integration](https://appwrite.io/docs/server/tablesdbdb#tablesdbCreate).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') + ->inject('transactionState') ->callback($this->action(...)); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php index 0fcdf319d2..8424b37a6d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php @@ -39,7 +39,7 @@ class Update extends CollectionUpdate ->label('audits.event', 'table.update') ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'tables', name: self::getName(), description: '/docs/references/tablesdb/update-table.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Usage/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Usage/Get.php index 87f720e689..0fb44ee94a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Usage/Get.php @@ -34,7 +34,7 @@ class Get extends CollectionUsageGet ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: null, name: self::getName(), description: '/docs/references/tablesdb/get-table-usage.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/XList.php index d9a92e41b1..a47a972371 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/XList.php @@ -35,7 +35,7 @@ class XList extends CollectionXList ->label('scope', ['tables.read', 'collections.read']) ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( - namespace: $this->getSdkNamespace(), + namespace: $this->getSDKNamespace(), group: 'tables', name: self::getName(), description: '/docs/references/tablesdb/list-tables.md', diff --git a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php index 3286aecf93..7de95da255 100644 --- a/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php +++ b/src/Appwrite/Platform/Modules/Databases/Services/Registry/Legacy.php @@ -78,6 +78,7 @@ use Utopia\Platform\Service; * - Documents * - Attributes * - Indexes + * - Transactions */ class Legacy extends Base { diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php index 89e095cb53..9bf459b19f 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/ACIDTest.php @@ -34,25 +34,25 @@ class ACIDTest extends Scope $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([ + // Create table with unique constraint + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'AtomicityTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add unique column - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([ + $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'] @@ -63,7 +63,7 @@ class ACIDTest extends Scope ]); // Add unique index - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/indexes', array_merge([ + $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -75,12 +75,12 @@ class ACIDTest extends Scope sleep(3); - // Create first document outside transaction - $doc1 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create first row outside transaction + $doc1 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'email' => 'existing@example.com' ] @@ -108,27 +108,27 @@ class ACIDTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'email' => 'newuser@example.com' // This should succeed ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'email' => 'existing@example.com' // This will fail - duplicate ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'email' => 'anotheruser@example.com' // This should not be created due to atomicity ] @@ -148,26 +148,26 @@ class ACIDTest extends Scope ]); 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([ + // If transaction succeeded, all rows should be created + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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) + // Should have 4 rows total (1 original + 3 from transaction) // But since we have a unique constraint violation, this might fail - $this->assertGreaterThanOrEqual(1, $documents['body']['total']); + $this->assertGreaterThanOrEqual(1, $rows['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([ + // Verify NO new rows were created (atomicity) + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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']); + $this->assertEquals(1, $rows['body']['total']); // Only the original row + $this->assertEquals('existing@example.com', $rows['body']['rows'][0]['email']); } } @@ -189,25 +189,25 @@ class ACIDTest extends Scope $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([ + // Create table with required fields and constraints + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'ConsistencyTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add required string column - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([ + $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'] @@ -218,7 +218,7 @@ class ACIDTest extends Scope ]); // Add integer column with min/max constraints - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/integer', array_merge([ + $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -249,9 +249,9 @@ class ACIDTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'required_field' => 'Valid User', 'age' => 25 // Valid age @@ -259,9 +259,9 @@ class ACIDTest extends Scope ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'required_field' => 'Too Young User', 'age' => 10 // Below minimum - will fail constraint @@ -269,9 +269,9 @@ class ACIDTest extends Scope ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => [ 'required_field' => 'Another Valid User', 'age' => 30 // Valid but should not be created due to transaction failure @@ -293,13 +293,13 @@ class ACIDTest extends Scope $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([ + // Verify no rows were created + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(0, $documents['body']['total']); + $this->assertEquals(0, $rows['body']['total']); } /** @@ -320,15 +320,15 @@ class ACIDTest extends Scope $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([ + // Create table + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'IsolationTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), @@ -336,10 +336,10 @@ class ACIDTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add counter column - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/integer', array_merge([ + $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -352,12 +352,12 @@ class ACIDTest extends Scope sleep(2); - // Create initial document with counter - $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create initial row with counter + $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'documentId' => 'shared_counter', + 'rowId' => 'shared_counter', 'data' => [ 'counter' => 0 ] @@ -396,8 +396,8 @@ class ACIDTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => 'shared_counter', + 'tableId' => $tableId, + 'rowId' => 'shared_counter', 'action' => 'increment', 'data' => [ 'column' => 'counter', @@ -416,8 +416,8 @@ class ACIDTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => 'shared_counter', + 'tableId' => $tableId, + 'rowId' => 'shared_counter', 'action' => 'increment', 'data' => [ 'column' => 'counter', @@ -450,13 +450,13 @@ class ACIDTest extends Scope $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([ + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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']); + $this->assertEquals(15, $row['body']['counter']); } /** @@ -477,15 +477,15 @@ class ACIDTest extends Scope $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([ + // Create table + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'DurabilityTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), @@ -494,10 +494,10 @@ class ACIDTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add column - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([ + $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'] @@ -529,27 +529,27 @@ class ACIDTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'durable_doc_1', + 'rowId' => 'durable_doc_1', 'data' => [ 'data' => 'Important data 1' ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'durable_doc_2', + 'rowId' => 'durable_doc_2', 'data' => [ 'data' => 'Important data 2' ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'durable_doc_1', + 'rowId' => 'durable_doc_1', 'data' => [ 'data' => 'Updated important data 1' ] @@ -569,33 +569,33 @@ class ACIDTest extends Scope $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([ + // List all rows to see what was created + $allDocs = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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'])); + $this->assertGreaterThan(0, $allDocs['body']['total'], 'Should have created rows. 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([ + // Verify rows exist and have correct data + $row1 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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']); + $this->assertEquals(200, $row1['headers']['status-code']); + $this->assertEquals('Updated important data 1', $row1['body']['data']); - $document2 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/durable_doc_2", array_merge([ + $row2 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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']); + $this->assertEquals(200, $row2['headers']['status-code']); + $this->assertEquals('Important data 2', $row2['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([ + $update = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/durable_doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -607,19 +607,19 @@ class ACIDTest extends Scope $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([ + $row1 = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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']); + $this->assertEquals('Modified outside transaction', $row1['body']['data']); - // List all documents to verify total count - $documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // List all rows to verify total count + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(2, $documents['body']['total']); + $this->assertEquals(2, $rows['body']['total']); } } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php index affe44b662..c19100e7ed 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -121,15 +121,15 @@ class TransactionsTest extends Scope $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; - // Create a collection for testing - $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([ + // Create a table for testing + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TransactionOperationsTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), @@ -138,11 +138,11 @@ class TransactionsTest extends Scope ], ]); - $this->assertEquals(201, $collection['headers']['status-code']); - $collectionId = $collection['body']['$id']; + $this->assertEquals(201, $table['headers']['status-code']); + $tableId = $table['body']['$id']; // Add columns - $column = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([ + $column = $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'] @@ -166,18 +166,18 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'doc1', + 'rowId' => 'doc1', 'data' => [ 'name' => 'Test Document 1' ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'doc2', + 'rowId' => 'doc2', 'data' => [ 'name' => 'Test Document 2' ] @@ -197,9 +197,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'doc1', + 'rowId' => 'doc1', 'data' => [ 'name' => 'Updated Document 1' ] @@ -219,9 +219,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => 'invalid_database', - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['name' => 'Test'] ] ] @@ -229,7 +229,7 @@ class TransactionsTest extends Scope $this->assertEquals(404, $response['headers']['status-code'], 'Invalid database should return 404. Got: ' . json_encode($response['body'])); - // Test invalid collection ID + // Test invalid table ID $response = $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -238,9 +238,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => 'invalid_collection', + 'tableId' => 'invalid_table', 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['name' => 'Test'] ] ] @@ -267,15 +267,15 @@ class TransactionsTest extends Scope $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([ + // Create table + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TransactionCommitTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), @@ -284,11 +284,11 @@ class TransactionsTest extends Scope ], ]); - $this->assertEquals(201, $collection['headers']['status-code']); - $collectionId = $collection['body']['$id']; + $this->assertEquals(201, $table['headers']['status-code']); + $tableId = $table['body']['$id']; // Add columns - $column = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([ + $column = $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'] @@ -320,27 +320,27 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'doc1', + 'rowId' => 'doc1', 'data' => [ 'name' => 'Test Document 1' ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'doc2', + 'rowId' => 'doc2', 'data' => [ 'name' => 'Test Document 2' ] ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'doc1', + 'rowId' => 'doc1', 'data' => [ 'name' => 'Updated Document 1' ] @@ -363,18 +363,18 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('committed', $response['body']['status']); - // Verify documents were created - $documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Verify rows were created + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(2, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(2, $rows['body']['total']); // Verify the update was applied $doc1Found = false; - foreach ($documents['body']['documents'] as $doc) { + foreach ($rows['body']['rows'] as $doc) { if ($doc['$id'] === 'doc1') { $this->assertEquals('Updated Document 1', $doc['name']); $doc1Found = true; @@ -422,15 +422,15 @@ class TransactionsTest extends Scope $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; - // Create a collection for rollback test - $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([ + // Create a table for rollback test + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TransactionRollbackTest', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), @@ -439,10 +439,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Add column - $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/string', array_merge([ + $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'] @@ -463,9 +463,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'rollback_doc', + 'rowId' => 'rollback_doc', 'data' => [ 'value' => 'Should not exist' ] @@ -487,14 +487,14 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('rolledBack', $response['body']['status']); - // Verify no documents were created - $documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Verify no rows were created + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $documents['headers']['status-code']); - $this->assertEquals(0, $documents['body']['total']); + $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(0, $rows['body']['total']); } /** @@ -502,7 +502,7 @@ class TransactionsTest extends Scope */ public function testTransactionExpiration(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -514,12 +514,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -529,10 +529,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -565,9 +565,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['data' => 'Should expire'] ] ] @@ -598,7 +598,7 @@ class TransactionsTest extends Scope */ public function testTransactionSizeLimit(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -610,20 +610,20 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [Permission::create(Role::any())], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -650,9 +650,9 @@ class TransactionsTest extends Scope for ($i = 0; $i < 50; $i++) { $operations[] = [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'doc_' . $i, + 'rowId' => 'doc_' . $i, 'data' => ['value' => 'Test ' . $i] ]; } @@ -674,8 +674,8 @@ class TransactionsTest extends Scope for ($i = 50; $i < 100; $i++) { $operations[] = [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => 'doc_' . $i, + 'tableId' => $tableId, + 'rowId' => 'doc_' . $i, 'action' => 'create', 'data' => ['value' => 'Test ' . $i] ]; @@ -701,9 +701,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'doc_overflow', + 'rowId' => 'doc_overflow', 'data' => ['value' => 'This should fail'] ] ] @@ -717,7 +717,7 @@ class TransactionsTest extends Scope */ public function testConcurrentTransactionConflicts(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -729,12 +729,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -743,10 +743,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/integer", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -759,13 +759,13 @@ class TransactionsTest extends Scope sleep(2); - // Create initial document - $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create initial row + $doc = $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'] ]), [ - 'documentId' => 'shared_doc', + 'rowId' => 'shared_doc', 'data' => ['counter' => 100] ]); @@ -787,7 +787,7 @@ class TransactionsTest extends Scope $transactionId1 = $txn1['body']['$id']; $transactionId2 = $txn2['body']['$id']; - // Both transactions try to update the same document + // Both transactions try to update the same row $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId1}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -796,9 +796,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'shared_doc', + 'rowId' => 'shared_doc', 'data' => ['counter' => 200] ] ] @@ -812,9 +812,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'shared_doc', + 'rowId' => 'shared_doc', 'data' => ['counter' => 300] ] ] @@ -842,8 +842,8 @@ class TransactionsTest extends Scope $this->assertEquals(409, $response2['headers']['status-code']); // Conflict - // Verify the document has the value from first transaction - $doc = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/shared_doc", array_merge([ + // Verify the row has the value from first transaction + $doc = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/shared_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -852,11 +852,11 @@ class TransactionsTest extends Scope } /** - * Test deleting a document that's being updated in a transaction + * Test deleting a row that's being updated in a transaction */ public function testDeleteDocumentDuringTransaction(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -868,12 +868,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -883,10 +883,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -898,13 +898,13 @@ class TransactionsTest extends Scope sleep(2); - // Create document - $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create row + $doc = $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'] ]), [ - 'documentId' => 'target_doc', + 'rowId' => 'target_doc', 'data' => ['data' => 'Original'] ]); @@ -928,16 +928,16 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'target_doc', + 'rowId' => 'target_doc', 'data' => ['data' => 'Updated in transaction'] ] ] ]); - // Delete the document outside of transaction - $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/target_doc", array_merge([ + // Delete the row outside of transaction + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/target_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -945,7 +945,7 @@ class TransactionsTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); - // Try to commit transaction - should fail because document no longer exists + // Try to commit transaction - should fail because row no longer exists $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -962,7 +962,7 @@ class TransactionsTest extends Scope */ public function testBulkOperations(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -974,12 +974,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -989,10 +989,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1002,7 +1002,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1014,14 +1014,14 @@ class TransactionsTest extends Scope sleep(3); - // Create some initial documents + // Create some initial rows for ($i = 1; $i <= 5; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'existing_' . $i, + 'rowId' => 'existing_' . $i, 'data' => [ 'name' => 'Existing ' . $i, 'category' => 'old' @@ -1048,7 +1048,7 @@ class TransactionsTest extends Scope // Bulk create [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'bulkCreate', 'data' => [ ['$id' => 'bulk_1', 'name' => 'Bulk 1', 'category' => 'new'], @@ -1059,7 +1059,7 @@ class TransactionsTest extends Scope // Bulk update [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'bulkUpdate', 'data' => [ 'queries' => [Query::equal('category', ['old'])->toString()], @@ -1069,7 +1069,7 @@ class TransactionsTest extends Scope // Bulk delete [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'bulkDelete', 'data' => [ 'queries' => [Query::equal('name', ['Existing 5'])->toString()] @@ -1092,20 +1092,20 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Verify results - $documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - // Should have 7 documents (5 existing - 1 deleted + 3 new) - $this->assertEquals(7, $documents['body']['total']); + // Should have 7 rows (5 existing - 1 deleted + 3 new) + $this->assertEquals(7, $rows['body']['total']); // Check categories were updated $oldCategoryCount = 0; $updatedCategoryCount = 0; $newCategoryCount = 0; - foreach ($documents['body']['documents'] as $doc) { + foreach ($rows['body']['rows'] as $doc) { switch ($doc['category']) { case 'old': $oldCategoryCount++; @@ -1129,7 +1129,7 @@ class TransactionsTest extends Scope */ public function testPartialFailureRollback(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1141,12 +1141,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -1154,10 +1154,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns with constraints - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1170,7 +1170,7 @@ class TransactionsTest extends Scope sleep(2); // Create unique index on email - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/indexes", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/indexes", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1182,13 +1182,13 @@ class TransactionsTest extends Scope sleep(2); - // Create an existing document - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create an existing row + $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'] ]), [ - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['email' => 'existing@example.com'] ]); @@ -1210,30 +1210,30 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['email' => 'valid1@example.com'] // Valid ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['email' => 'valid2@example.com'] // Valid ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['email' => 'existing@example.com'] // Will fail - duplicate ], [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['email' => 'valid3@example.com'] // Would be valid but should rollback ], ] @@ -1252,14 +1252,14 @@ class TransactionsTest extends Scope $this->assertEquals(409, $response['headers']['status-code']); // Conflict due to duplicate - // Verify NO new documents were created (atomicity) - $documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Verify NO new rows were created (atomicity) + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/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']); + $this->assertEquals(1, $rows['body']['total']); // Only the original row + $this->assertEquals('existing@example.com', $rows['body']['rows'][0]['email']); } /** @@ -1267,7 +1267,7 @@ class TransactionsTest extends Scope */ public function testDoubleCommitRollback(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1279,20 +1279,20 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [Permission::create(Role::any())], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1322,9 +1322,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => ID::unique(), + 'rowId' => ID::unique(), 'data' => ['data' => 'Test'] ] ] @@ -1385,11 +1385,11 @@ class TransactionsTest extends Scope } /** - * Test operations on non-existent documents + * Test operations on non-existent rows */ public function testOperationsOnNonExistentDocuments(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1401,12 +1401,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -1415,10 +1415,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1439,7 +1439,7 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Try to update non-existent document + // Try to update non-existent row $response = $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1448,9 +1448,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'update', - 'documentId' => 'non_existent_doc', + 'rowId' => 'non_existent_doc', 'data' => ['data' => 'Should fail'] ] ] @@ -1469,7 +1469,7 @@ class TransactionsTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); // Document not found - // Test delete non-existent document + // Test delete non-existent row $transaction2 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1486,9 +1486,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'delete', - 'documentId' => 'non_existent_doc', + 'rowId' => 'non_existent_doc', 'data' => [] ] ] @@ -1513,7 +1513,7 @@ class TransactionsTest extends Scope */ public function testCreateDocument(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1525,14 +1525,14 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', - 'documentSecurity' => false, + 'rowSecurity' => false, 'permissions' => [ Permission::create(Role::any()), Permission::read(Role::any()), @@ -1541,7 +1541,7 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns $columns = [ @@ -1555,7 +1555,7 @@ class TransactionsTest extends Scope $type = $attr['type']; unset($attr['type']); - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/{$type}", array_merge([ + $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/{$type}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1576,13 +1576,13 @@ class TransactionsTest extends Scope $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; - // Create document via normal route with transactionId - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create row via normal route with transactionId + $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'] ]), [ - 'documentId' => 'doc_from_route', + 'rowId' => 'doc_from_route', 'data' => [ 'name' => 'Created via normal route', 'counter' => 100, @@ -1594,7 +1594,7 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); // Document should not exist outside transaction yet - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_from_route", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_from_route", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1613,7 +1613,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Document should now exist - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_from_route", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_from_route", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1627,7 +1627,7 @@ class TransactionsTest extends Scope */ public function testUpdateDocument(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1639,12 +1639,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -1653,10 +1653,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1666,7 +1666,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/integer", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1677,7 +1677,7 @@ class TransactionsTest extends Scope 'max' => 10000, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1689,13 +1689,13 @@ class TransactionsTest extends Scope sleep(3); - // Create document outside transaction - $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create row outside transaction + $doc = $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'] ]), [ - 'documentId' => 'doc_to_update', + 'rowId' => 'doc_to_update', 'data' => [ 'name' => 'Original name', 'counter' => 50, @@ -1714,8 +1714,8 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Update document via normal route with transactionId - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_update", array_merge([ + // Update row via normal route with transactionId + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_to_update", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1731,7 +1731,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Document should still have original values outside transaction - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_update", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_to_update", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1751,7 +1751,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Document should now have updated values - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_update", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_to_update", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1765,7 +1765,7 @@ class TransactionsTest extends Scope */ public function testUpsertDocument(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1777,12 +1777,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -1791,10 +1791,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1804,7 +1804,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/integer", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1826,13 +1826,13 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Upsert document (create) via normal route with transactionId - $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + // Upsert row (create) via normal route with transactionId + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_upsert", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'doc_upsert', + 'rowId' => 'doc_upsert', 'data' => [ 'name' => 'Created by upsert', 'counter' => 25 @@ -1843,20 +1843,20 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); // Document should not exist outside transaction yet - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_upsert", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(404, $response['headers']['status-code']); - // Upsert same document (update) in same transaction - $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + // Upsert same row (update) in same transaction + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_upsert", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'doc_upsert', + 'rowId' => 'doc_upsert', 'data' => [ 'name' => 'Updated by upsert', 'counter' => 75 @@ -1878,7 +1878,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Document should now exist with updated values - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_upsert", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_upsert", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1893,7 +1893,7 @@ class TransactionsTest extends Scope */ public function testDeleteDocument(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1905,12 +1905,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -1919,10 +1919,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -1934,13 +1934,13 @@ class TransactionsTest extends Scope sleep(2); - // Create document outside transaction - $doc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create row outside transaction + $doc = $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'] ]), [ - 'documentId' => 'doc_to_delete', + 'rowId' => 'doc_to_delete', 'data' => ['name' => 'Will be deleted'] ]); @@ -1955,8 +1955,8 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Delete document via normal route with transactionId - $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_delete", array_merge([ + // Delete row via normal route with transactionId + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_to_delete", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -1967,7 +1967,7 @@ class TransactionsTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); // Document should still exist outside transaction - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_delete", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_to_delete", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1986,7 +1986,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Document should no longer exist - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_to_delete", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_to_delete", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -1999,7 +1999,7 @@ class TransactionsTest extends Scope */ public function testBulkCreate(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2011,12 +2011,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -2024,10 +2024,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2037,7 +2037,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2059,12 +2059,12 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; // Bulk create via normal route with transactionId - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documents' => [ + 'rows' => [ [ '$id' => 'bulk_create_1', 'name' => 'Bulk created 1', @@ -2087,7 +2087,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Bulk operations return 200 // Documents should not exist outside transaction yet - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2096,8 +2096,8 @@ class TransactionsTest extends Scope $this->assertEquals(0, $response['body']['total']); - // Individual document check - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_create_1", array_merge([ + // Individual row check + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/bulk_create_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2116,7 +2116,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Documents should now exist - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2125,9 +2125,9 @@ class TransactionsTest extends Scope $this->assertEquals(3, $response['body']['total']); - // Verify individual documents + // Verify individual rows for ($i = 1; $i <= 3; $i++) { - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_create_{$i}", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/bulk_create_{$i}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2143,7 +2143,7 @@ class TransactionsTest extends Scope */ public function testBulkUpdate(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2155,12 +2155,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -2169,10 +2169,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2182,7 +2182,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2194,14 +2194,14 @@ class TransactionsTest extends Scope sleep(3); - // Create documents for bulk testing + // Create rows for bulk testing for ($i = 1; $i <= 3; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'bulk_update_' . $i, + 'rowId' => 'bulk_update_' . $i, 'data' => [ 'name' => 'Bulk doc ' . $i, 'category' => 'bulk_test' @@ -2219,7 +2219,7 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; // Bulk update via normal route with transactionId - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] @@ -2232,7 +2232,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Documents should still have original category outside transaction - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2253,7 +2253,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Documents should now have updated category - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2268,7 +2268,7 @@ class TransactionsTest extends Scope */ public function testBulkUpsert(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2280,12 +2280,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -2294,10 +2294,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2307,7 +2307,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/integer", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -2320,13 +2320,13 @@ class TransactionsTest extends Scope sleep(3); - // Create one document outside transaction - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create one row outside transaction + $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'] ]), [ - 'documentId' => 'bulk_upsert_existing', + 'rowId' => 'bulk_upsert_existing', 'data' => [ 'name' => 'Existing doc', 'counter' => 10 @@ -2343,12 +2343,12 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; // Bulk upsert via normal route with transactionId (updates existing, creates new) - $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documents' => [ + 'rows' => [ [ '$id' => 'bulk_upsert_existing', 'name' => 'Updated existing', @@ -2365,8 +2365,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Original document should be unchanged, new document shouldn't exist outside transaction - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_existing", array_merge([ + // Original row should be unchanged, new row shouldn't exist outside transaction + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/bulk_upsert_existing", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2374,7 +2374,7 @@ class TransactionsTest extends Scope $this->assertEquals('Existing doc', $response['body']['name']); $this->assertEquals(10, $response['body']['counter']); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_new", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/bulk_upsert_new", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2392,8 +2392,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Check both documents exist with updated values - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_existing", array_merge([ + // Check both rows exist with updated values + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/bulk_upsert_existing", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2401,7 +2401,7 @@ class TransactionsTest extends Scope $this->assertEquals('Updated existing', $response['body']['name']); $this->assertEquals(20, $response['body']['counter']); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/bulk_upsert_new", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/bulk_upsert_new", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2415,7 +2415,7 @@ class TransactionsTest extends Scope */ public function testBulkDelete(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2427,12 +2427,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -2441,10 +2441,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2454,7 +2454,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2466,14 +2466,14 @@ class TransactionsTest extends Scope sleep(3); - // Create documents for bulk testing + // Create rows for bulk testing for ($i = 1; $i <= 3; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'bulk_delete_' . $i, + 'rowId' => 'bulk_delete_' . $i, 'data' => [ 'name' => 'Delete doc ' . $i, 'category' => 'bulk_delete_test' @@ -2491,7 +2491,7 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; // Bulk delete via normal route with transactionId - $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -2503,7 +2503,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Bulk delete with transaction returns 200 // Documents should still exist outside transaction - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2524,7 +2524,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); // Documents should now be deleted - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -2539,7 +2539,7 @@ class TransactionsTest extends Scope */ public function testMixedSingleOperations(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2551,12 +2551,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -2566,10 +2566,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2579,7 +2579,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2589,7 +2589,7 @@ class TransactionsTest extends Scope 'required' => false, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/integer", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -2602,13 +2602,13 @@ class TransactionsTest extends Scope sleep(3); - // Create an existing document outside transaction for testing - $existingDoc = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Create an existing row outside transaction for testing + $existingDoc = $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'] ]), [ - 'documentId' => 'existing_doc', + 'rowId' => 'existing_doc', 'data' => [ 'name' => 'Existing Document', 'status' => 'active', @@ -2628,13 +2628,13 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; $this->assertEquals(201, $transaction['headers']['status-code']); - // 1. Create new document via normal route with transactionId - $response1 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // 1. Create new row via normal route with transactionId + $response1 = $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'] ]), [ - 'documentId' => 'new_doc_1', + 'rowId' => 'new_doc_1', 'data' => [ 'name' => 'New Document 1', 'status' => 'pending', @@ -2645,13 +2645,13 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response1['headers']['status-code']); - // 2. Create another document via normal route with transactionId - $response2 = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // 2. Create another row via normal route with transactionId + $response2 = $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'] ]), [ - 'documentId' => 'new_doc_2', + 'rowId' => 'new_doc_2', 'data' => [ 'name' => 'New Document 2', 'status' => 'pending', @@ -2662,8 +2662,8 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response2['headers']['status-code']); - // 3. Update existing document via normal route with transactionId - $response3 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_doc", array_merge([ + // 3. Update existing row via normal route with transactionId + $response3 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/existing_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -2677,8 +2677,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response3['headers']['status-code']); - // 4. Update the first new document (created in same transaction) - $response4 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_1", array_merge([ + // 4. Update the first new row (created in same transaction) + $response4 = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/new_doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -2692,8 +2692,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response4['headers']['status-code']); - // 5. Delete the second new document (created in same transaction) - $response5 = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_2", array_merge([ + // 5. Delete the second new row (created in same transaction) + $response5 = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/new_doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -2703,13 +2703,13 @@ class TransactionsTest extends Scope $this->assertEquals(204, $response5['headers']['status-code']); - // 6. Upsert a new document via normal route with transactionId - $response6 = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/upserted_doc", array_merge([ + // 6. Upsert a new row via normal route with transactionId + $response6 = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/upserted_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ - 'documentId' => 'upserted_doc', + 'rowId' => 'upserted_doc', 'data' => [ 'name' => 'Upserted Document', 'status' => 'new', @@ -2731,14 +2731,14 @@ class TransactionsTest extends Scope $this->assertEquals(6, $txnDetails['body']['operations']); // 6 operations total // Verify nothing exists outside transaction yet - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_1", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/new_doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(404, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/upserted_doc", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/upserted_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2746,7 +2746,7 @@ class TransactionsTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); // Existing doc should still have original values - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_doc", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/existing_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2768,7 +2768,7 @@ class TransactionsTest extends Scope // Verify final state after commit // new_doc_1 should exist with updated values - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_1", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/new_doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2779,7 +2779,7 @@ class TransactionsTest extends Scope $this->assertEquals(8, $response['body']['priority']); // new_doc_2 should not exist (was deleted in transaction) - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/new_doc_2", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/new_doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2787,7 +2787,7 @@ class TransactionsTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); // existing_doc should have updated values - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_doc", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/existing_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2796,7 +2796,7 @@ class TransactionsTest extends Scope $this->assertEquals(10, $response['body']['priority']); // upserted_doc should exist - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/upserted_doc", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/upserted_doc", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2806,13 +2806,13 @@ class TransactionsTest extends Scope $this->assertEquals('new', $response['body']['status']); $this->assertEquals(3, $response['body']['priority']); - // Verify total document count - $documents = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Verify total row count + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(3, $documents['body']['total']); // existing_doc, new_doc_1, upserted_doc + $this->assertEquals(3, $rows['body']['total']); // existing_doc, new_doc_1, upserted_doc } /** @@ -2820,7 +2820,7 @@ class TransactionsTest extends Scope */ public function testMixedOperations(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2832,12 +2832,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::create(Role::any()), @@ -2847,10 +2847,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create column - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -2880,9 +2880,9 @@ class TransactionsTest extends Scope 'operations' => [ [ 'databaseId' => $databaseId, - 'collectionId' => $collectionId, + 'tableId' => $tableId, 'action' => 'create', - 'documentId' => 'mixed_doc1', + 'rowId' => 'mixed_doc1', 'data' => ['name' => 'Via Operations Add'] ] ] @@ -2892,12 +2892,12 @@ class TransactionsTest extends Scope $this->assertEquals(1, $response['body']['operations']); // Add operation via normal route with transactionId - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'mixed_doc2', + 'rowId' => 'mixed_doc2', 'data' => ['name' => 'Via normal route'], 'transactionId' => $transactionId ]); @@ -2913,15 +2913,15 @@ class TransactionsTest extends Scope $this->assertEquals(2, $txnDetails['body']['operations']); - // Both documents shouldn't exist yet - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc1", array_merge([ + // Both rows shouldn't exist yet + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/mixed_doc1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(404, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc2", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/mixed_doc2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2939,8 +2939,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Both documents should now exist - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc1", array_merge([ + // Both rows should now exist + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/mixed_doc1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2948,7 +2948,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('Via Operations Add', $response['body']['name']); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/mixed_doc2", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/mixed_doc2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -2958,11 +2958,11 @@ class TransactionsTest extends Scope } /** - * Test bulk update with queries that should match documents created in the same transaction + * Test bulk update with queries that should match rows created in the same transaction */ public function testBulkUpdateWithTransactionAwareQueries(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2974,12 +2974,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -2989,10 +2989,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3002,7 +3002,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/integer", array_merge([ + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3011,7 +3011,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3023,14 +3023,14 @@ class TransactionsTest extends Scope sleep(3); // Wait for columns to be created - // Create some existing documents + // Create some existing rows for ($i = 1; $i <= 3; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'existing_' . $i, + 'rowId' => 'existing_' . $i, 'data' => [ 'name' => 'Existing ' . $i, 'age' => 20 + $i, @@ -3048,13 +3048,13 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Step 1: Create new documents with age > 25 in transaction - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Step 1: Create new rows with age > 25 in transaction + $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'] ]), [ - 'documentId' => 'txn_doc_1', + 'rowId' => 'txn_doc_1', 'data' => [ 'name' => 'Transaction Doc 1', 'age' => 30, @@ -3065,12 +3065,12 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'txn_doc_2', + 'rowId' => 'txn_doc_2', 'data' => [ 'name' => 'Transaction Doc 2', 'age' => 35, @@ -3081,10 +3081,10 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); - // Step 2: Bulk update all documents with age > 25 to have status 'active' - // This should match both existing_3 (age=23 doesn't match, age=24 doesn't match, but existing documents have age 21,22,23) + // Step 2: Bulk update all rows with age > 25 to have status 'active' + // This should match both existing_3 (age=23 doesn't match, age=24 doesn't match, but existing rows have age 21,22,23) // Wait, let me fix the ages - existing docs have ages 21, 22, 23, so only txn docs should match - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] @@ -3109,8 +3109,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Verify that documents created in the transaction were updated by the bulk update - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/txn_doc_1", array_merge([ + // Verify that rows created in the transaction were updated by the bulk update + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/txn_doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -3118,7 +3118,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('active', $response['body']['status'], 'Document created in transaction should be updated by bulk update query'); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/txn_doc_2", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/txn_doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -3126,24 +3126,24 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('active', $response['body']['status'], 'Document created in transaction should be updated by bulk update query'); - // Verify existing documents were not affected + // Verify existing rows were not affected for ($i = 1; $i <= 3; $i++) { - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_{$i}", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/existing_{$i}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('inactive', $response['body']['status'], "Existing document {$i} should remain inactive (age <= 25)"); + $this->assertEquals('inactive', $response['body']['status'], "Existing row {$i} should remain inactive (age <= 25)"); } } /** - * Test bulk update with queries that should match documents updated in the same transaction + * Test bulk update with queries that should match rows updated in the same transaction */ public function testBulkUpdateMatchingUpdatedDocuments(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3155,12 +3155,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -3170,10 +3170,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3183,7 +3183,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3193,7 +3193,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3205,14 +3205,14 @@ class TransactionsTest extends Scope sleep(3); // Wait for columns to be created - // Create existing documents + // Create existing rows for ($i = 1; $i <= 4; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'doc_' . $i, + 'rowId' => 'doc_' . $i, 'data' => [ 'name' => 'Document ' . $i, 'category' => 'normal', @@ -3230,8 +3230,8 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Step 1: Update some documents to have category 'special' in transaction - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_1", array_merge([ + // Step 1: Update some rows to have category 'special' in transaction + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3244,7 +3244,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3257,9 +3257,9 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Step 2: Bulk update all documents with category 'special' to have priority 'high' - // This should match the documents we just updated in the transaction - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Step 2: Bulk update all rows with category 'special' to have priority 'high' + // This should match the rows we just updated in the 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'] @@ -3284,8 +3284,8 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Verify that the updated documents were matched by bulk update - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_1", array_merge([ + // Verify that the updated rows were matched by bulk update + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -3294,7 +3294,7 @@ class TransactionsTest extends Scope $this->assertEquals('special', $response['body']['category']); $this->assertEquals('high', $response['body']['priority'], 'Document updated in transaction should be matched by bulk update query'); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -3303,8 +3303,8 @@ class TransactionsTest extends Scope $this->assertEquals('special', $response['body']['category']); $this->assertEquals('high', $response['body']['priority'], 'Document updated in transaction should be matched by bulk update query'); - // Verify other documents were not affected - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_3", array_merge([ + // Verify other rows were not affected + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_3", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -3315,11 +3315,11 @@ class TransactionsTest extends Scope } /** - * Test bulk delete with queries that should match documents created in the same transaction + * Test bulk delete with queries that should match rows created in the same transaction */ public function testBulkDeleteMatchingCreatedDocuments(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3331,12 +3331,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -3346,10 +3346,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3359,7 +3359,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3371,14 +3371,14 @@ class TransactionsTest extends Scope sleep(3); // Wait for columns to be created - // Create existing documents + // Create existing rows for ($i = 1; $i <= 3; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'existing_' . $i, + 'rowId' => 'existing_' . $i, 'data' => [ 'name' => 'Existing ' . $i, 'type' => 'permanent' @@ -3395,13 +3395,13 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Step 1: Create temporary documents in transaction - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Step 1: Create temporary rows in transaction + $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'] ]), [ - 'documentId' => 'temp_1', + 'rowId' => 'temp_1', 'data' => [ 'name' => 'Temporary 1', 'type' => 'temporary' @@ -3411,12 +3411,12 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'temp_2', + 'rowId' => 'temp_2', 'data' => [ 'name' => 'Temporary 2', 'type' => 'temporary' @@ -3426,9 +3426,9 @@ class TransactionsTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); - // Step 2: Bulk delete all documents with type 'temporary' - // This should delete the documents we just created in the transaction - $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Step 2: Bulk delete all rows with type 'temporary' + // This should delete the rows we just created in the transaction + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3450,39 +3450,39 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Verify temporary documents were deleted (should not exist) - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/temp_1", array_merge([ + // Verify temporary rows were deleted (should not exist) + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/temp_1", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(404, $response['headers']['status-code'], 'Temporary document created and deleted in transaction should not exist'); + $this->assertEquals(404, $response['headers']['status-code'], 'Temporary row created and deleted in transaction should not exist'); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/temp_2", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/temp_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(404, $response['headers']['status-code'], 'Temporary document created and deleted in transaction should not exist'); + $this->assertEquals(404, $response['headers']['status-code'], 'Temporary row created and deleted in transaction should not exist'); - // Verify existing documents were not affected + // Verify existing rows were not affected for ($i = 1; $i <= 3; $i++) { - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/existing_{$i}", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/existing_{$i}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $this->assertEquals(200, $response['headers']['status-code'], "Permanent document {$i} should still exist"); + $this->assertEquals(200, $response['headers']['status-code'], "Permanent row {$i} should still exist"); $this->assertEquals('permanent', $response['body']['type']); } } /** - * Test bulk delete with queries that should match documents updated in the same transaction + * Test bulk delete with queries that should match rows updated in the same transaction */ public function testBulkDeleteMatchingUpdatedDocuments(): void { - // Create database and collection + // Create database and table $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -3494,12 +3494,12 @@ class TransactionsTest extends Scope $databaseId = $database['body']['$id']; - $collection = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + $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'] ]), [ - 'collectionId' => ID::unique(), + 'tableId' => ID::unique(), 'name' => 'TestCollection', 'permissions' => [ Permission::read(Role::any()), @@ -3509,10 +3509,10 @@ class TransactionsTest extends Scope ], ]); - $collectionId = $collection['body']['$id']; + $tableId = $table['body']['$id']; // Create columns - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3522,7 +3522,7 @@ class TransactionsTest extends Scope 'required' => true, ]); - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/columns/string", array_merge([ + $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'] @@ -3534,14 +3534,14 @@ class TransactionsTest extends Scope sleep(3); // Wait for columns to be created - // Create existing documents + // Create existing rows for ($i = 1; $i <= 5; $i++) { - $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + $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'] ]), [ - 'documentId' => 'doc_' . $i, + 'rowId' => 'doc_' . $i, 'data' => [ 'name' => 'Document ' . $i, 'status' => 'active' @@ -3558,8 +3558,8 @@ class TransactionsTest extends Scope $transactionId = $transaction['body']['$id']; - // Step 1: Mark some documents for deletion by updating their status - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + // Step 1: Mark some rows for deletion by updating their status + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3572,7 +3572,7 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_4", array_merge([ + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_4", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3585,9 +3585,9 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Step 2: Bulk delete all documents with status 'marked_for_deletion' - // This should delete the documents we just updated in the transaction - $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows", array_merge([ + // Step 2: Bulk delete all rows with status 'marked_for_deletion' + // This should delete the rows we just updated in the transaction + $response = $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -3609,24 +3609,24 @@ class TransactionsTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - // Verify marked documents were deleted - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_2", array_merge([ + // Verify marked rows were deleted + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_2", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(404, $response['headers']['status-code'], 'Document marked for deletion should have been deleted'); - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_4", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_4", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); $this->assertEquals(404, $response['headers']['status-code'], 'Document marked for deletion should have been deleted'); - // Verify other documents still exist + // Verify other rows still exist foreach ([1, 3, 5] as $i) { - $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$collectionId}/rows/doc_{$i}", array_merge([ + $response = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc_{$i}", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); From a31190422a7b3be745d1114e8c86bba071dd7636 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 11 Sep 2025 09:41:17 +0300 Subject: [PATCH 093/274] formatting --- .../Platform/Workers/StatsResources.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index daecabf16a..0c8f11c07b 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -440,25 +440,31 @@ class StatsResources extends Action usort($this->documents, function ($a, $b) { // Metric DESC $cmp = strcmp($b['metric'], $a['metric']); - if ($cmp !== 0) return $cmp; + if ($cmp !== 0) { + return $cmp; + } // Period ASC $cmp = strcmp($a['period'], $b['period']); - if ($cmp !== 0) return $cmp; + if ($cmp !== 0) { + return $cmp; + } // Time ASC, NULLs first - if ($a['time'] === null) return ($b['time'] === null) ? 0 : -1; - if ($b['time'] === null) return 1; + if ($a['time'] === null) { + return ($b['time'] === null) ? 0 : -1; + } + if ($b['time'] === null) { + return 1; + } return strcmp($a['time'], $b['time']); }); - var_dump($this->documents); try { $dbForLogs->createOrUpdateDocuments( 'stats', $this->documents, - 10 // See if this make an effect ); Console::success($message . ' | Documents: ' . count($this->documents)); From 5740eb89012d474142c25646eed696ba3f55441a Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 11 Sep 2025 10:04:42 +0300 Subject: [PATCH 094/274] Add sorting --- src/Appwrite/Platform/Workers/StatsUsage.php | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 3610381d5a..991fed633a 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -424,6 +424,34 @@ class StatsUsage extends Action try { $dbForProject = $getProjectDB($projectStats['project']); Console::log('Processing batch with ' . count($projectStats['stats']) . ' stats'); + + /** + * Sort by unique index key reduce locks/deadlocks + */ + usort($projectStats['stats'], function ($a, $b) { + // Metric DESC + $cmp = strcmp($b['metric'], $a['metric']); + if ($cmp !== 0) { + return $cmp; + } + + // Period ASC + $cmp = strcmp($a['period'], $b['period']); + if ($cmp !== 0) { + return $cmp; + } + + // Time ASC, NULLs first + if ($a['time'] === null) { + return ($b['time'] === null) ? 0 : -1; + } + if ($b['time'] === null) { + return 1; + } + + return strcmp($a['time'], $b['time']); + }); + $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); Console::success('Batch successfully written to DB'); @@ -468,6 +496,42 @@ class StatsUsage extends Action try { Console::log('Processing batch with ' . count($this->statDocuments) . ' stats'); + + /** + * Sort by UNIQUE KEY "_key_metric_period_time" ("_tenant","metric" DESC,"period","time") + * Here we sort by _tenant as well because of setTenantPerDocument + */ + + usort($this->statDocuments, function ($a, $b) { + // Tenant ASC + $cmp = $a['_tenant'] <=> $b['_tenant']; + if ($cmp !== 0) { + return $cmp; + } + + // Metric DESC + $cmp = strcmp($b['metric'], $a['metric']); + if ($cmp !== 0) { + return $cmp; + } + + // Period ASC + $cmp = strcmp($a['period'], $b['period']); + if ($cmp !== 0) { + return $cmp; + } + + // Time ASC, NULLs first + if ($a['time'] === null) { + return ($b['time'] === null) ? 0 : -1; + } + if ($b['time'] === null) { + return 1; + } + + return strcmp($a['time'], $b['time']); + }); + $dbForLogs->createOrUpdateDocumentsWithIncrease( 'stats', 'value', From 84aaaf810f7716e1cf983c0b87f25eaf0aa7cadd Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 11 Sep 2025 10:07:45 +0300 Subject: [PATCH 095/274] Question --- src/Appwrite/Platform/Workers/StatsUsage.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 991fed633a..ba72f7f30c 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -454,10 +454,10 @@ class StatsUsage extends Action $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); Console::success('Batch successfully written to DB'); - - unset($this->projects[$sequence]); } catch (Throwable $e) { Console::error('Error processing stats: ' . $e->getMessage()); + } finally { + unset($this->projects[$sequence]); } } @@ -538,6 +538,11 @@ class StatsUsage extends Action $this->statDocuments ); Console::success('Usage logs pushed to Logs DB'); + + /** + * todo: Do we need to unset $this->statDocuments? + */ + } catch (Throwable $th) { Console::error($th->getMessage()); } From e673d405d685b52e544817aceca57b6c7e8779b4 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 11 Sep 2025 10:14:16 +0300 Subject: [PATCH 096/274] formatting? --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index ba72f7f30c..9d998d05bc 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -538,7 +538,7 @@ class StatsUsage extends Action $this->statDocuments ); Console::success('Usage logs pushed to Logs DB'); - + /** * todo: Do we need to unset $this->statDocuments? */ From 8706828c2235fa3863997be9590c33aed991e1af Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 21:13:41 +1200 Subject: [PATCH 097/274] Fix tests --- .../Databases/Http/Databases/Transactions/Update.php | 7 +++++-- .../Databases/TablesDB/Transactions/TransactionsTest.php | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 3e504822f6..dde39efd90 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -271,7 +271,7 @@ class Update extends Action new Document($data), ); if ($document->isEmpty()) { - throw new ConflictException(''); + throw new NotFoundException(''); } $state[$collectionId][$documentId] = $document; }); @@ -330,7 +330,10 @@ class Update extends Action // Use timestamp wrapper for independent operations $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, &$state) { - $dbForProject->deleteDocument($collectionId, $documentId); + $deleted = $dbForProject->deleteDocument($collectionId, $documentId); + if (!$deleted) { + throw new NotFoundException(''); + } if (isset($state[$collectionId][$documentId])) { unset($state[$collectionId][$documentId]); } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php index c19100e7ed..9ec5994903 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -954,7 +954,7 @@ class TransactionsTest extends Scope 'commit' => true ]); - $this->assertEquals(409, $response['headers']['status-code']); // Conflict + $this->assertEquals(404, $response['headers']['status-code']); } /** From be81c73c4076ee48889fd215f20e740b9b382b98 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 21:30:07 +1200 Subject: [PATCH 098/274] Update DB --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index aa33f5ff1b..ba8b58fcea 100644 --- a/composer.lock +++ b/composer.lock @@ -3638,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5" + "reference": "44af82dcb44cdaa4b2d7f30528903f186a972ee0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/e4a03ba543abc4e436ec1b450750a14bd36011d5", - "reference": "e4a03ba543abc4e436ec1b450750a14bd36011d5", + "url": "https://api.github.com/repos/utopia-php/database/zipball/44af82dcb44cdaa4b2d7f30528903f186a972ee0", + "reference": "44af82dcb44cdaa4b2d7f30528903f186a972ee0", "shasum": "" }, "require": { @@ -3688,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.0.0" + "source": "https://github.com/utopia-php/database/tree/2.0.1" }, - "time": "2025-09-04T12:36:53+00:00" + "time": "2025-09-11T08:33:25+00:00" }, { "name": "utopia-php/detector", From dd64afa97035a0608ee89be94e876a1e751d4f06 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 22:16:44 +1200 Subject: [PATCH 099/274] Add missing references --- docs/references/databases/create-operations.md | 0 docs/references/databases/create-transaction.md | 0 docs/references/databases/delete-transaction.md | 0 docs/references/databases/get-transaction.md | 0 docs/references/databases/list-transactions.md | 0 docs/references/databases/update-transaction.md | 0 docs/references/tablesdb/create-operations.md | 1 + docs/references/tablesdb/create-transaction.md | 1 + docs/references/tablesdb/delete-transaction.md | 1 + docs/references/tablesdb/get-transaction.md | 1 + docs/references/tablesdb/list-transactions.md | 1 + docs/references/tablesdb/update-transaction.md | 1 + 12 files changed, 6 insertions(+) create mode 100644 docs/references/databases/create-operations.md create mode 100644 docs/references/databases/create-transaction.md create mode 100644 docs/references/databases/delete-transaction.md create mode 100644 docs/references/databases/get-transaction.md create mode 100644 docs/references/databases/list-transactions.md create mode 100644 docs/references/databases/update-transaction.md create mode 100644 docs/references/tablesdb/create-operations.md create mode 100644 docs/references/tablesdb/create-transaction.md create mode 100644 docs/references/tablesdb/delete-transaction.md create mode 100644 docs/references/tablesdb/get-transaction.md create mode 100644 docs/references/tablesdb/list-transactions.md create mode 100644 docs/references/tablesdb/update-transaction.md diff --git a/docs/references/databases/create-operations.md b/docs/references/databases/create-operations.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/references/databases/create-transaction.md b/docs/references/databases/create-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/references/databases/delete-transaction.md b/docs/references/databases/delete-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/references/databases/get-transaction.md b/docs/references/databases/get-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/references/databases/list-transactions.md b/docs/references/databases/list-transactions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/references/databases/update-transaction.md b/docs/references/databases/update-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/references/tablesdb/create-operations.md b/docs/references/tablesdb/create-operations.md new file mode 100644 index 0000000000..a737b95a55 --- /dev/null +++ b/docs/references/tablesdb/create-operations.md @@ -0,0 +1 @@ +Create multiple operations in a single transaction. \ No newline at end of file diff --git a/docs/references/tablesdb/create-transaction.md b/docs/references/tablesdb/create-transaction.md new file mode 100644 index 0000000000..fdf369a789 --- /dev/null +++ b/docs/references/tablesdb/create-transaction.md @@ -0,0 +1 @@ +Create a new transaction. \ No newline at end of file diff --git a/docs/references/tablesdb/delete-transaction.md b/docs/references/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..f1395c228f --- /dev/null +++ b/docs/references/tablesdb/delete-transaction.md @@ -0,0 +1 @@ +Delete a transaction by its unique ID. \ No newline at end of file diff --git a/docs/references/tablesdb/get-transaction.md b/docs/references/tablesdb/get-transaction.md new file mode 100644 index 0000000000..41900f7468 --- /dev/null +++ b/docs/references/tablesdb/get-transaction.md @@ -0,0 +1 @@ +Get a transaction by its unique ID. \ No newline at end of file diff --git a/docs/references/tablesdb/list-transactions.md b/docs/references/tablesdb/list-transactions.md new file mode 100644 index 0000000000..9a63d9f04a --- /dev/null +++ b/docs/references/tablesdb/list-transactions.md @@ -0,0 +1 @@ +List transactions across all databases. \ No newline at end of file diff --git a/docs/references/tablesdb/update-transaction.md b/docs/references/tablesdb/update-transaction.md new file mode 100644 index 0000000000..d9d5f45439 --- /dev/null +++ b/docs/references/tablesdb/update-transaction.md @@ -0,0 +1 @@ +Update a transaction, to either commit or roll back its operations. \ No newline at end of file From c7dba3690a125f598e8c2d33080320e910e8facd Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 22:17:00 +1200 Subject: [PATCH 100/274] Update auth types --- docs/references/databases/create-operations.md | 1 + docs/references/databases/create-transaction.md | 1 + docs/references/databases/delete-transaction.md | 1 + docs/references/databases/get-index.md | 2 +- docs/references/databases/get-transaction.md | 1 + docs/references/databases/list-transactions.md | 1 + docs/references/databases/update-transaction.md | 1 + .../Modules/Databases/Http/Databases/Transactions/Create.php | 2 +- .../Modules/Databases/Http/Databases/Transactions/Delete.php | 2 +- .../Modules/Databases/Http/Databases/Transactions/Get.php | 2 +- .../Modules/Databases/Http/Databases/Transactions/Update.php | 2 +- .../Modules/Databases/Http/Databases/Transactions/XList.php | 2 +- 12 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/references/databases/create-operations.md b/docs/references/databases/create-operations.md index e69de29bb2..a737b95a55 100644 --- a/docs/references/databases/create-operations.md +++ b/docs/references/databases/create-operations.md @@ -0,0 +1 @@ +Create multiple operations in a single transaction. \ No newline at end of file diff --git a/docs/references/databases/create-transaction.md b/docs/references/databases/create-transaction.md index e69de29bb2..fdf369a789 100644 --- a/docs/references/databases/create-transaction.md +++ b/docs/references/databases/create-transaction.md @@ -0,0 +1 @@ +Create a new transaction. \ No newline at end of file diff --git a/docs/references/databases/delete-transaction.md b/docs/references/databases/delete-transaction.md index e69de29bb2..f1395c228f 100644 --- a/docs/references/databases/delete-transaction.md +++ b/docs/references/databases/delete-transaction.md @@ -0,0 +1 @@ +Delete a transaction by its unique ID. \ No newline at end of file diff --git a/docs/references/databases/get-index.md b/docs/references/databases/get-index.md index cdea5b4f27..cdc27fa967 100644 --- a/docs/references/databases/get-index.md +++ b/docs/references/databases/get-index.md @@ -1 +1 @@ -Get index by ID. \ No newline at end of file +Get an index by its unique ID. \ No newline at end of file diff --git a/docs/references/databases/get-transaction.md b/docs/references/databases/get-transaction.md index e69de29bb2..41900f7468 100644 --- a/docs/references/databases/get-transaction.md +++ b/docs/references/databases/get-transaction.md @@ -0,0 +1 @@ +Get a transaction by its unique ID. \ No newline at end of file diff --git a/docs/references/databases/list-transactions.md b/docs/references/databases/list-transactions.md index e69de29bb2..9a63d9f04a 100644 --- a/docs/references/databases/list-transactions.md +++ b/docs/references/databases/list-transactions.md @@ -0,0 +1 @@ +List transactions across all databases. \ No newline at end of file diff --git a/docs/references/databases/update-transaction.md b/docs/references/databases/update-transaction.md index e69de29bb2..d9d5f45439 100644 --- a/docs/references/databases/update-transaction.md +++ b/docs/references/databases/update-transaction.md @@ -0,0 +1 @@ +Update a transaction, to either commit or roll back its operations. \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index 77589e848a..4a7e95d5ca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -40,7 +40,7 @@ class Create extends Action group: 'transactions', name: 'createTransaction', description: '/docs/references/databases/create-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php index 81315498f6..43c0c5c4a8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php @@ -39,7 +39,7 @@ class Delete extends Action group: 'transactions', name: 'deleteTransaction', description: '/docs/references/databases/delete-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_NOCONTENT, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php index 6afe28d626..43406a6751 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php @@ -38,7 +38,7 @@ class Get extends Action group: 'transactions', name: 'getTransaction', description: '/docs/references/databases/get-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index dde39efd90..ed70afee35 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -48,7 +48,7 @@ class Update extends Action group: 'transactions', name: 'updateTransaction', description: '/docs/references/databases/update-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php index b13ca8c52b..05ef5ae9e8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php @@ -41,7 +41,7 @@ class XList extends Action group: 'transactions', name: 'listTransactions', description: '/docs/references/databases/list-transactions.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, From 6652e27dd59bec080f8382ad462bd1fafb410ea9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 22:21:53 +1200 Subject: [PATCH 101/274] Update TablesDB auth --- .../Modules/Databases/Http/Databases/Transactions/Action.php | 1 - .../Modules/Databases/Http/TablesDB/Transactions/Create.php | 2 +- .../Modules/Databases/Http/TablesDB/Transactions/Delete.php | 2 +- .../Modules/Databases/Http/TablesDB/Transactions/Get.php | 2 +- .../Databases/Http/TablesDB/Transactions/Operations/Create.php | 2 +- .../Modules/Databases/Http/TablesDB/Transactions/Update.php | 2 +- .../Modules/Databases/Http/TablesDB/Transactions/XList.php | 2 +- 7 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php index 512deb8fc8..8915ae6141 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; -use Appwrite\Extend\Exception; use Utopia\Platform\Action as UtopiaAction; abstract class Action extends UtopiaAction diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php index 77b87c77dc..6a28d31621 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php @@ -37,7 +37,7 @@ class Create extends TransactionsCreate group: 'transactions', name: 'createTransaction', description: '/docs/references/tablesdb/create-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php index 9440b586c5..cad11e795a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php @@ -37,7 +37,7 @@ class Delete extends TransactionsDelete group: 'transactions', name: 'deleteTransaction', description: '/docs/references/tablesdb/delete-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_NOCONTENT, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php index a5e9c374a6..39f6754a1e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php @@ -37,7 +37,7 @@ class Get extends TransactionsGet group: 'transactions', name: 'getTransaction', description: '/docs/references/tablesdb/get-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php index eac524682c..6b2ee2ce4c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -39,7 +39,7 @@ class Create extends OperationsCreate group: 'transactions', name: 'createOperations', description: '/docs/references/tablesdb/create-operations.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php index d5b0e737a0..b754ec97a1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php @@ -38,7 +38,7 @@ class Update extends TransactionsUpdate group: 'transactions', name: 'updateTransaction', description: '/docs/references/tablesdb/update-transaction.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php index e04d28f200..67a58d7e5f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php @@ -37,7 +37,7 @@ class XList extends TransactionsList group: 'transactions', name: 'listTransactions', description: '/docs/references/tablesdb/list-transactions.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_OK, From b74e5ce5cd707817b1ccd201ca4d548a16f02af6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 22:22:46 +1200 Subject: [PATCH 102/274] Block bulk ops through txn create multi ops --- .../Databases/Transactions/Operations/Create.php | 15 ++++++++++++++- .../Utopia/Database/Validator/Operation.php | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 58505591ff..a33af05e3d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations; +use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Action; use Appwrite\SDK\AuthType; @@ -13,6 +14,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; @@ -43,7 +45,7 @@ class Create extends Action group: 'transactions', name: 'createOperations', description: '/docs/references/databases/create-operations.md', - auth: [AuthType::KEY], + auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT], responses: [ new SDKResponse( code: SwooleResponse::STATUS_CODE_CREATED, @@ -77,8 +79,19 @@ class Create extends Action ); } + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $databases = $collections = $staged = []; foreach ($operations as $operation) { + if (!$isAPIKey && !$isPrivilegedUser && \in_array($operation['action'], [ + 'bulkCreate', + 'bulkUpdate', + 'bulkDelete' + ])) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + $database = $databases[$operation['databaseId']] ??= $dbForProject->getDocument('databases', $operation['databaseId']); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index ec2d4f0d49..f3fe2375b0 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -64,6 +64,16 @@ class Operation extends Validator return $this->description; } + public function getCollectionIdName(): string + { + return $this->collectionIdName; + } + + public function getDocumentIdName(): string + { + return $this->documentIdName; + } + public function isArray(): bool { return true; From 67b05a3f1ace8c333cf066061b54658631c6cb6e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 23:08:23 +1200 Subject: [PATCH 103/274] Fix test workflow --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7b1a243da1..cebdc02163 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -248,7 +248,6 @@ jobs: Console, Databases/Legacy, Databases/TablesDB, - Databases/Transactions, Functions, FunctionsSchedule, GraphQL, From fd6ddf6926bab320fc80e5f2edc8c426af290ec6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 11 Sep 2025 23:31:53 +1200 Subject: [PATCH 104/274] Fix operation spec gen --- app/config/specs/open-api3-1.8.x-client.json | 1081 ++++++++++++- app/config/specs/open-api3-1.8.x-console.json | 1405 +++++++++++++++-- app/config/specs/open-api3-1.8.x-server.json | 1366 ++++++++++++++-- app/config/specs/open-api3-latest-client.json | 1081 ++++++++++++- .../specs/open-api3-latest-console.json | 1405 +++++++++++++++-- app/config/specs/open-api3-latest-server.json | 1366 ++++++++++++++-- app/config/specs/swagger2-1.8.x-client.json | 1064 ++++++++++++- app/config/specs/swagger2-1.8.x-console.json | 1374 ++++++++++++++-- app/config/specs/swagger2-1.8.x-server.json | 1340 ++++++++++++++-- app/config/specs/swagger2-latest-client.json | 1064 ++++++++++++- app/config/specs/swagger2-latest-console.json | 1374 ++++++++++++++-- app/config/specs/swagger2-latest-server.json | 1340 ++++++++++++++-- .../SDK/Specification/Format/OpenAPI3.php | 95 +- .../SDK/Specification/Format/Swagger2.php | 73 +- .../Utopia/Database/Validator/Operation.php | 4 +- 15 files changed, 14213 insertions(+), 1219 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index d226fcc4e1..e94ac895b6 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -4806,6 +4806,424 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents": { "get": { "summary": "List documents", @@ -4893,6 +5311,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -4951,7 +5379,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5037,6 +5466,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5142,6 +5576,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -5200,7 +5644,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5283,6 +5728,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -5396,6 +5846,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5480,7 +5935,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { @@ -5594,6 +6065,11 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5713,6 +6189,11 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5745,7 +6226,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -5820,7 +6301,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -5896,9 +6377,9 @@ "x-enum-keys": [] }, "headers": { - "type": "string", + "type": "object", "description": "HTTP headers of execution. Defaults to empty.", - "x-example": null + "x-example": "{}" }, "scheduledAt": { "type": "string", @@ -5936,7 +6417,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -7467,6 +7948,424 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows": { "get": { "summary": "List rows", @@ -7491,7 +8390,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -7553,6 +8452,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -7579,7 +8488,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -7610,7 +8519,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7692,6 +8602,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -7724,7 +8639,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -7796,6 +8711,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -7822,7 +8747,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -7853,7 +8778,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7931,6 +8857,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -7961,7 +8892,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -8040,6 +8971,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -8063,7 +8999,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -8123,7 +9059,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows\/{rowId}\/{column}\/decrement": { @@ -8150,7 +9102,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -8236,6 +9188,11 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -8268,7 +9225,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -8354,6 +9311,11 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9935,6 +10897,34 @@ "localeCodes": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "$ref": "#\/components\/schemas\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "row": { "description": "Row", "type": "object", @@ -11839,6 +12829,59 @@ "recoveryCode": true } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 02d97fffc7..1ff651107b 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -4888,7 +4888,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 496, + "weight": 508, "cookies": false, "type": "", "demo": "console\/get-resource.md", @@ -5205,6 +5205,424 @@ } } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/databases\/usage": { "get": { "summary": "Get databases usage stats", @@ -9460,6 +9878,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -9518,7 +9946,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9549,7 +9978,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9634,6 +10064,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9693,7 +10128,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9759,6 +10195,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -9860,6 +10301,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9953,6 +10399,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10058,6 +10509,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -10116,7 +10577,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -10199,6 +10661,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -10312,6 +10779,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10396,7 +10868,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/logs": { @@ -10607,6 +11095,11 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10726,6 +11219,11 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10960,7 +11458,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -11540,7 +12038,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -11613,7 +12111,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -11846,7 +12344,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -11895,7 +12393,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -11945,7 +12443,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 466, + "weight": 478, "cookies": false, "type": "", "demo": "functions\/list-templates.md", @@ -12045,7 +12543,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 465, + "weight": 477, "cookies": false, "type": "", "demo": "functions\/get-template.md", @@ -12105,7 +12603,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 459, + "weight": 471, "cookies": false, "type": "", "demo": "functions\/list-usage.md", @@ -12177,7 +12675,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -12236,7 +12734,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -12466,7 +12964,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -12527,7 +13025,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -12607,7 +13105,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -12690,7 +13188,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -12786,7 +13284,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -12871,7 +13369,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -12974,7 +13472,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -13071,7 +13569,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -13133,7 +13631,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -13197,7 +13695,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -13287,7 +13785,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -13358,7 +13856,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -13433,7 +13931,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -13509,9 +14007,9 @@ "x-enum-keys": [] }, "headers": { - "type": "string", + "type": "object", "description": "HTTP headers of execution. Defaults to empty.", - "x-example": null + "x-example": "{}" }, "scheduledAt": { "type": "string", @@ -13549,7 +14047,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -13614,7 +14112,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -13685,7 +14183,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 458, + "weight": 470, "cookies": false, "type": "", "demo": "functions\/get-usage.md", @@ -13767,7 +14265,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -13826,7 +14324,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -13917,7 +14415,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -13986,7 +14484,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -14077,7 +14575,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -16411,7 +16909,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -16592,7 +17090,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -21190,7 +21688,7 @@ "resourceId": { "type": "string", "description": "Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "internalFile": { "type": "boolean", @@ -22433,7 +22931,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 436, + "weight": 448, "cookies": false, "type": "", "demo": "projects\/list.md", @@ -24068,7 +24566,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 434, + "weight": 446, "cookies": false, "type": "", "demo": "projects\/list-dev-keys.md", @@ -24106,7 +24604,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: accessedAt, expire", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -24136,7 +24637,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 431, + "weight": 443, "cookies": false, "type": "", "demo": "projects\/create-dev-key.md", @@ -24221,7 +24722,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 433, + "weight": 445, "cookies": false, "type": "", "demo": "projects\/get-dev-key.md", @@ -24289,7 +24790,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 432, + "weight": 444, "cookies": false, "type": "", "demo": "projects\/update-dev-key.md", @@ -24375,7 +24876,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 435, + "weight": 447, "cookies": false, "type": "", "demo": "projects\/delete-dev-key.md", @@ -28209,7 +28710,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 502, + "weight": 514, "cookies": false, "type": "", "demo": "proxy\/list-rules.md", @@ -28283,7 +28784,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 497, + "weight": 509, "cookies": false, "type": "", "demo": "proxy\/create-api-rule.md", @@ -28350,7 +28851,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 499, + "weight": 511, "cookies": false, "type": "", "demo": "proxy\/create-function-rule.md", @@ -28428,7 +28929,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 500, + "weight": 512, "cookies": false, "type": "", "demo": "proxy\/create-redirect-rule.md", @@ -28541,7 +29042,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 498, + "weight": 510, "cookies": false, "type": "", "demo": "proxy\/create-site-rule.md", @@ -28619,7 +29120,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 501, + "weight": 513, "cookies": false, "type": "", "demo": "proxy\/get-rule.md", @@ -28670,7 +29171,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 503, + "weight": 515, "cookies": false, "type": "", "demo": "proxy\/delete-rule.md", @@ -28730,7 +29231,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 504, + "weight": 516, "cookies": false, "type": "", "demo": "proxy\/update-rule-verification.md", @@ -28790,7 +29291,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -28819,7 +29320,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, enabled, framework, deploymentId, buildCommand, installCommand, outputDirectory, installationId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -28860,7 +29364,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -29109,7 +29613,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -29158,7 +29662,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -29208,7 +29712,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 491, + "weight": 503, "cookies": false, "type": "", "demo": "sites\/list-templates.md", @@ -29308,7 +29812,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 492, + "weight": 504, "cookies": false, "type": "", "demo": "sites\/get-template.md", @@ -29368,7 +29872,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 493, + "weight": 505, "cookies": false, "type": "", "demo": "sites\/list-usage.md", @@ -29440,7 +29944,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -29499,7 +30003,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -29744,7 +30248,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -29805,7 +30309,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -29885,7 +30389,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -29968,7 +30472,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -30069,7 +30573,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -30149,7 +30653,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -30252,7 +30756,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -30350,7 +30854,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -30412,7 +30916,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -30476,7 +30980,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -30566,7 +31070,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -30637,7 +31141,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -30676,7 +31180,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: trigger, status, responseStatusCode, duration, requestMethod, requestPath, deploymentId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -30708,7 +31215,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -30770,7 +31277,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -30841,7 +31348,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 494, + "weight": 506, "cookies": false, "type": "", "demo": "sites\/get-usage.md", @@ -30923,7 +31430,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -30982,7 +31489,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -31073,7 +31580,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -31142,7 +31649,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -31233,7 +31740,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -32705,7 +33212,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -32778,7 +33285,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -32833,6 +33340,424 @@ } } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/tablesdb\/usage": { "get": { "summary": "Get TablesDB usage stats", @@ -32857,7 +33782,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 378, + "weight": 384, "cookies": false, "type": "", "demo": "tablesdb\/list-usage.md", @@ -32954,7 +33879,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -33013,7 +33938,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -33089,7 +34014,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -33150,7 +34075,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -33236,7 +34161,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -33343,7 +34268,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -33415,7 +34340,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -33517,7 +34442,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -33591,7 +34516,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -33678,7 +34603,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -33787,7 +34712,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -33901,7 +34826,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -34010,7 +34935,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -34124,7 +35049,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -34233,7 +35158,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -34347,7 +35272,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -34465,7 +35390,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -34588,7 +35513,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -34707,7 +35632,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -34831,7 +35756,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -34950,7 +35875,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -35074,7 +35999,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -35183,7 +36108,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -35297,7 +36222,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -35409,7 +36334,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -35529,7 +36454,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -35641,7 +36566,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -35761,7 +36686,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -35873,7 +36798,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -35993,7 +36918,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -36127,7 +37052,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -36247,7 +37172,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -36366,7 +37291,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -36475,7 +37400,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -36620,7 +37545,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -36694,7 +37619,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -36777,7 +37702,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -36888,7 +37813,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -36973,7 +37898,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -37105,7 +38030,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -37179,7 +38104,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -37262,7 +38187,7 @@ "x-appwrite": { "method": "listTableLogs", "group": "tables", - "weight": 384, + "weight": 390, "cookies": false, "type": "", "demo": "tablesdb\/list-table-logs.md", @@ -37348,7 +38273,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -37410,6 +38335,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -37436,7 +38371,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -37467,7 +38402,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -37494,7 +38430,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37575,6 +38512,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -37605,7 +38547,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -37633,7 +38575,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37695,6 +38638,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -37728,7 +38676,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -37795,6 +38743,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -37825,7 +38778,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -37887,6 +38840,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -37919,7 +38877,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -37991,6 +38949,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -38017,7 +38985,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -38048,7 +39016,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -38126,6 +39095,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38156,7 +39130,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -38235,6 +39209,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38258,7 +39237,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -38318,7 +39297,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows\/{rowId}\/logs": { @@ -38345,7 +39340,7 @@ "x-appwrite": { "method": "listRowLogs", "group": "logs", - "weight": 428, + "weight": 434, "cookies": false, "type": "", "demo": "tablesdb\/list-row-logs.md", @@ -38441,7 +39436,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -38527,6 +39522,11 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38559,7 +39559,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -38645,6 +39645,11 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38677,7 +39682,7 @@ "x-appwrite": { "method": "getTableUsage", "group": null, - "weight": 385, + "weight": 391, "cookies": false, "type": "", "demo": "tablesdb\/get-table-usage.md", @@ -38772,7 +39777,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 377, + "weight": 383, "cookies": false, "type": "", "demo": "tablesdb\/get-usage.md", @@ -39984,7 +40989,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -40034,7 +41039,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: expire", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -40064,7 +41072,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -40153,7 +41161,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -40213,7 +41221,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -40283,7 +41291,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -46107,6 +47115,34 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "$ref": "#\/components\/schemas\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "migrationList": { "description": "Migrations List", "type": "object", @@ -56505,6 +57541,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 09d53dbdf0..987ae7fece 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -4743,6 +4743,436 @@ } } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/databases\/{databaseId}": { "get": { "summary": "Get database", @@ -8938,6 +9368,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -8997,7 +9437,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9029,7 +9470,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9116,6 +9558,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9176,7 +9623,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9243,6 +9691,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -9345,6 +9798,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9439,6 +9897,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9546,6 +10009,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -9605,7 +10078,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9690,6 +10164,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -9805,6 +10284,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9891,7 +10375,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { @@ -10007,6 +10507,11 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10128,6 +10633,11 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10364,7 +10874,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -10542,7 +11052,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -10616,7 +11126,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -10850,7 +11360,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -10900,7 +11410,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -10951,7 +11461,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -11011,7 +11521,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -11242,7 +11752,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -11304,7 +11814,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -11385,7 +11895,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -11469,7 +11979,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -11566,7 +12076,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -11652,7 +12162,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -11756,7 +12266,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -11854,7 +12364,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -11917,7 +12427,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -11982,7 +12492,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -12073,7 +12583,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -12145,7 +12655,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -12222,7 +12732,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -12300,9 +12810,9 @@ "x-enum-keys": [] }, "headers": { - "type": "string", + "type": "object", "description": "HTTP headers of execution. Defaults to empty.", - "x-example": null + "x-example": "{}" }, "scheduledAt": { "type": "string", @@ -12340,7 +12850,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -12407,7 +12917,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -12479,7 +12989,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -12539,7 +13049,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -12631,7 +13141,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -12701,7 +13211,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -12793,7 +13303,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -15174,7 +15684,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -15356,7 +15866,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -19717,7 +20227,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -19747,7 +20257,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, enabled, framework, deploymentId, buildCommand, installCommand, outputDirectory, installationId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -19788,7 +20301,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -20038,7 +20551,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -20088,7 +20601,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -20139,7 +20652,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -20199,7 +20712,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -20445,7 +20958,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -20507,7 +21020,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -20588,7 +21101,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -20672,7 +21185,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -20774,7 +21287,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -20855,7 +21368,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -20959,7 +21472,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -21058,7 +21571,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -21121,7 +21634,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -21186,7 +21699,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -21277,7 +21790,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -21349,7 +21862,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -21389,7 +21902,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: trigger, status, responseStatusCode, duration, requestMethod, requestPath, deploymentId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -21421,7 +21937,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -21484,7 +22000,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -21556,7 +22072,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -21616,7 +22132,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -21708,7 +22224,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -21778,7 +22294,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -21870,7 +22386,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -23210,7 +23726,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -23284,7 +23800,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -23340,6 +23856,436 @@ } } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/tablesdb\/{databaseId}": { "get": { "summary": "Get database", @@ -23364,7 +24310,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -23424,7 +24370,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -23501,7 +24447,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -23563,7 +24509,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -23650,7 +24596,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -23758,7 +24704,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -23831,7 +24777,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -23934,7 +24880,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -24009,7 +24955,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -24097,7 +25043,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -24207,7 +25153,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -24322,7 +25268,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -24432,7 +25378,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -24547,7 +25493,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -24657,7 +25603,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -24772,7 +25718,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -24891,7 +25837,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -25015,7 +25961,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -25135,7 +26081,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -25260,7 +26206,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -25380,7 +26326,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -25505,7 +26451,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -25615,7 +26561,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -25730,7 +26676,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -25843,7 +26789,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -25964,7 +26910,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -26077,7 +27023,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -26198,7 +27144,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -26311,7 +27257,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -26432,7 +27378,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -26567,7 +27513,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -26688,7 +27634,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -26808,7 +27754,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -26918,7 +27864,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -27064,7 +28010,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -27139,7 +28085,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -27223,7 +28169,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -27335,7 +28281,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -27421,7 +28367,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -27554,7 +28500,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -27629,7 +28575,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -27713,7 +28659,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -27777,6 +28723,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -27803,7 +28759,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -27835,7 +28791,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -27863,7 +28820,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -27946,6 +28904,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -27976,7 +28939,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -28005,7 +28968,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -28068,6 +29032,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -28101,7 +29070,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -28169,6 +29138,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28199,7 +29173,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -28262,6 +29236,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28294,7 +29273,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -28368,6 +29347,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -28394,7 +29383,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -28426,7 +29415,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -28506,6 +29496,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28536,7 +29531,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -28617,6 +29612,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28640,7 +29640,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -28702,7 +29702,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows\/{rowId}\/{column}\/decrement": { @@ -28729,7 +29745,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -28817,6 +29833,11 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28849,7 +29870,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -28937,6 +29958,11 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -30024,7 +31050,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -30075,7 +31101,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: expire", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -30105,7 +31134,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -30195,7 +31224,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -30256,7 +31285,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -30327,7 +31356,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -35033,6 +36062,34 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "$ref": "#\/components\/schemas\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "specificationList": { "description": "Specifications List", "type": "object", @@ -41438,6 +42495,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index d226fcc4e1..a24d36fe76 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -4806,6 +4806,424 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents": { "get": { "summary": "List documents", @@ -4893,6 +5311,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -4951,7 +5379,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5037,6 +5466,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5142,6 +5576,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -5200,7 +5644,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5283,6 +5728,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -5396,6 +5846,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5480,7 +5935,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { @@ -5594,6 +6065,11 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5713,6 +6189,11 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -5745,7 +6226,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -5820,7 +6301,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -5896,9 +6377,9 @@ "x-enum-keys": [] }, "headers": { - "type": "string", + "type": "object", "description": "HTTP headers of execution. Defaults to empty.", - "x-example": null + "x-example": "{}" }, "scheduledAt": { "type": "string", @@ -5936,7 +6417,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -7467,6 +7948,424 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows": { "get": { "summary": "List rows", @@ -7491,7 +8390,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -7553,6 +8452,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -7579,7 +8488,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -7610,7 +8519,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7692,6 +8602,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -7724,7 +8639,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -7796,6 +8711,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -7822,7 +8747,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -7853,7 +8778,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7931,6 +8857,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -7961,7 +8892,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -8040,6 +8971,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -8063,7 +8999,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -8123,7 +9059,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows\/{rowId}\/{column}\/decrement": { @@ -8150,7 +9102,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -8236,6 +9188,11 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -8268,7 +9225,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -8354,6 +9311,11 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9935,6 +10897,34 @@ "localeCodes": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "$ref": "#\/components\/schemas\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "row": { "description": "Row", "type": "object", @@ -11839,6 +12829,59 @@ "recoveryCode": true } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 02d97fffc7..3316be2f8b 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -4888,7 +4888,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 496, + "weight": 508, "cookies": false, "type": "", "demo": "console\/get-resource.md", @@ -5205,6 +5205,424 @@ } } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/databases\/usage": { "get": { "summary": "Get databases usage stats", @@ -9460,6 +9878,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -9518,7 +9946,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9549,7 +9978,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9634,6 +10064,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9693,7 +10128,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9759,6 +10195,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -9860,6 +10301,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9953,6 +10399,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10058,6 +10509,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -10116,7 +10577,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -10199,6 +10661,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -10312,6 +10779,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10396,7 +10868,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/logs": { @@ -10607,6 +11095,11 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10726,6 +11219,11 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10960,7 +11458,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -11540,7 +12038,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -11613,7 +12111,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -11846,7 +12344,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -11895,7 +12393,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -11945,7 +12443,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 466, + "weight": 478, "cookies": false, "type": "", "demo": "functions\/list-templates.md", @@ -12045,7 +12543,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 465, + "weight": 477, "cookies": false, "type": "", "demo": "functions\/get-template.md", @@ -12105,7 +12603,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 459, + "weight": 471, "cookies": false, "type": "", "demo": "functions\/list-usage.md", @@ -12177,7 +12675,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -12236,7 +12734,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -12466,7 +12964,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -12527,7 +13025,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -12607,7 +13105,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -12690,7 +13188,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -12786,7 +13284,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -12871,7 +13369,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -12974,7 +13472,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -13071,7 +13569,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -13133,7 +13631,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -13197,7 +13695,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -13287,7 +13785,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -13358,7 +13856,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -13433,7 +13931,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -13509,9 +14007,9 @@ "x-enum-keys": [] }, "headers": { - "type": "string", + "type": "object", "description": "HTTP headers of execution. Defaults to empty.", - "x-example": null + "x-example": "{}" }, "scheduledAt": { "type": "string", @@ -13549,7 +14047,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -13614,7 +14112,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -13685,7 +14183,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 458, + "weight": 470, "cookies": false, "type": "", "demo": "functions\/get-usage.md", @@ -13767,7 +14265,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -13826,7 +14324,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -13917,7 +14415,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -13986,7 +14484,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -14077,7 +14575,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -16411,7 +16909,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -16592,7 +17090,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -21190,7 +21688,7 @@ "resourceId": { "type": "string", "description": "Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "internalFile": { "type": "boolean", @@ -22433,7 +22931,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 436, + "weight": 448, "cookies": false, "type": "", "demo": "projects\/list.md", @@ -24068,7 +24566,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 434, + "weight": 446, "cookies": false, "type": "", "demo": "projects\/list-dev-keys.md", @@ -24106,7 +24604,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: accessedAt, expire", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -24136,7 +24637,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 431, + "weight": 443, "cookies": false, "type": "", "demo": "projects\/create-dev-key.md", @@ -24221,7 +24722,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 433, + "weight": 445, "cookies": false, "type": "", "demo": "projects\/get-dev-key.md", @@ -24289,7 +24790,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 432, + "weight": 444, "cookies": false, "type": "", "demo": "projects\/update-dev-key.md", @@ -24375,7 +24876,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 435, + "weight": 447, "cookies": false, "type": "", "demo": "projects\/delete-dev-key.md", @@ -28209,7 +28710,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 502, + "weight": 514, "cookies": false, "type": "", "demo": "proxy\/list-rules.md", @@ -28283,7 +28784,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 497, + "weight": 509, "cookies": false, "type": "", "demo": "proxy\/create-api-rule.md", @@ -28350,7 +28851,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 499, + "weight": 511, "cookies": false, "type": "", "demo": "proxy\/create-function-rule.md", @@ -28428,7 +28929,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 500, + "weight": 512, "cookies": false, "type": "", "demo": "proxy\/create-redirect-rule.md", @@ -28541,7 +29042,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 498, + "weight": 510, "cookies": false, "type": "", "demo": "proxy\/create-site-rule.md", @@ -28619,7 +29120,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 501, + "weight": 513, "cookies": false, "type": "", "demo": "proxy\/get-rule.md", @@ -28670,7 +29171,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 503, + "weight": 515, "cookies": false, "type": "", "demo": "proxy\/delete-rule.md", @@ -28730,7 +29231,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 504, + "weight": 516, "cookies": false, "type": "", "demo": "proxy\/update-rule-verification.md", @@ -28790,7 +29291,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -28819,7 +29320,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, enabled, framework, deploymentId, buildCommand, installCommand, outputDirectory, installationId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -28860,7 +29364,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -29109,7 +29613,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -29158,7 +29662,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -29208,7 +29712,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 491, + "weight": 503, "cookies": false, "type": "", "demo": "sites\/list-templates.md", @@ -29308,7 +29812,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 492, + "weight": 504, "cookies": false, "type": "", "demo": "sites\/get-template.md", @@ -29368,7 +29872,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 493, + "weight": 505, "cookies": false, "type": "", "demo": "sites\/list-usage.md", @@ -29440,7 +29944,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -29499,7 +30003,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -29744,7 +30248,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -29805,7 +30309,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -29885,7 +30389,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -29968,7 +30472,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -30069,7 +30573,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -30149,7 +30653,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -30252,7 +30756,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -30350,7 +30854,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -30412,7 +30916,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -30476,7 +30980,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -30566,7 +31070,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -30637,7 +31141,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -30676,7 +31180,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: trigger, status, responseStatusCode, duration, requestMethod, requestPath, deploymentId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -30708,7 +31215,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -30770,7 +31277,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -30841,7 +31348,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 494, + "weight": 506, "cookies": false, "type": "", "demo": "sites\/get-usage.md", @@ -30923,7 +31430,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -30982,7 +31489,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -31073,7 +31580,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -31142,7 +31649,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -31233,7 +31740,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -32705,7 +33212,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -32778,7 +33285,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -32833,6 +33340,424 @@ } } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/tablesdb\/usage": { "get": { "summary": "Get TablesDB usage stats", @@ -32857,7 +33782,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 378, + "weight": 384, "cookies": false, "type": "", "demo": "tablesdb\/list-usage.md", @@ -32954,7 +33879,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -33013,7 +33938,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -33089,7 +34014,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -33150,7 +34075,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -33236,7 +34161,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -33343,7 +34268,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -33415,7 +34340,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -33517,7 +34442,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -33591,7 +34516,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -33678,7 +34603,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -33787,7 +34712,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -33901,7 +34826,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -34010,7 +34935,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -34124,7 +35049,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -34233,7 +35158,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -34347,7 +35272,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -34465,7 +35390,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -34588,7 +35513,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -34707,7 +35632,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -34831,7 +35756,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -34950,7 +35875,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -35074,7 +35999,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -35183,7 +36108,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -35297,7 +36222,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -35409,7 +36334,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -35529,7 +36454,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -35641,7 +36566,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -35761,7 +36686,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -35873,7 +36798,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -35993,7 +36918,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -36127,7 +37052,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -36247,7 +37172,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -36366,7 +37291,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -36475,7 +37400,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -36620,7 +37545,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -36694,7 +37619,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -36777,7 +37702,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -36888,7 +37813,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -36973,7 +37898,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -37105,7 +38030,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -37179,7 +38104,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -37262,7 +38187,7 @@ "x-appwrite": { "method": "listTableLogs", "group": "tables", - "weight": 384, + "weight": 390, "cookies": false, "type": "", "demo": "tablesdb\/list-table-logs.md", @@ -37348,7 +38273,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -37410,6 +38335,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -37436,7 +38371,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -37467,7 +38402,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -37494,7 +38430,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37575,6 +38512,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -37605,7 +38547,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -37633,7 +38575,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37695,6 +38638,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -37728,7 +38676,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -37795,6 +38743,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -37825,7 +38778,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -37887,6 +38840,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -37919,7 +38877,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -37991,6 +38949,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -38017,7 +38985,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -38048,7 +39016,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -38126,6 +39095,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38156,7 +39130,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -38235,6 +39209,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38258,7 +39237,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -38318,7 +39297,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows\/{rowId}\/logs": { @@ -38345,7 +39340,7 @@ "x-appwrite": { "method": "listRowLogs", "group": "logs", - "weight": 428, + "weight": 434, "cookies": false, "type": "", "demo": "tablesdb\/list-row-logs.md", @@ -38441,7 +39436,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -38527,6 +39522,11 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38559,7 +39559,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -38645,6 +39645,11 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -38677,7 +39682,7 @@ "x-appwrite": { "method": "getTableUsage", "group": null, - "weight": 385, + "weight": 391, "cookies": false, "type": "", "demo": "tablesdb\/get-table-usage.md", @@ -38772,7 +39777,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 377, + "weight": 383, "cookies": false, "type": "", "demo": "tablesdb\/get-usage.md", @@ -39984,7 +40989,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -40034,7 +41039,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: expire", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -40064,7 +41072,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -40153,7 +41161,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -40213,7 +41221,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -40283,7 +41291,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -46107,6 +47115,34 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "$ref": "#\/components\/schemas\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "migrationList": { "description": "Migrations List", "type": "object", @@ -56505,6 +57541,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 09d53dbdf0..a2d91def99 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -4743,6 +4743,436 @@ } } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/databases\/{databaseId}": { "get": { "summary": "Get database", @@ -8938,6 +9368,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -8997,7 +9437,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9029,7 +9470,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9116,6 +9558,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9176,7 +9623,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9243,6 +9691,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -9345,6 +9798,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9439,6 +9897,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9546,6 +10009,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -9605,7 +10078,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9690,6 +10164,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -9805,6 +10284,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -9891,7 +10375,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { @@ -10007,6 +10507,11 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10128,6 +10633,11 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -10364,7 +10874,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -10542,7 +11052,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -10616,7 +11126,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -10850,7 +11360,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -10900,7 +11410,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -10951,7 +11461,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -11011,7 +11521,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -11242,7 +11752,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -11304,7 +11814,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -11385,7 +11895,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -11469,7 +11979,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -11566,7 +12076,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -11652,7 +12162,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -11756,7 +12266,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -11854,7 +12364,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -11917,7 +12427,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -11982,7 +12492,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -12073,7 +12583,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -12145,7 +12655,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -12222,7 +12732,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -12300,9 +12810,9 @@ "x-enum-keys": [] }, "headers": { - "type": "string", + "type": "object", "description": "HTTP headers of execution. Defaults to empty.", - "x-example": null + "x-example": "{}" }, "scheduledAt": { "type": "string", @@ -12340,7 +12850,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -12407,7 +12917,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -12479,7 +12989,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -12539,7 +13049,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -12631,7 +13141,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -12701,7 +13211,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -12793,7 +13303,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -15174,7 +15684,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -15356,7 +15866,7 @@ "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -19717,7 +20227,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -19747,7 +20257,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, enabled, framework, deploymentId, buildCommand, installCommand, outputDirectory, installationId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -19788,7 +20301,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -20038,7 +20551,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -20088,7 +20601,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -20139,7 +20652,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -20199,7 +20712,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -20445,7 +20958,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -20507,7 +21020,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -20588,7 +21101,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -20672,7 +21185,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -20774,7 +21287,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -20855,7 +21368,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -20959,7 +21472,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -21058,7 +21571,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -21121,7 +21634,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -21186,7 +21699,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -21277,7 +21790,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -21349,7 +21862,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -21389,7 +21902,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: trigger, status, responseStatusCode, duration, requestMethod, requestPath, deploymentId", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -21421,7 +21937,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -21484,7 +22000,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -21556,7 +22072,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -21616,7 +22132,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -21708,7 +22224,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -21778,7 +22294,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -21870,7 +22386,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -23210,7 +23726,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -23284,7 +23800,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -23340,6 +23856,436 @@ } } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transactionList" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "x-example": 60 + } + } + } + } + } + } + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "x-example": false + } + } + } + } + } + } + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/transaction" + } + } + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + }, "\/tablesdb\/{databaseId}": { "get": { "summary": "Get database", @@ -23364,7 +24310,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -23424,7 +24370,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -23501,7 +24447,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -23563,7 +24509,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -23650,7 +24596,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -23758,7 +24704,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -23831,7 +24777,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -23934,7 +24880,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -24009,7 +24955,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -24097,7 +25043,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -24207,7 +25153,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -24322,7 +25268,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -24432,7 +25378,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -24547,7 +25493,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -24657,7 +25603,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -24772,7 +25718,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -24891,7 +25837,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -25015,7 +25961,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -25135,7 +26081,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -25260,7 +26206,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -25380,7 +26326,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -25505,7 +26451,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -25615,7 +26561,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -25730,7 +26676,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -25843,7 +26789,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -25964,7 +26910,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -26077,7 +27023,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -26198,7 +27144,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -26311,7 +27257,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -26432,7 +27378,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -26567,7 +27513,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -26688,7 +27634,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -26808,7 +27754,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -26918,7 +27864,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -27064,7 +28010,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -27139,7 +28085,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -27223,7 +28169,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -27335,7 +28281,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -27421,7 +28367,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -27554,7 +28500,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -27629,7 +28575,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -27713,7 +28659,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -27777,6 +28723,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -27803,7 +28759,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -27835,7 +28791,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -27863,7 +28820,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -27946,6 +28904,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -27976,7 +28939,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -28005,7 +28968,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -28068,6 +29032,11 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } }, "required": [ @@ -28101,7 +29070,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -28169,6 +29138,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28199,7 +29173,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -28262,6 +29236,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28294,7 +29273,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -28368,6 +29347,16 @@ "default": [] }, "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" } ] }, @@ -28394,7 +29383,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -28426,7 +29415,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -28506,6 +29496,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28536,7 +29531,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -28617,6 +29612,11 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28640,7 +29640,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -28702,7 +29702,23 @@ }, "in": "path" } - ] + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" + } + } + } + } + } + } } }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows\/{rowId}\/{column}\/decrement": { @@ -28729,7 +29745,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -28817,6 +29833,11 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -28849,7 +29870,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -28937,6 +29958,11 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "x-example": "" } } } @@ -30024,7 +31050,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -30075,7 +31101,10 @@ "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: expire", "required": false, "schema": { - "type": "string", + "type": "array", + "items": { + "type": "string" + }, "default": [] }, "in": "query" @@ -30105,7 +31134,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -30195,7 +31224,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -30256,7 +31285,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -30327,7 +31356,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -35033,6 +36062,34 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "$ref": "#\/components\/schemas\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "specificationList": { "description": "Specifications List", "type": "object", @@ -41438,6 +42495,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 55911e9556..7297412924 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -4948,6 +4948,419 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents": { "get": { "summary": "List documents", @@ -5029,6 +5442,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -5088,7 +5509,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5173,6 +5595,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5269,6 +5697,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -5328,7 +5764,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5406,6 +5843,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -5514,6 +5957,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5593,6 +6042,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -5702,6 +6166,12 @@ "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5814,6 +6284,12 @@ "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5845,7 +6321,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -5918,7 +6394,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -6035,7 +6511,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -7549,6 +8025,419 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows": { "get": { "summary": "List rows", @@ -7573,7 +8462,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -7629,6 +8518,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -7657,7 +8554,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -7687,7 +8584,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7768,6 +8666,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -7799,7 +8703,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -7863,6 +8767,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -7891,7 +8803,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -7921,7 +8833,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7994,6 +8907,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -8025,7 +8944,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -8098,6 +9017,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -8124,7 +9049,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -8176,6 +9101,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -8206,7 +9146,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -8284,6 +9224,12 @@ "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -8317,7 +9263,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -8395,6 +9341,12 @@ "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9931,6 +10883,35 @@ "localeCodes": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "type": "object", + "$ref": "#\/definitions\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "row": { "description": "Row", "type": "object", @@ -11840,6 +12821,59 @@ "recoveryCode": true } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 6d5721c73b..5e8aad66e4 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -5052,7 +5052,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 496, + "weight": 508, "cookies": false, "type": "", "demo": "console\/get-resource.md", @@ -5367,6 +5367,419 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/databases\/usage": { "get": { "summary": "Get databases usage stats", @@ -9525,6 +9938,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -9584,7 +10005,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9615,7 +10037,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9699,6 +10122,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9759,7 +10188,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9821,6 +10251,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -9920,6 +10356,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10010,6 +10452,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10106,6 +10554,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -10165,7 +10621,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -10243,6 +10700,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -10351,6 +10814,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10430,6 +10899,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -10629,6 +11113,12 @@ "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10741,6 +11231,12 @@ "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10974,7 +11470,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -11524,7 +12020,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -11596,7 +12092,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -11847,7 +12343,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -11896,7 +12392,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -11946,7 +12442,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 466, + "weight": 478, "cookies": false, "type": "", "demo": "functions\/list-templates.md", @@ -12040,7 +12536,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 465, + "weight": 477, "cookies": false, "type": "", "demo": "functions\/get-template.md", @@ -12098,7 +12594,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 459, + "weight": 471, "cookies": false, "type": "", "demo": "functions\/list-usage.md", @@ -12168,7 +12664,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -12227,7 +12723,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -12474,7 +12970,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -12535,7 +13031,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -12612,7 +13108,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -12692,7 +13188,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -12784,7 +13280,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -12869,7 +13365,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -12975,7 +13471,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -13071,7 +13567,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -13133,7 +13629,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -13200,7 +13696,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -13285,7 +13781,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -13352,7 +13848,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -13425,7 +13921,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -13542,7 +14038,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -13606,7 +14102,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -13673,7 +14169,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 458, + "weight": 470, "cookies": false, "type": "", "demo": "functions\/get-usage.md", @@ -13751,7 +14247,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -13810,7 +14306,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -13900,7 +14396,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -13967,7 +14463,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -14059,7 +14555,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -16423,7 +16919,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": "", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -16620,7 +17116,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": null, - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -21355,7 +21851,7 @@ "type": "string", "description": "Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.", "default": null, - "x-example": "[ID1:ID2]" + "x-example": "" }, "internalFile": { "type": "boolean", @@ -22590,7 +23086,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 436, + "weight": 448, "cookies": false, "type": "", "demo": "projects\/list.md", @@ -24233,7 +24729,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 434, + "weight": 446, "cookies": false, "type": "", "demo": "projects\/list-dev-keys.md", @@ -24303,7 +24799,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 431, + "weight": 443, "cookies": false, "type": "", "demo": "projects\/create-dev-key.md", @@ -24386,7 +24882,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 433, + "weight": 445, "cookies": false, "type": "", "demo": "projects\/get-dev-key.md", @@ -24452,7 +24948,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 432, + "weight": 444, "cookies": false, "type": "", "demo": "projects\/update-dev-key.md", @@ -24538,7 +25034,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 435, + "weight": 447, "cookies": false, "type": "", "demo": "projects\/delete-dev-key.md", @@ -28351,7 +28847,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 502, + "weight": 514, "cookies": false, "type": "", "demo": "proxy\/list-rules.md", @@ -28424,7 +28920,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 497, + "weight": 509, "cookies": false, "type": "", "demo": "proxy\/create-api-rule.md", @@ -28494,7 +28990,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 499, + "weight": 511, "cookies": false, "type": "", "demo": "proxy\/create-function-rule.md", @@ -28577,7 +29073,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 500, + "weight": 512, "cookies": false, "type": "", "demo": "proxy\/create-redirect-rule.md", @@ -28697,7 +29193,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 498, + "weight": 510, "cookies": false, "type": "", "demo": "proxy\/create-site-rule.md", @@ -28778,7 +29274,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 501, + "weight": 513, "cookies": false, "type": "", "demo": "proxy\/get-rule.md", @@ -28831,7 +29327,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 503, + "weight": 515, "cookies": false, "type": "", "demo": "proxy\/delete-rule.md", @@ -28891,7 +29387,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 504, + "weight": 516, "cookies": false, "type": "", "demo": "proxy\/update-rule-verification.md", @@ -28949,7 +29445,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -29021,7 +29517,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -29288,7 +29784,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -29337,7 +29833,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -29387,7 +29883,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 491, + "weight": 503, "cookies": false, "type": "", "demo": "sites\/list-templates.md", @@ -29481,7 +29977,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 492, + "weight": 504, "cookies": false, "type": "", "demo": "sites\/get-template.md", @@ -29539,7 +30035,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 493, + "weight": 505, "cookies": false, "type": "", "demo": "sites\/list-usage.md", @@ -29609,7 +30105,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -29668,7 +30164,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -29930,7 +30426,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -29991,7 +30487,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -30068,7 +30564,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -30148,7 +30644,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -30248,7 +30744,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -30327,7 +30823,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -30433,7 +30929,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -30530,7 +31026,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -30592,7 +31088,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -30659,7 +31155,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -30744,7 +31240,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -30811,7 +31307,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -30882,7 +31378,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -30946,7 +31442,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -31013,7 +31509,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 494, + "weight": 506, "cookies": false, "type": "", "demo": "sites\/get-usage.md", @@ -31091,7 +31587,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -31150,7 +31646,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -31240,7 +31736,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -31307,7 +31803,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -31399,7 +31895,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -32833,7 +33329,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -32905,7 +33401,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -32963,6 +33459,419 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/tablesdb\/usage": { "get": { "summary": "Get TablesDB usage stats", @@ -32987,7 +33896,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 378, + "weight": 384, "cookies": false, "type": "", "demo": "tablesdb\/list-usage.md", @@ -33082,7 +33991,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -33141,7 +34050,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -33219,7 +34128,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -33278,7 +34187,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -33361,7 +34270,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -33469,7 +34378,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -33539,7 +34448,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -33643,7 +34552,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -33713,7 +34622,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -33797,7 +34706,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -33906,7 +34815,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -34017,7 +34926,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -34126,7 +35035,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -34237,7 +35146,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -34346,7 +35255,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -34457,7 +35366,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -34576,7 +35485,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -34697,7 +35606,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -34818,7 +35727,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -34941,7 +35850,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -35062,7 +35971,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -35185,7 +36094,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -35294,7 +36203,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -35405,7 +36314,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -35509,7 +36418,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -35619,7 +36528,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -35723,7 +36632,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -35833,7 +36742,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -35937,7 +36846,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -36047,7 +36956,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -36183,7 +37092,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -36305,7 +37214,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -36422,7 +37331,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -36531,7 +37440,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -36671,7 +37580,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -36743,7 +37652,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -36822,7 +37731,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -36927,7 +37836,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -37009,7 +37918,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -37140,7 +38049,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -37212,7 +38121,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -37289,7 +38198,7 @@ "x-appwrite": { "method": "listTableLogs", "group": "tables", - "weight": 384, + "weight": 390, "cookies": false, "type": "", "demo": "tablesdb\/list-table-logs.md", @@ -37370,7 +38279,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -37426,6 +38335,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -37454,7 +38371,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -37484,7 +38401,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -37511,7 +38429,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37591,6 +38510,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -37622,7 +38547,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -37650,7 +38575,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37708,6 +38634,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -37742,7 +38674,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -37806,6 +38738,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -37837,7 +38775,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -37895,6 +38833,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -37926,7 +38870,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -37990,6 +38934,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -38018,7 +38970,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -38048,7 +39000,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -38121,6 +39074,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38152,7 +39111,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -38225,6 +39184,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38251,7 +39216,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -38303,6 +39268,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -38331,7 +39311,7 @@ "x-appwrite": { "method": "listRowLogs", "group": "logs", - "weight": 428, + "weight": 434, "cookies": false, "type": "", "demo": "tablesdb\/list-row-logs.md", @@ -38422,7 +39402,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -38500,6 +39480,12 @@ "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38533,7 +39519,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -38611,6 +39597,12 @@ "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38642,7 +39634,7 @@ "x-appwrite": { "method": "getTableUsage", "group": null, - "weight": 385, + "weight": 391, "cookies": false, "type": "", "demo": "tablesdb\/get-table-usage.md", @@ -38731,7 +39723,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 377, + "weight": 383, "cookies": false, "type": "", "demo": "tablesdb\/get-usage.md", @@ -39916,7 +40908,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -39996,7 +40988,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -40080,7 +41072,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -40140,7 +41132,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -40211,7 +41203,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -46040,6 +47032,35 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "type": "object", + "$ref": "#\/definitions\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "migrationList": { "description": "Migrations List", "type": "object", @@ -56554,6 +57575,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 98077f1050..9dd44734bb 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -4893,6 +4893,431 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/databases\/{databaseId}": { "get": { "summary": "Get database", @@ -8993,6 +9418,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -9053,7 +9486,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9085,7 +9519,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9171,6 +9606,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9232,7 +9673,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9295,6 +9737,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -9395,6 +9843,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9486,6 +9940,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9584,6 +10044,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -9644,7 +10112,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9724,6 +10193,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -9834,6 +10309,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9915,6 +10396,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -10026,6 +10522,12 @@ "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10140,6 +10642,12 @@ "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10375,7 +10883,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -10541,7 +11049,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -10614,7 +11122,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -10866,7 +11374,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -10916,7 +11424,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -10967,7 +11475,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -11027,7 +11535,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -11275,7 +11783,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -11337,7 +11845,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -11415,7 +11923,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -11496,7 +12004,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -11589,7 +12097,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -11675,7 +12183,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -11782,7 +12290,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -11879,7 +12387,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -11942,7 +12450,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -12010,7 +12518,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -12096,7 +12604,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -12164,7 +12672,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -12239,7 +12747,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -12358,7 +12866,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -12424,7 +12932,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -12492,7 +13000,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -12552,7 +13060,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -12643,7 +13151,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -12711,7 +13219,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -12804,7 +13312,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -15215,7 +15723,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": "", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -15413,7 +15921,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": null, - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -19909,7 +20417,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -19982,7 +20490,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -20250,7 +20758,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -20300,7 +20808,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -20351,7 +20859,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -20411,7 +20919,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -20674,7 +21182,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -20736,7 +21244,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -20814,7 +21322,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -20895,7 +21403,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -20996,7 +21504,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -21076,7 +21584,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -21183,7 +21691,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -21281,7 +21789,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -21344,7 +21852,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -21412,7 +21920,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -21498,7 +22006,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -21566,7 +22074,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -21638,7 +22146,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -21703,7 +22211,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -21771,7 +22279,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -21831,7 +22339,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -21922,7 +22430,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -21990,7 +22498,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -22083,7 +22591,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -23391,7 +23899,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -23464,7 +23972,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -23523,6 +24031,431 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/tablesdb\/{databaseId}": { "get": { "summary": "Get database", @@ -23547,7 +24480,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -23607,7 +24540,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -23686,7 +24619,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -23746,7 +24679,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -23830,7 +24763,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -23939,7 +24872,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -24010,7 +24943,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -24115,7 +25048,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -24186,7 +25119,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -24271,7 +25204,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -24381,7 +25314,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -24493,7 +25426,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -24603,7 +25536,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -24715,7 +25648,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -24825,7 +25758,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -24937,7 +25870,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -25057,7 +25990,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -25179,7 +26112,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -25301,7 +26234,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -25425,7 +26358,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -25547,7 +26480,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -25671,7 +26604,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -25781,7 +26714,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -25893,7 +26826,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -25998,7 +26931,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -26109,7 +27042,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -26214,7 +27147,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -26325,7 +27258,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -26430,7 +27363,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -26541,7 +27474,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -26678,7 +27611,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -26801,7 +27734,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -26919,7 +27852,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -27029,7 +27962,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -27170,7 +28103,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -27243,7 +28176,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -27323,7 +28256,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -27429,7 +28362,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -27512,7 +28445,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -27644,7 +28577,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -27717,7 +28650,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -27795,7 +28728,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -27853,6 +28786,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -27881,7 +28822,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -27912,7 +28853,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -27940,7 +28882,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -28022,6 +28965,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28053,7 +29002,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -28082,7 +29031,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -28141,6 +29091,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -28175,7 +29131,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -28240,6 +29196,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28271,7 +29233,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -28330,6 +29292,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28361,7 +29329,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -28427,6 +29395,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -28455,7 +29431,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -28486,7 +29462,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -28561,6 +29538,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28592,7 +29575,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -28667,6 +29650,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28693,7 +29682,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -28747,6 +29736,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -28777,7 +29781,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -28857,6 +29861,12 @@ "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28890,7 +29900,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -28970,6 +29980,12 @@ "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -30036,7 +31052,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -30117,7 +31133,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -30202,7 +31218,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -30263,7 +31279,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -30335,7 +31351,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -35059,6 +36075,35 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "type": "object", + "$ref": "#\/definitions\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "specificationList": { "description": "Specifications List", "type": "object", @@ -41477,6 +42522,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 55911e9556..4d0e75c6fa 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -4948,6 +4948,419 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/documents": { "get": { "summary": "List documents", @@ -5029,6 +5442,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -5088,7 +5509,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5173,6 +5595,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5269,6 +5697,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -5328,7 +5764,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -5406,6 +5843,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -5514,6 +5957,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5593,6 +6042,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -5702,6 +6166,12 @@ "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5814,6 +6284,12 @@ "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -5845,7 +6321,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -5918,7 +6394,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -6035,7 +6511,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -7549,6 +8025,419 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/tablesdb\/{databaseId}\/tables\/{tableId}\/rows": { "get": { "summary": "List rows", @@ -7573,7 +8462,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -7629,6 +8518,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -7657,7 +8554,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -7687,7 +8584,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7768,6 +8666,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -7799,7 +8703,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -7863,6 +8767,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -7891,7 +8803,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -7921,7 +8833,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -7994,6 +8907,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -8025,7 +8944,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -8098,6 +9017,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -8124,7 +9049,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -8176,6 +9101,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -8206,7 +9146,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -8284,6 +9224,12 @@ "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -8317,7 +9263,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -8395,6 +9341,12 @@ "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9931,6 +10883,35 @@ "localeCodes": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "type": "object", + "$ref": "#\/definitions\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "row": { "description": "Row", "type": "object", @@ -11840,6 +12821,59 @@ "recoveryCode": true } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 6d5721c73b..c39987cbaf 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -5052,7 +5052,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 496, + "weight": 508, "cookies": false, "type": "", "demo": "console\/get-resource.md", @@ -5367,6 +5367,419 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/databases\/usage": { "get": { "summary": "Get databases usage stats", @@ -9525,6 +9938,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -9584,7 +10005,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9615,7 +10037,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9699,6 +10122,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9759,7 +10188,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9821,6 +10251,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -9920,6 +10356,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10010,6 +10452,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10106,6 +10554,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -10165,7 +10621,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -10243,6 +10700,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -10351,6 +10814,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10430,6 +10899,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -10629,6 +11113,12 @@ "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10741,6 +11231,12 @@ "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10974,7 +11470,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -11524,7 +12020,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -11596,7 +12092,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -11847,7 +12343,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -11896,7 +12392,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -11946,7 +12442,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 466, + "weight": 478, "cookies": false, "type": "", "demo": "functions\/list-templates.md", @@ -12040,7 +12536,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 465, + "weight": 477, "cookies": false, "type": "", "demo": "functions\/get-template.md", @@ -12098,7 +12594,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 459, + "weight": 471, "cookies": false, "type": "", "demo": "functions\/list-usage.md", @@ -12168,7 +12664,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -12227,7 +12723,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -12474,7 +12970,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -12535,7 +13031,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -12612,7 +13108,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -12692,7 +13188,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -12784,7 +13280,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -12869,7 +13365,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -12975,7 +13471,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -13071,7 +13567,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -13133,7 +13629,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -13200,7 +13696,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -13285,7 +13781,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -13352,7 +13848,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -13425,7 +13921,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -13542,7 +14038,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -13606,7 +14102,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -13673,7 +14169,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 458, + "weight": 470, "cookies": false, "type": "", "demo": "functions\/get-usage.md", @@ -13751,7 +14247,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -13810,7 +14306,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -13900,7 +14396,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -13967,7 +14463,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -14059,7 +14555,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -16423,7 +16919,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": "", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -16620,7 +17116,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": null, - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -21355,7 +21851,7 @@ "type": "string", "description": "Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.", "default": null, - "x-example": "[ID1:ID2]" + "x-example": "" }, "internalFile": { "type": "boolean", @@ -22590,7 +23086,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 436, + "weight": 448, "cookies": false, "type": "", "demo": "projects\/list.md", @@ -24233,7 +24729,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 434, + "weight": 446, "cookies": false, "type": "", "demo": "projects\/list-dev-keys.md", @@ -24303,7 +24799,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 431, + "weight": 443, "cookies": false, "type": "", "demo": "projects\/create-dev-key.md", @@ -24386,7 +24882,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 433, + "weight": 445, "cookies": false, "type": "", "demo": "projects\/get-dev-key.md", @@ -24452,7 +24948,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 432, + "weight": 444, "cookies": false, "type": "", "demo": "projects\/update-dev-key.md", @@ -24538,7 +25034,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 435, + "weight": 447, "cookies": false, "type": "", "demo": "projects\/delete-dev-key.md", @@ -28351,7 +28847,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 502, + "weight": 514, "cookies": false, "type": "", "demo": "proxy\/list-rules.md", @@ -28424,7 +28920,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 497, + "weight": 509, "cookies": false, "type": "", "demo": "proxy\/create-api-rule.md", @@ -28494,7 +28990,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 499, + "weight": 511, "cookies": false, "type": "", "demo": "proxy\/create-function-rule.md", @@ -28577,7 +29073,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 500, + "weight": 512, "cookies": false, "type": "", "demo": "proxy\/create-redirect-rule.md", @@ -28697,7 +29193,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 498, + "weight": 510, "cookies": false, "type": "", "demo": "proxy\/create-site-rule.md", @@ -28778,7 +29274,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 501, + "weight": 513, "cookies": false, "type": "", "demo": "proxy\/get-rule.md", @@ -28831,7 +29327,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 503, + "weight": 515, "cookies": false, "type": "", "demo": "proxy\/delete-rule.md", @@ -28891,7 +29387,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 504, + "weight": 516, "cookies": false, "type": "", "demo": "proxy\/update-rule-verification.md", @@ -28949,7 +29445,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -29021,7 +29517,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -29288,7 +29784,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -29337,7 +29833,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -29387,7 +29883,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 491, + "weight": 503, "cookies": false, "type": "", "demo": "sites\/list-templates.md", @@ -29481,7 +29977,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 492, + "weight": 504, "cookies": false, "type": "", "demo": "sites\/get-template.md", @@ -29539,7 +30035,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 493, + "weight": 505, "cookies": false, "type": "", "demo": "sites\/list-usage.md", @@ -29609,7 +30105,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -29668,7 +30164,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -29930,7 +30426,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -29991,7 +30487,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -30068,7 +30564,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -30148,7 +30644,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -30248,7 +30744,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -30327,7 +30823,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -30433,7 +30929,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -30530,7 +31026,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -30592,7 +31088,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -30659,7 +31155,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -30744,7 +31240,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -30811,7 +31307,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -30882,7 +31378,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -30946,7 +31442,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -31013,7 +31509,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 494, + "weight": 506, "cookies": false, "type": "", "demo": "sites\/get-usage.md", @@ -31091,7 +31587,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -31150,7 +31646,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -31240,7 +31736,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -31307,7 +31803,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -31399,7 +31895,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -32833,7 +33329,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -32905,7 +33401,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -32963,6 +33459,419 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/tablesdb\/usage": { "get": { "summary": "Get TablesDB usage stats", @@ -32987,7 +33896,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 378, + "weight": 384, "cookies": false, "type": "", "demo": "tablesdb\/list-usage.md", @@ -33082,7 +33991,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -33141,7 +34050,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -33219,7 +34128,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -33278,7 +34187,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -33361,7 +34270,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -33469,7 +34378,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -33539,7 +34448,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -33643,7 +34552,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -33713,7 +34622,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -33797,7 +34706,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -33906,7 +34815,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -34017,7 +34926,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -34126,7 +35035,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -34237,7 +35146,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -34346,7 +35255,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -34457,7 +35366,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -34576,7 +35485,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -34697,7 +35606,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -34818,7 +35727,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -34941,7 +35850,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -35062,7 +35971,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -35185,7 +36094,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -35294,7 +36203,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -35405,7 +36314,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -35509,7 +36418,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -35619,7 +36528,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -35723,7 +36632,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -35833,7 +36742,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -35937,7 +36846,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -36047,7 +36956,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -36183,7 +37092,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -36305,7 +37214,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -36422,7 +37331,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -36531,7 +37440,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -36671,7 +37580,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -36743,7 +37652,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -36822,7 +37731,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -36927,7 +37836,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -37009,7 +37918,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -37140,7 +38049,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -37212,7 +38121,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -37289,7 +38198,7 @@ "x-appwrite": { "method": "listTableLogs", "group": "tables", - "weight": 384, + "weight": 390, "cookies": false, "type": "", "demo": "tablesdb\/list-table-logs.md", @@ -37370,7 +38279,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -37426,6 +38335,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -37454,7 +38371,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -37484,7 +38401,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -37511,7 +38429,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37591,6 +38510,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -37622,7 +38547,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -37650,7 +38575,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -37708,6 +38634,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -37742,7 +38674,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -37806,6 +38738,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -37837,7 +38775,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -37895,6 +38833,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -37926,7 +38870,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -37990,6 +38934,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -38018,7 +38970,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -38048,7 +39000,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -38121,6 +39074,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38152,7 +39111,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -38225,6 +39184,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38251,7 +39216,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -38303,6 +39268,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -38331,7 +39311,7 @@ "x-appwrite": { "method": "listRowLogs", "group": "logs", - "weight": 428, + "weight": 434, "cookies": false, "type": "", "demo": "tablesdb\/list-row-logs.md", @@ -38422,7 +39402,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -38500,6 +39480,12 @@ "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38533,7 +39519,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -38611,6 +39597,12 @@ "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -38642,7 +39634,7 @@ "x-appwrite": { "method": "getTableUsage", "group": null, - "weight": 385, + "weight": 391, "cookies": false, "type": "", "demo": "tablesdb\/get-table-usage.md", @@ -38731,7 +39723,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 377, + "weight": 383, "cookies": false, "type": "", "demo": "tablesdb\/get-usage.md", @@ -39916,7 +40908,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -39996,7 +40988,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -40080,7 +41072,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -40140,7 +41132,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -40211,7 +41203,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -46040,6 +47032,35 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "type": "object", + "$ref": "#\/definitions\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "migrationList": { "description": "Migrations List", "type": "object", @@ -56554,6 +57575,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 98077f1050..7e0785fc0a 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -4893,6 +4893,431 @@ ] } }, + "\/databases\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "databasesListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 376, + "cookies": false, + "type": "", + "demo": "databases\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "databasesCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 372, + "cookies": false, + "type": "", + "demo": "databases\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/databases\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "databasesGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 373, + "cookies": false, + "type": "", + "demo": "databases\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "databasesUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 374, + "cookies": false, + "type": "", + "demo": "databases\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "databasesDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "databases" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 375, + "cookies": false, + "type": "", + "demo": "databases\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/databases\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "databasesCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 377, + "cookies": false, + "type": "", + "demo": "databases\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/databases\/{databaseId}": { "get": { "summary": "Get database", @@ -8993,6 +9418,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -9053,7 +9486,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9085,7 +9519,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9171,6 +9606,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9232,7 +9673,8 @@ "parameters": [ "databaseId", "collectionId", - "documents" + "documents", + "transactionId" ], "required": [ "databaseId", @@ -9295,6 +9737,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -9395,6 +9843,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9486,6 +9940,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9584,6 +10044,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -9644,7 +10112,8 @@ "collectionId", "documentId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -9724,6 +10193,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -9834,6 +10309,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -9915,6 +10396,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -10026,6 +10522,12 @@ "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10140,6 +10642,12 @@ "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -10375,7 +10883,7 @@ "tags": [ "databases" ], - "description": "Get index by ID.", + "description": "Get an index by its unique ID.", "responses": { "200": { "description": "Index", @@ -10541,7 +11049,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 440, + "weight": 452, "cookies": false, "type": "", "demo": "functions\/list.md", @@ -10614,7 +11122,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 437, + "weight": 449, "cookies": false, "type": "", "demo": "functions\/create.md", @@ -10866,7 +11374,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 442, + "weight": 454, "cookies": false, "type": "", "demo": "functions\/list-runtimes.md", @@ -10916,7 +11424,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 443, + "weight": 455, "cookies": false, "type": "", "demo": "functions\/list-specifications.md", @@ -10967,7 +11475,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 438, + "weight": 450, "cookies": false, "type": "", "demo": "functions\/get.md", @@ -11027,7 +11535,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 439, + "weight": 451, "cookies": false, "type": "", "demo": "functions\/update.md", @@ -11275,7 +11783,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 441, + "weight": 453, "cookies": false, "type": "", "demo": "functions\/delete.md", @@ -11337,7 +11845,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 446, + "weight": 458, "cookies": false, "type": "", "demo": "functions\/update-function-deployment.md", @@ -11415,7 +11923,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 447, + "weight": 459, "cookies": false, "type": "", "demo": "functions\/list-deployments.md", @@ -11496,7 +12004,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 444, + "weight": 456, "cookies": false, "type": "upload", "demo": "functions\/create-deployment.md", @@ -11589,7 +12097,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 452, + "weight": 464, "cookies": false, "type": "", "demo": "functions\/create-duplicate-deployment.md", @@ -11675,7 +12183,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 449, + "weight": 461, "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", @@ -11782,7 +12290,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 450, + "weight": 462, "cookies": false, "type": "", "demo": "functions\/create-vcs-deployment.md", @@ -11879,7 +12387,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 445, + "weight": 457, "cookies": false, "type": "", "demo": "functions\/get-deployment.md", @@ -11942,7 +12450,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 448, + "weight": 460, "cookies": false, "type": "", "demo": "functions\/delete-deployment.md", @@ -12010,7 +12518,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 451, + "weight": 463, "cookies": false, "type": "location", "demo": "functions\/get-deployment-download.md", @@ -12096,7 +12604,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 453, + "weight": 465, "cookies": false, "type": "", "demo": "functions\/update-deployment-status.md", @@ -12164,7 +12672,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 456, + "weight": 468, "cookies": false, "type": "", "demo": "functions\/list-executions.md", @@ -12239,7 +12747,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 454, + "weight": 466, "cookies": false, "type": "", "demo": "functions\/create-execution.md", @@ -12358,7 +12866,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 455, + "weight": 467, "cookies": false, "type": "", "demo": "functions\/get-execution.md", @@ -12424,7 +12932,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 457, + "weight": 469, "cookies": false, "type": "", "demo": "functions\/delete-execution.md", @@ -12492,7 +13000,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 462, + "weight": 474, "cookies": false, "type": "", "demo": "functions\/list-variables.md", @@ -12552,7 +13060,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 460, + "weight": 472, "cookies": false, "type": "", "demo": "functions\/create-variable.md", @@ -12643,7 +13151,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 461, + "weight": 473, "cookies": false, "type": "", "demo": "functions\/get-variable.md", @@ -12711,7 +13219,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 463, + "weight": 475, "cookies": false, "type": "", "demo": "functions\/update-variable.md", @@ -12804,7 +13312,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 464, + "weight": 476, "cookies": false, "type": "", "demo": "functions\/delete-variable.md", @@ -15215,7 +15723,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": "", - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -15413,7 +15921,7 @@ "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as :.", "default": null, - "x-example": "[ID1:ID2]" + "x-example": "" }, "icon": { "type": "string", @@ -19909,7 +20417,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 469, + "weight": 481, "cookies": false, "type": "", "demo": "sites\/list.md", @@ -19982,7 +20490,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 467, + "weight": 479, "cookies": false, "type": "", "demo": "sites\/create.md", @@ -20250,7 +20758,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 472, + "weight": 484, "cookies": false, "type": "", "demo": "sites\/list-frameworks.md", @@ -20300,7 +20808,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 495, + "weight": 507, "cookies": false, "type": "", "demo": "sites\/list-specifications.md", @@ -20351,7 +20859,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 468, + "weight": 480, "cookies": false, "type": "", "demo": "sites\/get.md", @@ -20411,7 +20919,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 470, + "weight": 482, "cookies": false, "type": "", "demo": "sites\/update.md", @@ -20674,7 +21182,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 471, + "weight": 483, "cookies": false, "type": "", "demo": "sites\/delete.md", @@ -20736,7 +21244,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 478, + "weight": 490, "cookies": false, "type": "", "demo": "sites\/update-site-deployment.md", @@ -20814,7 +21322,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 477, + "weight": 489, "cookies": false, "type": "", "demo": "sites\/list-deployments.md", @@ -20895,7 +21403,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 473, + "weight": 485, "cookies": false, "type": "upload", "demo": "sites\/create-deployment.md", @@ -20996,7 +21504,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 481, + "weight": 493, "cookies": false, "type": "", "demo": "sites\/create-duplicate-deployment.md", @@ -21076,7 +21584,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 474, + "weight": 486, "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", @@ -21183,7 +21691,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 475, + "weight": 487, "cookies": false, "type": "", "demo": "sites\/create-vcs-deployment.md", @@ -21281,7 +21789,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 476, + "weight": 488, "cookies": false, "type": "", "demo": "sites\/get-deployment.md", @@ -21344,7 +21852,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 479, + "weight": 491, "cookies": false, "type": "", "demo": "sites\/delete-deployment.md", @@ -21412,7 +21920,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 480, + "weight": 492, "cookies": false, "type": "location", "demo": "sites\/get-deployment-download.md", @@ -21498,7 +22006,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 482, + "weight": 494, "cookies": false, "type": "", "demo": "sites\/update-deployment-status.md", @@ -21566,7 +22074,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 484, + "weight": 496, "cookies": false, "type": "", "demo": "sites\/list-logs.md", @@ -21638,7 +22146,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 483, + "weight": 495, "cookies": false, "type": "", "demo": "sites\/get-log.md", @@ -21703,7 +22211,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 485, + "weight": 497, "cookies": false, "type": "", "demo": "sites\/delete-log.md", @@ -21771,7 +22279,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 488, + "weight": 500, "cookies": false, "type": "", "demo": "sites\/list-variables.md", @@ -21831,7 +22339,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 486, + "weight": 498, "cookies": false, "type": "", "demo": "sites\/create-variable.md", @@ -21922,7 +22430,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 487, + "weight": 499, "cookies": false, "type": "", "demo": "sites\/get-variable.md", @@ -21990,7 +22498,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 489, + "weight": 501, "cookies": false, "type": "", "demo": "sites\/update-variable.md", @@ -22083,7 +22591,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 490, + "weight": 502, "cookies": false, "type": "", "demo": "sites\/delete-variable.md", @@ -23391,7 +23899,7 @@ "x-appwrite": { "method": "list", "group": "tablesdb", - "weight": 376, + "weight": 382, "cookies": false, "type": "", "demo": "tablesdb\/list.md", @@ -23464,7 +23972,7 @@ "x-appwrite": { "method": "create", "group": "tablesdb", - "weight": 372, + "weight": 378, "cookies": false, "type": "", "demo": "tablesdb\/create.md", @@ -23523,6 +24031,431 @@ ] } }, + "\/tablesdb\/transactions": { + "get": { + "summary": "List transactions", + "operationId": "tablesDBListTransactions", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "List transactions across all databases.", + "responses": { + "200": { + "description": "Transaction List", + "schema": { + "$ref": "#\/definitions\/transactionList" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "listTransactions", + "group": "transactions", + "weight": 441, + "cookies": false, + "type": "", + "demo": "tablesdb\/list-transactions.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/list-transactions.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "queries", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries).", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" + } + ] + }, + "post": { + "summary": "Create transaction", + "operationId": "tablesDBCreateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create a new transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createTransaction", + "group": "transactions", + "weight": 437, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "ttl": { + "type": "integer", + "description": "Seconds before the transaction expires.", + "default": 300, + "x-example": 60 + } + } + } + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}": { + "get": { + "summary": "Get transaction", + "operationId": "tablesDBGetTransaction", + "consumes": [], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Get a transaction by its unique ID.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "getTransaction", + "group": "transactions", + "weight": 438, + "cookies": false, + "type": "", + "demo": "tablesdb\/get-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/get-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.read", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + }, + "patch": { + "summary": "Update transaction", + "operationId": "tablesDBUpdateTransaction", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Update a transaction, to either commit or roll back its operations.", + "responses": { + "200": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "updateTransaction", + "group": "transactions", + "weight": 439, + "cookies": false, + "type": "", + "demo": "tablesdb\/update-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/update-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "commit": { + "type": "boolean", + "description": "Commit transaction?", + "default": false, + "x-example": false + }, + "rollback": { + "type": "boolean", + "description": "Rollback transaction?", + "default": false, + "x-example": false + } + } + } + } + ] + }, + "delete": { + "summary": "Delete transaction", + "operationId": "tablesDBDeleteTransaction", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "tablesDB" + ], + "description": "Delete a transaction by its unique ID.", + "responses": { + "204": { + "description": "No content" + } + }, + "deprecated": false, + "x-appwrite": { + "method": "deleteTransaction", + "group": "transactions", + "weight": 440, + "cookies": false, + "type": "", + "demo": "tablesdb\/delete-transaction.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/delete-transaction.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + } + ] + } + }, + "\/tablesdb\/transactions\/{transactionId}\/operations": { + "post": { + "summary": "Add operations to transaction", + "operationId": "tablesDBCreateOperations", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "tablesDB" + ], + "description": "Create multiple operations in a single transaction.", + "responses": { + "201": { + "description": "Transaction", + "schema": { + "$ref": "#\/definitions\/transaction" + } + } + }, + "deprecated": false, + "x-appwrite": { + "method": "createOperations", + "group": "transactions", + "weight": 442, + "cookies": false, + "type": "", + "demo": "tablesdb\/create-operations.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/tablesdb\/create-operations.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "transactions.write", + "platforms": [ + "server", + "client" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "transactionId", + "description": "Transaction ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "description": "Array of staged operations.", + "default": [], + "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "items": { + "type": "object" + } + } + } + } + } + ] + } + }, "\/tablesdb\/{databaseId}": { "get": { "summary": "Get database", @@ -23547,7 +24480,7 @@ "x-appwrite": { "method": "get", "group": "tablesdb", - "weight": 373, + "weight": 379, "cookies": false, "type": "", "demo": "tablesdb\/get.md", @@ -23607,7 +24540,7 @@ "x-appwrite": { "method": "update", "group": "tablesdb", - "weight": 374, + "weight": 380, "cookies": false, "type": "", "demo": "tablesdb\/update.md", @@ -23686,7 +24619,7 @@ "x-appwrite": { "method": "delete", "group": "tablesdb", - "weight": 375, + "weight": 381, "cookies": false, "type": "", "demo": "tablesdb\/delete.md", @@ -23746,7 +24679,7 @@ "x-appwrite": { "method": "listTables", "group": "tables", - "weight": 383, + "weight": 389, "cookies": false, "type": "", "demo": "tablesdb\/list-tables.md", @@ -23830,7 +24763,7 @@ "x-appwrite": { "method": "createTable", "group": "tables", - "weight": 379, + "weight": 385, "cookies": false, "type": "", "demo": "tablesdb\/create-table.md", @@ -23939,7 +24872,7 @@ "x-appwrite": { "method": "getTable", "group": "tables", - "weight": 380, + "weight": 386, "cookies": false, "type": "", "demo": "tablesdb\/get-table.md", @@ -24010,7 +24943,7 @@ "x-appwrite": { "method": "updateTable", "group": "tables", - "weight": 381, + "weight": 387, "cookies": false, "type": "", "demo": "tablesdb\/update-table.md", @@ -24115,7 +25048,7 @@ "x-appwrite": { "method": "deleteTable", "group": "tables", - "weight": 382, + "weight": 388, "cookies": false, "type": "", "demo": "tablesdb\/delete-table.md", @@ -24186,7 +25119,7 @@ "x-appwrite": { "method": "listColumns", "group": "columns", - "weight": 388, + "weight": 394, "cookies": false, "type": "", "demo": "tablesdb\/list-columns.md", @@ -24271,7 +25204,7 @@ "x-appwrite": { "method": "createBooleanColumn", "group": "columns", - "weight": 389, + "weight": 395, "cookies": false, "type": "", "demo": "tablesdb\/create-boolean-column.md", @@ -24381,7 +25314,7 @@ "x-appwrite": { "method": "updateBooleanColumn", "group": "columns", - "weight": 390, + "weight": 396, "cookies": false, "type": "", "demo": "tablesdb\/update-boolean-column.md", @@ -24493,7 +25426,7 @@ "x-appwrite": { "method": "createDatetimeColumn", "group": "columns", - "weight": 391, + "weight": 397, "cookies": false, "type": "", "demo": "tablesdb\/create-datetime-column.md", @@ -24603,7 +25536,7 @@ "x-appwrite": { "method": "updateDatetimeColumn", "group": "columns", - "weight": 392, + "weight": 398, "cookies": false, "type": "", "demo": "tablesdb\/update-datetime-column.md", @@ -24715,7 +25648,7 @@ "x-appwrite": { "method": "createEmailColumn", "group": "columns", - "weight": 393, + "weight": 399, "cookies": false, "type": "", "demo": "tablesdb\/create-email-column.md", @@ -24825,7 +25758,7 @@ "x-appwrite": { "method": "updateEmailColumn", "group": "columns", - "weight": 394, + "weight": 400, "cookies": false, "type": "", "demo": "tablesdb\/update-email-column.md", @@ -24937,7 +25870,7 @@ "x-appwrite": { "method": "createEnumColumn", "group": "columns", - "weight": 395, + "weight": 401, "cookies": false, "type": "", "demo": "tablesdb\/create-enum-column.md", @@ -25057,7 +25990,7 @@ "x-appwrite": { "method": "updateEnumColumn", "group": "columns", - "weight": 396, + "weight": 402, "cookies": false, "type": "", "demo": "tablesdb\/update-enum-column.md", @@ -25179,7 +26112,7 @@ "x-appwrite": { "method": "createFloatColumn", "group": "columns", - "weight": 397, + "weight": 403, "cookies": false, "type": "", "demo": "tablesdb\/create-float-column.md", @@ -25301,7 +26234,7 @@ "x-appwrite": { "method": "updateFloatColumn", "group": "columns", - "weight": 398, + "weight": 404, "cookies": false, "type": "", "demo": "tablesdb\/update-float-column.md", @@ -25425,7 +26358,7 @@ "x-appwrite": { "method": "createIntegerColumn", "group": "columns", - "weight": 399, + "weight": 405, "cookies": false, "type": "", "demo": "tablesdb\/create-integer-column.md", @@ -25547,7 +26480,7 @@ "x-appwrite": { "method": "updateIntegerColumn", "group": "columns", - "weight": 400, + "weight": 406, "cookies": false, "type": "", "demo": "tablesdb\/update-integer-column.md", @@ -25671,7 +26604,7 @@ "x-appwrite": { "method": "createIpColumn", "group": "columns", - "weight": 401, + "weight": 407, "cookies": false, "type": "", "demo": "tablesdb\/create-ip-column.md", @@ -25781,7 +26714,7 @@ "x-appwrite": { "method": "updateIpColumn", "group": "columns", - "weight": 402, + "weight": 408, "cookies": false, "type": "", "demo": "tablesdb\/update-ip-column.md", @@ -25893,7 +26826,7 @@ "x-appwrite": { "method": "createLineColumn", "group": "columns", - "weight": 403, + "weight": 409, "cookies": false, "type": "", "demo": "tablesdb\/create-line-column.md", @@ -25998,7 +26931,7 @@ "x-appwrite": { "method": "updateLineColumn", "group": "columns", - "weight": 404, + "weight": 410, "cookies": false, "type": "", "demo": "tablesdb\/update-line-column.md", @@ -26109,7 +27042,7 @@ "x-appwrite": { "method": "createPointColumn", "group": "columns", - "weight": 405, + "weight": 411, "cookies": false, "type": "", "demo": "tablesdb\/create-point-column.md", @@ -26214,7 +27147,7 @@ "x-appwrite": { "method": "updatePointColumn", "group": "columns", - "weight": 406, + "weight": 412, "cookies": false, "type": "", "demo": "tablesdb\/update-point-column.md", @@ -26325,7 +27258,7 @@ "x-appwrite": { "method": "createPolygonColumn", "group": "columns", - "weight": 407, + "weight": 413, "cookies": false, "type": "", "demo": "tablesdb\/create-polygon-column.md", @@ -26430,7 +27363,7 @@ "x-appwrite": { "method": "updatePolygonColumn", "group": "columns", - "weight": 408, + "weight": 414, "cookies": false, "type": "", "demo": "tablesdb\/update-polygon-column.md", @@ -26541,7 +27474,7 @@ "x-appwrite": { "method": "createRelationshipColumn", "group": "columns", - "weight": 409, + "weight": 415, "cookies": false, "type": "", "demo": "tablesdb\/create-relationship-column.md", @@ -26678,7 +27611,7 @@ "x-appwrite": { "method": "createStringColumn", "group": "columns", - "weight": 411, + "weight": 417, "cookies": false, "type": "", "demo": "tablesdb\/create-string-column.md", @@ -26801,7 +27734,7 @@ "x-appwrite": { "method": "updateStringColumn", "group": "columns", - "weight": 412, + "weight": 418, "cookies": false, "type": "", "demo": "tablesdb\/update-string-column.md", @@ -26919,7 +27852,7 @@ "x-appwrite": { "method": "createUrlColumn", "group": "columns", - "weight": 413, + "weight": 419, "cookies": false, "type": "", "demo": "tablesdb\/create-url-column.md", @@ -27029,7 +27962,7 @@ "x-appwrite": { "method": "updateUrlColumn", "group": "columns", - "weight": 414, + "weight": 420, "cookies": false, "type": "", "demo": "tablesdb\/update-url-column.md", @@ -27170,7 +28103,7 @@ "x-appwrite": { "method": "getColumn", "group": "columns", - "weight": 386, + "weight": 392, "cookies": false, "type": "", "demo": "tablesdb\/get-column.md", @@ -27243,7 +28176,7 @@ "x-appwrite": { "method": "deleteColumn", "group": "columns", - "weight": 387, + "weight": 393, "cookies": false, "type": "", "demo": "tablesdb\/delete-column.md", @@ -27323,7 +28256,7 @@ "x-appwrite": { "method": "updateRelationshipColumn", "group": "columns", - "weight": 410, + "weight": 416, "cookies": false, "type": "", "demo": "tablesdb\/update-relationship-column.md", @@ -27429,7 +28362,7 @@ "x-appwrite": { "method": "listIndexes", "group": "indexes", - "weight": 418, + "weight": 424, "cookies": false, "type": "", "demo": "tablesdb\/list-indexes.md", @@ -27512,7 +28445,7 @@ "x-appwrite": { "method": "createIndex", "group": "indexes", - "weight": 415, + "weight": 421, "cookies": false, "type": "", "demo": "tablesdb\/create-index.md", @@ -27644,7 +28577,7 @@ "x-appwrite": { "method": "getIndex", "group": "indexes", - "weight": 416, + "weight": 422, "cookies": false, "type": "", "demo": "tablesdb\/get-index.md", @@ -27717,7 +28650,7 @@ "x-appwrite": { "method": "deleteIndex", "group": "indexes", - "weight": 417, + "weight": 423, "cookies": false, "type": "", "demo": "tablesdb\/delete-index.md", @@ -27795,7 +28728,7 @@ "x-appwrite": { "method": "listRows", "group": "rows", - "weight": 427, + "weight": 433, "cookies": false, "type": "", "demo": "tablesdb\/list-rows.md", @@ -27853,6 +28786,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -27881,7 +28822,7 @@ "x-appwrite": { "method": "createRow", "group": "rows", - "weight": 419, + "weight": 425, "cookies": false, "type": "", "demo": "tablesdb\/create-row.md", @@ -27912,7 +28853,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -27940,7 +28882,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -28022,6 +28965,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28053,7 +29002,7 @@ "x-appwrite": { "method": "upsertRows", "group": "rows", - "weight": 424, + "weight": 430, "cookies": false, "type": "", "demo": "tablesdb\/upsert-rows.md", @@ -28082,7 +29031,8 @@ "parameters": [ "databaseId", "tableId", - "rows" + "rows", + "transactionId" ], "required": [ "databaseId", @@ -28141,6 +29091,12 @@ "items": { "type": "object" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } }, "required": [ @@ -28175,7 +29131,7 @@ "x-appwrite": { "method": "updateRows", "group": "rows", - "weight": 422, + "weight": 428, "cookies": false, "type": "", "demo": "tablesdb\/update-rows.md", @@ -28240,6 +29196,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28271,7 +29233,7 @@ "x-appwrite": { "method": "deleteRows", "group": "rows", - "weight": 426, + "weight": 432, "cookies": false, "type": "", "demo": "tablesdb\/delete-rows.md", @@ -28330,6 +29292,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28361,7 +29329,7 @@ "x-appwrite": { "method": "getRow", "group": "rows", - "weight": 420, + "weight": 426, "cookies": false, "type": "", "demo": "tablesdb\/get-row.md", @@ -28427,6 +29395,14 @@ }, "default": [], "in": "query" + }, + { + "name": "transactionId", + "description": "Transaction ID to read uncommitted changes within the transaction.", + "required": false, + "type": "string", + "x-example": "", + "in": "query" } ] }, @@ -28455,7 +29431,7 @@ "x-appwrite": { "method": "upsertRow", "group": "rows", - "weight": 423, + "weight": 429, "cookies": false, "type": "", "demo": "tablesdb\/upsert-row.md", @@ -28486,7 +29462,8 @@ "tableId", "rowId", "data", - "permissions" + "permissions", + "transactionId" ], "required": [ "databaseId", @@ -28561,6 +29538,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28592,7 +29575,7 @@ "x-appwrite": { "method": "updateRow", "group": "rows", - "weight": 421, + "weight": 427, "cookies": false, "type": "", "demo": "tablesdb\/update-row.md", @@ -28667,6 +29650,12 @@ "items": { "type": "string" } + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28693,7 +29682,7 @@ "x-appwrite": { "method": "deleteRow", "group": "rows", - "weight": 425, + "weight": 431, "cookies": false, "type": "", "demo": "tablesdb\/delete-row.md", @@ -28747,6 +29736,21 @@ "type": "string", "x-example": "", "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" + } + } + } } ] } @@ -28777,7 +29781,7 @@ "x-appwrite": { "method": "decrementRowColumn", "group": "rows", - "weight": 430, + "weight": 436, "cookies": false, "type": "", "demo": "tablesdb\/decrement-row-column.md", @@ -28857,6 +29861,12 @@ "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -28890,7 +29900,7 @@ "x-appwrite": { "method": "incrementRowColumn", "group": "rows", - "weight": 429, + "weight": 435, "cookies": false, "type": "", "demo": "tablesdb\/increment-row-column.md", @@ -28970,6 +29980,12 @@ "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, "x-example": null + }, + "transactionId": { + "type": "string", + "description": "Transaction ID for staging the operation.", + "default": null, + "x-example": "" } } } @@ -30036,7 +31052,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 507, + "weight": 519, "cookies": false, "type": "", "demo": "tokens\/list.md", @@ -30117,7 +31133,7 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 505, + "weight": 517, "cookies": false, "type": "", "demo": "tokens\/create-file-token.md", @@ -30202,7 +31218,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 506, + "weight": 518, "cookies": false, "type": "", "demo": "tokens\/get.md", @@ -30263,7 +31279,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 508, + "weight": 520, "cookies": false, "type": "", "demo": "tokens\/update.md", @@ -30335,7 +31351,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 509, + "weight": 521, "cookies": false, "type": "", "demo": "tokens\/delete.md", @@ -35059,6 +36075,35 @@ "targets": "" } }, + "transactionList": { + "description": "Transaction List", + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Total number of transactions that matched your query.", + "x-example": 5, + "format": "int32" + }, + "transactions": { + "type": "array", + "description": "List of transactions.", + "items": { + "type": "object", + "$ref": "#\/definitions\/transaction" + }, + "x-example": "" + } + }, + "required": [ + "total", + "transactions" + ], + "example": { + "total": 5, + "transactions": "" + } + }, "specificationList": { "description": "Specifications List", "type": "object", @@ -41477,6 +42522,59 @@ "subscribe": "users" } }, + "transaction": { + "description": "Transaction", + "type": "object", + "properties": { + "$id": { + "type": "string", + "description": "Transaction ID.", + "x-example": "259125845563242502" + }, + "$createdAt": { + "type": "string", + "description": "Transaction creation time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "$updatedAt": { + "type": "string", + "description": "Transaction update date in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + }, + "status": { + "type": "string", + "description": "Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.", + "x-example": "pending" + }, + "operations": { + "type": "integer", + "description": "Number of operations in the transaction.", + "x-example": 5, + "format": "int32" + }, + "expiresAt": { + "type": "string", + "description": "Expiration time in ISO 8601 format.", + "x-example": "2020-10-15T06:38:00.000+00:00" + } + }, + "required": [ + "$id", + "$createdAt", + "$updatedAt", + "status", + "operations", + "expiresAt" + ], + "example": { + "$id": "259125845563242502", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "operations": 5, + "expiresAt": "2020-10-15T06:38:00.000+00:00" + } + }, "subscriber": { "description": "Subscriber", "type": "object", diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index c9c2135ab3..3dd1faf175 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -8,6 +8,7 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response; use Appwrite\SDK\Specification\Format; use Appwrite\Template\Template; +use Appwrite\Utopia\Database\Validator\Operation; use Appwrite\Utopia\Response\Model; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; @@ -396,7 +397,37 @@ class OpenAPI3 extends Format $validator = $validator->getValidator(); } - switch ((!empty($validator)) ? \get_class($validator) : '') { + $class = !empty($validator) + ? \get_class($validator) + : ''; + + $base = !empty($class) + ? \get_parent_class($class) + : ''; + + switch ($base) { + case 'Appwrite\Utopia\Database\Validator\Queries\Base': + $class = $base; + break; + } + + if ($class === 'Utopia\Validator\AnyOf') { + $validator = $param['validator']->getValidators()[0]; + $class = \get_class($validator); + } + + $array = false; + if ($class === 'Utopia\Validator\ArrayList') { + $array = true; + $subclass = \get_class($validator->getValidator()); + switch ($subclass) { + case 'Appwrite\Utopia\Database\Validator\Operation': + $class = $subclass; + break; + } + } + + switch ($class) { case 'Utopia\Database\Validator\UID': case 'Utopia\Validator\Text': $node['schema']['type'] = $validator->getType(); @@ -418,6 +449,20 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'datetime'; $node['schema']['x-example'] = Model::TYPE_DATETIME_EXAMPLE; break; + case 'Utopia\Database\Validator\Spatial': + /** @var Spatial $validator */ + $node['schema']['type'] = 'array'; + $node['schema']['items'] = [ + 'oneOf' => [ + ['type' => 'array'] + ] + ]; + $node['schema']['x-example'] = match ($validator->getSpatialType()) { + Database::VAR_POINT => '[1, 2]', + Database::VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]', + Database::VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]', + }; + break; case 'Appwrite\Network\Validator\Email': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'email'; @@ -449,20 +494,7 @@ class OpenAPI3 extends Format 'type' => $validator->getValidator()->getType(), ]; break; - case 'Utopia\Database\Validator\Spatial': - /** @var Spatial $validator */ - $node['schema']['type'] = 'array'; - $node['schema']['items'] = [ - 'oneOf' => [ - ['type' => 'array'] - ] - ]; - $node['schema']['x-example'] = match ($validator->getSpatialType()) { - Database::VAR_POINT => '[1, 2]', - Database::VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]', - Database::VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]', - }; - break; + case 'Appwrite\Utopia\Database\Validator\Queries\Base': case 'Appwrite\Utopia\Database\Validator\Queries\Columns': case 'Appwrite\Utopia\Database\Validator\Queries\Attributes': case 'Appwrite\Utopia\Database\Validator\Queries\Buckets': @@ -556,8 +588,7 @@ class OpenAPI3 extends Format break; } } - - if ($allowed) { + if ($allowed && $validator->getType() === 'string') { $node['schema']['enum'] = $validator->getList(); $node['schema']['x-enum-name'] = $this->getEnumName($sdk->getNamespace() ?? '', $methodName, $name); $node['schema']['x-enum-keys'] = $this->getEnumKeys($sdk->getNamespace() ?? '', $methodName, $name); @@ -568,7 +599,35 @@ class OpenAPI3 extends Format break; case 'Appwrite\Utopia\Database\Validator\CompoundUID': $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = '[ID1:ID2]'; + $node['schema']['x-example'] = ''; + break; + case 'Appwrite\Utopia\Database\Validator\Operation': + if ($array) { + $validator = $validator->getValidator(); + } + + /** @var Operation $validator */ + $collectionIdKey = $validator->getCollectionIdKey(); + $documentIdKey = $validator->getDocumentIdKey(); + if ($array) { + $node['schema']['type'] = 'array'; + $node['schema']['items'] = ['type' => 'object']; + } else { + $node['schema']['type'] = 'object'; + } + $example = [ + 'action' => 'create', + 'databaseId' => '', + $collectionIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($collectionIdKey)).'>', + $documentIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($documentIdKey)).'>', + 'data' => [ + 'name' => 'Walter O\'Brien', + ], + ]; + if ($array) { + $example = [$example]; + } + $node['schema']['x-example'] = \str_replace("\n", "\n\t", \json_encode($example, JSON_PRETTY_PRINT)); break; default: $node['schema']['type'] = 'string'; diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index a923f40ffa..ffa74ecd22 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -8,6 +8,7 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response; use Appwrite\SDK\Specification\Format; use Appwrite\Template\Template; +use Appwrite\Utopia\Database\Validator\Operation; use Appwrite\Utopia\Response\Model; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; @@ -422,6 +423,17 @@ class Swagger2 extends Format $class = \get_class($validator); } + $array = false; + if ($class === 'Utopia\Validator\ArrayList') { + $array = true; + $subclass = \get_class($validator->getValidator()); + switch ($subclass) { + case 'Appwrite\Utopia\Database\Validator\Operation': + $class = $subclass; + break; + } + } + switch ($class) { case 'Utopia\Validator\Text': case 'Utopia\Database\Validator\UID': @@ -444,6 +456,20 @@ class Swagger2 extends Format $node['format'] = 'datetime'; $node['x-example'] = Model::TYPE_DATETIME_EXAMPLE; break; + case 'Utopia\Database\Validator\Spatial': + /** @var Spatial $validator */ + $node['type'] = 'array'; + $node['schema']['items'] = [ + 'oneOf' => [ + ['type' => 'array'] + ] + ]; + $node['x-example'] = match ($validator->getSpatialType()) { + Database::VAR_POINT => '[1, 2]', + Database::VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]', + Database::VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]', + }; + break; case 'Appwrite\Network\Validator\Email': $node['type'] = $validator->getType(); $node['format'] = 'email'; @@ -464,20 +490,6 @@ class Swagger2 extends Format 'type' => $validator->getValidator()->getType(), ]; break; - case 'Utopia\Database\Validator\Spatial': - /** @var Spatial $validator */ - $node['type'] = 'array'; - $node['schema']['items'] = [ - 'oneOf' => [ - ['type' => 'array'] - ] - ]; - $node['x-example'] = match ($validator->getSpatialType()) { - Database::VAR_POINT => '[1, 2]', - Database::VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]', - Database::VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]', - }; - break; case 'Utopia\Validator\JSON': case 'Utopia\Validator\Mock': case 'Utopia\Validator\Assoc': @@ -562,20 +574,47 @@ class Swagger2 extends Format break; } } - if ($allowed && $validator->getType() === 'string') { $node['enum'] = $validator->getList(); $node['x-enum-name'] = $this->getEnumName($namespace, $methodName, $name); $node['x-enum-keys'] = $this->getEnumKeys($namespace, $methodName, $name); } - if ($validator->getType() === 'integer') { $node['format'] = 'int32'; } break; case 'Appwrite\Utopia\Database\Validator\CompoundUID': $node['type'] = $validator->getType(); - $node['x-example'] = '[ID1:ID2]'; + $node['x-example'] = ''; + break; + case 'Appwrite\Utopia\Database\Validator\Operation': + if ($array) { + $validator = $validator->getValidator(); + } + + /** @var Operation $validator */ + $collectionIdKey = $validator->getCollectionIdKey(); + $documentIdKey = $validator->getDocumentIdKey(); + if ($array) { + $node['type'] = 'array'; + $node['collectionFormat'] = 'multi'; + $node['items'] = ['type' => 'object']; + } else { + $node['type'] = 'object'; + } + $example = [ + 'action' => 'create', + 'databaseId' => '', + $collectionIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($collectionIdKey)).'>', + $documentIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($documentIdKey)).'>', + 'data' => [ + 'name' => 'Walter O\'Brien', + ], + ]; + if ($array) { + $example = [$example]; + } + $node['x-example'] = \str_replace("\n", "\n\t", \json_encode($example, JSON_PRETTY_PRINT)); break; default: $node['type'] = 'string'; diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index f3fe2375b0..4632678940 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -64,12 +64,12 @@ class Operation extends Validator return $this->description; } - public function getCollectionIdName(): string + public function getCollectionIdKey(): string { return $this->collectionIdName; } - public function getDocumentIdName(): string + public function getDocumentIdKey(): string { return $this->documentIdName; } From ef685c5bc169307b0cf2dad6ad5477941d3784e3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Sep 2025 00:19:39 +1200 Subject: [PATCH 105/274] Fix tests --- .../Databases/Legacy/DatabasesBase.php | 140 +++++------------- .../Legacy/Transactions/TransactionsTest.php | 2 +- 2 files changed, 34 insertions(+), 108 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 0616f982b1..ecf6cfc1ad 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -4197,69 +4197,13 @@ trait DatabasesBase $this->assertCount(1, $documentsUser2['body']['documents']); } - public function testUniqueIndexDuplicate(): void + /** + * @depends testDefaultPermissions + */ + public function testUniqueIndexDuplicate(array $data): array { - // Setup: create database, collection, attribute, and initial document - $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'databaseId' => ID::unique(), - 'name' => 'Unique Index DB', - ]); - $databaseId = $database['body']['$id']; - $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'collectionId' => ID::unique(), - 'name' => 'Movies', - 'permissions' => [ - Permission::create(Role::user(ID::custom($this->getUser()['$id']))), - Permission::read(Role::user(ID::custom($this->getUser()['$id']))), - Permission::update(Role::user(ID::custom($this->getUser()['$id']))), - Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), - ], - 'documentSecurity' => true, - ]); - $moviesId = $movies['body']['$id']; - $title = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/attributes/string', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'key' => 'title', - 'size' => 256, - 'required' => true, - ]); - $this->assertEquals(202, $title['headers']['status-code']); - sleep(2); - // Insert initial document - $doc1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'documentId' => ID::unique(), - 'data' => [ - 'title' => 'Captain America', - 'releaseYear' => 1944, - 'actors' => [ - 'Chris Evans', - 'Samuel Jackson', - ] - ], - 'permissions' => [ - Permission::read(Role::user(ID::custom($this->getUser()['$id']))), - Permission::update(Role::user(ID::custom($this->getUser()['$id']))), - Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), - ] - ]); - $this->assertEquals(201, $doc1['headers']['status-code']); - - // Create unique index - $uniqueIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/indexes', array_merge([ + $databaseId = $data['databaseId']; + $uniqueIndex = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -4268,11 +4212,13 @@ trait DatabasesBase 'type' => 'unique', 'attributes' => ['title'], ]); + $this->assertEquals(202, $uniqueIndex['headers']['status-code']); + sleep(2); - // test for failure (duplicate title) - $duplicate = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ + // test for failure + $duplicate = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -4291,10 +4237,11 @@ trait DatabasesBase Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ] ]); + $this->assertEquals(409, $duplicate['headers']['status-code']); // Test for exception when updating document to conflict - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -4313,9 +4260,11 @@ trait DatabasesBase Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ] ]); + $this->assertEquals(201, $document['headers']['status-code']); - $duplicate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $document['body']['$id'], array_merge([ + // Test for exception when updating document to conflict + $duplicate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $document['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -4334,48 +4283,17 @@ trait DatabasesBase Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), ] ]); + $this->assertEquals(409, $duplicate['headers']['status-code']); + + return $data; } - public function testPersistentCreatedAt(): void + /** + * @depends testUniqueIndexDuplicate + */ + public function testPersistentCreatedAt(array $data): array { - // Setup: create database, collection, attribute - $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'databaseId' => ID::unique(), - 'name' => 'CreatedAtDB', - ]); - $databaseId = $database['body']['$id']; - $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'collectionId' => ID::unique(), - 'name' => 'Movies', - 'permissions' => [ - Permission::create(Role::user(ID::custom($this->getUser()['$id']))), - Permission::read(Role::user(ID::custom($this->getUser()['$id']))), - Permission::update(Role::user(ID::custom($this->getUser()['$id']))), - Permission::delete(Role::user(ID::custom($this->getUser()['$id']))), - ], - 'documentSecurity' => true, - ]); - $moviesId = $movies['body']['$id']; - $title = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/attributes/string', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'key' => 'title', - 'size' => 256, - 'required' => true, - ]); - $this->assertEquals(202, $title['headers']['status-code']); - sleep(2); $headers = $this->getSide() === 'client' ? array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -4385,31 +4303,37 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]; - $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', $headers, [ + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents', $headers, [ 'documentId' => ID::unique(), 'data' => [ 'title' => 'Creation Date Test', 'releaseYear' => 2000 ] ]); + $this->assertEquals($document['body']['title'], 'Creation Date Test'); + $documentId = $document['body']['$id']; $createdAt = $document['body']['$createdAt']; $updatedAt = $document['body']['$updatedAt']; \sleep(1); - $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $documentId, $headers, [ + + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, $headers, [ 'data' => [ 'title' => 'Updated Date Test', ] ]); + $updatedAtSecond = $document['body']['$updatedAt']; + $this->assertEquals($document['body']['title'], 'Updated Date Test'); $this->assertEquals($document['body']['$createdAt'], $createdAt); $this->assertNotEquals($document['body']['$updatedAt'], $updatedAt); \sleep(1); - $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $documentId, $headers, [ + + $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/collections/' . $data['moviesId'] . '/documents/' . $documentId, $headers, [ 'data' => [ 'title' => 'Again Updated Date Test', '$createdAt' => '2022-08-01 13:09:23.040', @@ -4426,6 +4350,8 @@ trait DatabasesBase $this->assertEquals($document['body']['$updatedAt'], DateTime::formatTz('2022-08-01 13:09:23.050')); } + + return $data; } public function testUpdatePermissionsWithEmptyPayload(): array diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php index d2017c4a08..34eb561d63 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php @@ -954,7 +954,7 @@ class TransactionsTest extends Scope 'commit' => true ]); - $this->assertEquals(409, $response['headers']['status-code']); // Conflict + $this->assertEquals(404, $response['headers']['status-code']); // Conflict } /** From 09753cd6dc1af932615b095c4f8a31f09ef80c56 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Sep 2025 01:16:23 +1200 Subject: [PATCH 106/274] Optimise schema --- app/config/collections/projects.php | 22 ++++------------------ src/Appwrite/Platform/Workers/Deletes.php | 5 ++--- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index 7bd1d4a7d8..bf0cee3527 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2530,7 +2530,7 @@ return [ [ '$id' => ID::custom('status'), 'type' => Database::VAR_STRING, - 'size' => 16, // pending | committing | committed | rolled_back | failed + 'size' => 16, // pending | committing | committed | failed 'signed' => true, 'required' => false, 'default' => 'pending', @@ -2560,14 +2560,7 @@ return [ ], 'indexes' => [ [ - '$id' => ID::custom('_key_status'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['status'], - 'lengths' => [], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_expires'), + '$id' => ID::custom('_key_expiresAt'), 'type' => Database::INDEX_KEY, 'attributes' => ['expiresAt'], 'lengths' => [], @@ -2624,7 +2617,7 @@ return [ [ '$id' => ID::custom('action'), 'type' => Database::VAR_STRING, - 'size' => 32, // create | update | upsert | increment | decrement | delete + 'size' => 32, // create | update | upsert | increment | decrement | delete | bulkCreate | bulkUpdate | bulkUpsert | bulkDelete 'signed' => true, 'required' => true, 'default' => null, @@ -2634,7 +2627,7 @@ return [ [ '$id' => ID::custom('data'), 'type' => Database::VAR_STRING, - 'size' => 65535, + 'size' => 5_000_000, // Allow large payloads for bulk operations 'signed' => false, 'required' => true, 'default' => null, @@ -2650,13 +2643,6 @@ return [ 'lengths' => [], 'orders' => [], ], - [ - '$id' => ID::custom('_key_internal_path'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['databaseInternalId', 'collectionInternalId'], - 'lengths' => [], - 'orders' => [], - ], ], ], ]; diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index cb6e843fd5..331a2668a3 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -1310,9 +1310,9 @@ class Deletes extends Action $dbForProject->deleteDocuments('transactionLogs', [ Query::equal('transactionInternalId', [$transactionInternalId]), ]); - Console::info("Transaction logs for {$transactionId} deleted."); + Console::info("Transaction logs for transaction {$transactionId} deleted."); } catch (Throwable $th) { - Console::error("Failed to delete transaction logs for {$transactionId}: " . $th->getMessage()); + Console::error("Failed to delete transaction logs for transaction {$transactionId}: " . $th->getMessage()); } } @@ -1323,7 +1323,6 @@ class Deletes extends Action try { $dbForProject->deleteDocuments('transactions', [ - Query::equal('status', ['pending']), Query::lessThan('expiresAt', DateTime::format(new \DateTime())), ], onNext: function (Document $transaction) use ($dbForProject, $project, &$transactionInternalIds) { $transactionInternalIds[] = $transaction->getSequence(); From 65ad2c9ff54cc97883ee0822e00e1585f7fad9b9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Sep 2025 01:45:16 +1200 Subject: [PATCH 107/274] Handle usage + realtime --- .../Http/Databases/Transactions/Update.php | 147 ++++++++++++++++-- .../Http/TablesDB/Transactions/Update.php | 5 + .../Legacy/Transactions/TransactionsTest.php | 2 +- .../Transactions/TransactionsTest.php | 2 +- 4 files changed, 139 insertions(+), 17 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index ed70afee35..e8d3e6a1e4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -3,6 +3,8 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; use Appwrite\Event\Delete; +use Appwrite\Event\Event; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -63,6 +65,11 @@ class Update extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForDeletes') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->inject('queueForRealtime') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') ->callback($this->action(...)); } @@ -73,6 +80,11 @@ class Update extends Action * @param UtopiaResponse $response * @param Database $dbForProject * @param Delete $queueForDeletes + * @param Event $queueForEvents + * @param StatsUsage $queueForStatsUsage + * @param Event $queueForRealtime + * @param Event $queueForFunctions + * @param Event $queueForWebhooks * @return void * @throws ConflictException * @throws Exception @@ -82,7 +94,7 @@ class Update extends Action * @throws Structure * @throws \Utopia\Exception */ - public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Delete $queueForDeletes): void + public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void { if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); @@ -106,13 +118,18 @@ class Update extends Action } if ($commit) { - $dbForProject->withTransaction(function () use ($dbForProject, $queueForDeletes, $transactionId, &$transaction) { + $operations = []; + + // Track metrics for usage stats + $totalOperations = 0; + $databaseOperations = []; + + $dbForProject->withTransaction(function () use ($dbForProject, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committing', ])); - // Fetch operations ordered by sequence by default to - // replay operations in exact order they were created + // Fetch operations ordered by sequence by default to replay operations in exact order they were created $operations = $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), ]); @@ -130,6 +147,10 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; + // Track operations for stats + $totalOperations++; + $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + 1; + if ($data instanceof Document) { $data = $data->getArrayCopy(); } @@ -196,13 +217,99 @@ class Update extends Action throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); } }); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $totalOperations); + + // Add per-database metrics + foreach ($databaseOperations as $sequence => $count) { + $queueForStatsUsage->addMetric( + str_replace('{databaseInternalId}', $sequence, METRIC_DATABASE_ID_OPERATIONS_WRITES), + $count + ); + } + + // Trigger realtime events for each operation + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $action = $operation['action']; + $documentId = $operation['documentId']; + $data = $operation['data']; + + if ($data instanceof Document) { + $data = $data->getArrayCopy(); + } + + $database = $dbForProject->findOne('databases', [ + Query::equal('$sequence', [$databaseInternalId]) + ]); + $collection = $dbForProject->findOne('database_' . $databaseInternalId, [ + Query::equal('$sequence', [$collectionInternalId]) + ]); + + $queueForEvents + ->setParam('databaseId', $database->getId()) + ->setContext('database', $database) + ->setParam('collectionId', $collection->getId()) + ->setParam('tableId', $collection->getId()) + ->setContext('collection', $collection); + + $eventAction = ''; + $documents = []; + + switch ($action) { + case 'create': + $eventAction = 'create'; + $documents[] = $documentId ?? $data['$id'] ?? null; + break; + case 'update': + case 'increment': + case 'decrement': + $eventAction = 'update'; + $documents[] = $documentId; + break; + case 'delete': + $eventAction = 'delete'; + $documents[] = $documentId; + break; + case 'upsert': + $eventAction = 'upsert'; + $documents[] = $documentId ?? $data['$id'] ?? null; + break; + case 'bulkCreate': + case 'bulkUpdate': + case 'bulkUpsert': + case 'bulkDelete': + break; + } + + // Trigger events for each document + foreach ($documents as $docId) { + if ($docId) { + $queueForEvents + ->setParam('documentId', $docId) + ->setParam('rowId', $docId) + ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].' . $eventAction); + + $queueForRealtime->from($queueForEvents)->trigger(); + $queueForFunctions->from($queueForEvents)->trigger(); + $queueForWebhooks->from($queueForEvents)->trigger(); + + $queueForEvents->reset(); + $queueForRealtime->reset(); + $queueForFunctions->reset(); + $queueForWebhooks->reset(); + } + } + } } if ($rollback) { $transaction = $dbForProject->updateDocument( 'transactions', $transactionId, - new Document(['status' => 'rolledBack']) + new Document(['status' => 'failed']) ); $queueForDeletes @@ -226,7 +333,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { if ($documentId && !isset($data['$id'])) { $data['$id'] = $documentId; } @@ -250,7 +358,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -288,7 +397,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -318,7 +428,8 @@ class Update extends Action string $documentId, \DateTime $createdAt, array &$state - ): void { + ): void + { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -350,7 +461,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -387,7 +499,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -423,7 +536,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { $dbForProject->createDocuments( $collectionId, @@ -447,7 +561,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->updateDocuments( @@ -481,7 +596,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { // Run bulk upsert without timestamp wrapper, checking manually in callback $dbForProject->upsertDocuments( $collectionId, @@ -517,7 +633,8 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): void + { $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->deleteDocuments( diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php index b754ec97a1..d76de0766d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php @@ -53,6 +53,11 @@ class Update extends TransactionsUpdate ->inject('response') ->inject('dbForProject') ->inject('queueForDeletes') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->inject('queueForRealtime') + ->inject('queueForFunctions') + ->inject('queueForWebhooks') ->callback($this->action(...)); } } diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php index 34eb561d63..02e3082626 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php @@ -485,7 +485,7 @@ class TransactionsTest extends Scope ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('rolledBack', $response['body']['status']); + $this->assertEquals('failed', $response['body']['status']); // Verify no documents were created $documents = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php index 9ec5994903..30bb4cb290 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -485,7 +485,7 @@ class TransactionsTest extends Scope ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('rolledBack', $response['body']['status']); + $this->assertEquals('failed', $response['body']['status']); // Verify no rows were created $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ From 38dc92bfa792536d202eea56b314234840d16357 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Sep 2025 02:05:19 +1200 Subject: [PATCH 108/274] Delete with worker --- .../Http/Databases/Transactions/Delete.php | 15 +++++----- .../Http/Databases/Transactions/Update.php | 30 +++++++------------ 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php index 43c0c5c4a8..da92ce1b4c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; +use Appwrite\Event\Delete as DeleteEvent; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -9,7 +10,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -51,10 +51,11 @@ class Delete extends Action ->param('transactionId', '', new UID(), 'Transaction ID.') ->inject('response') ->inject('dbForProject') + ->inject('queueForDeletes') ->callback($this->action(...)); } - public function action(string $transactionId, UtopiaResponse $response, Database $dbForProject): void + public function action(string $transactionId, UtopiaResponse $response, Database $dbForProject, DeleteEvent $queueForDeletes): void { $transaction = $dbForProject->getDocument('transactions', $transactionId); @@ -62,13 +63,11 @@ class Delete extends Action throw new Exception(Exception::TRANSACTION_NOT_FOUND); } - if (!$dbForProject->deleteDocument('transactions', $transactionId)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove transaction from DB'); - } + $dbForProject->deleteDocument('transactions', $transactionId); - $dbForProject->deleteDocuments('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - ]); + $queueForDeletes + ->setType(DELETE_TYPE_DOCUMENT) + ->setDocument($transaction); $response->noContent(); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index e8d3e6a1e4..54b339a46c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -333,8 +333,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { if ($documentId && !isset($data['$id'])) { $data['$id'] = $documentId; } @@ -358,8 +357,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -397,8 +395,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -428,8 +425,7 @@ class Update extends Action string $documentId, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -461,8 +457,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -499,8 +494,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { @@ -536,8 +530,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { $dbForProject->createDocuments( $collectionId, @@ -561,8 +554,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->updateDocuments( @@ -596,8 +588,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { // Run bulk upsert without timestamp wrapper, checking manually in callback $dbForProject->upsertDocuments( $collectionId, @@ -633,8 +624,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void - { + ): void { $queries = Query::parseQueries($data['queries'] ?? []); $dbForProject->deleteDocuments( From 7120bce761aa3401d5065e7c3bf9eb6f2dc705a7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Sep 2025 21:47:40 +1200 Subject: [PATCH 109/274] Handle structure exception --- .../Http/Databases/Transactions/Operations/Create.php | 2 +- .../Databases/Http/Databases/Transactions/Update.php | 7 ++++++- .../Http/TablesDB/Transactions/Operations/Create.php | 2 +- .../Databases/Legacy/Transactions/TransactionsTest.php | 2 +- .../Databases/TablesDB/Transactions/TransactionsTest.php | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index a33af05e3d..d2e438706a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -36,7 +36,7 @@ class Create extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/transactions/:transactionId/operations') - ->desc('Add operations to transaction') + ->desc('Create operations scoped to a transaction') ->groups(['api', 'database', 'transactions']) ->label('scope', 'transactions.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 54b339a46c..2c555d433c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -17,7 +17,7 @@ use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\NotFound as NotFoundException; -use Utopia\Database\Exception\Structure; +use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Query; use Utopia\Database\Validator\UID; @@ -210,6 +210,11 @@ class Update extends Action 'status' => 'failed', ])); throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); + } catch (StructureException $e) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); } catch (TransactionException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php index 6b2ee2ce4c..2280a6f7e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -30,7 +30,7 @@ class Create extends OperationsCreate $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/tablesdb/transactions/:transactionId/operations') - ->desc('Add operations to transaction') + ->desc('Create operations scoped to a transaction') ->groups(['api', 'database', 'transactions']) ->label('scope', 'transactions.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php index 02e3082626..9d8cb7961e 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php @@ -96,7 +96,7 @@ class TransactionsTest extends Scope /** * Test adding operations to a transaction */ - public function testAddOperations(): void + public function testCreateOperations(): void { // Create database first $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php index 30bb4cb290..68f0afb835 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -96,7 +96,7 @@ class TransactionsTest extends Scope /** * Test adding operations to a transaction */ - public function testAddOperations(): void + public function testCreateOperations(): void { // Create database first $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ From 4f916fae220e30fe3bbe65fbfd35e7fa2d762847 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 13 Sep 2025 00:04:37 +1200 Subject: [PATCH 110/274] Handle limit exception --- .../Databases/Http/Databases/Transactions/Update.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 2c555d433c..4509114bfc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -16,6 +16,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Transaction as TransactionException; @@ -215,6 +216,11 @@ class Update extends Action 'status' => 'failed', ])); throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } catch (LimitException $e) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage()); } catch (TransactionException $e) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', From 304ebf12b91bdaf38e61054ea779b0aa666d2bea Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 13 Sep 2025 00:04:49 +1200 Subject: [PATCH 111/274] Add more tests --- .../Legacy/Transactions/TransactionsTest.php | 596 ++++++++++++++++++ .../Transactions/TransactionsTest.php | 596 ++++++++++++++++++ 2 files changed, 1192 insertions(+) diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php index 9d8cb7961e..d712c2ca6c 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php @@ -3635,4 +3635,600 @@ class TransactionsTest extends Scope $this->assertEquals('active', $response['body']['status']); } } + + /** + * Test increment and decrement operations in transaction + */ + public function testIncrementDecrementOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'IncrementDecrementTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'CounterCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add integer attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'default' => 0, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'score', + 'required' => false, + 'default' => 100, + ]); + + sleep(2); + + // Create initial document + $doc = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'counter_doc', + 'data' => [ + 'counter' => 10, + 'score' => 50 + ] + ]); + + $this->assertEquals(201, $doc['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add increment and decrement operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'increment', + 'documentId' => 'counter_doc', + 'data' => [ + 'attribute' => 'counter', + 'value' => 5, + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'decrement', + 'documentId' => 'counter_doc', + 'data' => [ + 'attribute' => 'score', + 'value' => 20, + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'increment', + 'documentId' => 'counter_doc', + 'data' => [ + 'attribute' => 'counter', + 'value' => 3, + 'max' => 20 + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'decrement', + 'documentId' => 'counter_doc', + 'data' => [ + 'attribute' => 'score', + 'value' => 30, + 'min' => 0 + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 final values + $doc = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/counter_doc", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $doc['headers']['status-code']); + // counter: 10 + 5 + 3 = 18 (capped at 20 max) + $this->assertEquals(18, $doc['body']['counter']); + // score: 50 - 20 - 100 = -70, but min is 0 + $this->assertEquals(0, $doc['body']['score']); + } + + /** + * Test bulk update operations in transaction + */ + public function testBulkUpdateOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkUpdateTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'BulkUpdateCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'status', + 'size' => 50, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'category', + 'size' => 50, + 'required' => false, + ]); + + sleep(2); + + // Create initial documents + for ($i = 1; $i <= 5; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => "doc_{$i}", + 'data' => [ + 'status' => 'pending', + 'category' => $i % 2 === 0 ? 'even' : 'odd' + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add bulk update operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'bulkUpdate', + 'data' => [ + 'queries' => [ + Query::equal('category', ['even'])->toString() + ], + 'data' => [ + 'status' => 'approved' + ] + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkUpdate', + 'data' => [ + 'queries' => [ + Query::equal('category', ['odd'])->toString() + ], + 'data' => [ + 'status' => 'rejected' + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 updates + $docs = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + foreach ($docs['body']['documents'] as $doc) { + if ($doc['category'] === 'even') { + $this->assertEquals('approved', $doc['status']); + } else { + $this->assertEquals('rejected', $doc['status']); + } + } + } + + /** + * Test bulk upsert operations in transaction + */ + public function testBulkUpsertOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkUpsertTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'BulkUpsertCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 100, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'value', + 'required' => false, + ]); + + sleep(2); + + // Create some initial documents + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'existing_1', + 'data' => [ + 'name' => 'Existing Document 1', + 'value' => 10 + ] + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'existing_2', + 'data' => [ + 'name' => 'Existing Document 2', + 'value' => 20 + ] + ]); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add bulk upsert operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'bulkUpsert', + 'data' => [ + [ + '$id' => 'existing_1', + 'name' => 'Updated Document 1', + 'value' => 100 + ], + [ + '$id' => 'new_1', + 'name' => 'New Document 1', + 'value' => 30 + ], + [ + '$id' => 'new_2', + 'name' => 'New Document 2', + 'value' => 40 + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 results + $docs = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(4, $docs['body']['total']); + + $docMap = []; + foreach ($docs['body']['documents'] as $doc) { + $docMap[$doc['$id']] = $doc; + } + + // Verify updated document + $this->assertEquals('Updated Document 1', $docMap['existing_1']['name']); + $this->assertEquals(100, $docMap['existing_1']['value']); + + // Verify unchanged document + $this->assertEquals('Existing Document 2', $docMap['existing_2']['name']); + $this->assertEquals(20, $docMap['existing_2']['value']); + + // Verify new documents + $this->assertEquals('New Document 1', $docMap['new_1']['name']); + $this->assertEquals(30, $docMap['new_1']['value']); + $this->assertEquals('New Document 2', $docMap['new_2']['name']); + $this->assertEquals(40, $docMap['new_2']['value']); + } + + /** + * Test bulk delete operations in transaction + */ + public function testBulkDeleteOperations(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'BulkDeleteTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'BulkDeleteCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add attributes + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'type', + 'size' => 50, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'priority', + 'required' => false, + ]); + + sleep(2); + + // Create initial documents + for ($i = 1; $i <= 10; $i++) { + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => "doc_{$i}", + 'data' => [ + 'type' => $i <= 5 ? 'temp' : 'permanent', + 'priority' => $i + ] + ]); + } + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add bulk delete operations + $response = $this->client->call(Client::METHOD_POST, "/databases/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' => 'bulkDelete', + 'data' => [ + 'queries' => [ + Query::equal('type', ['temp'])->toString() + ] + ] + ], + [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => [ + Query::greaterThan('priority', 8)->toString() + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Commit transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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 deletions + $docs = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Should have deleted docs 1-5 (temp) and docs 9-10 (priority > 8) + // Remaining should be docs 6-8 + $this->assertEquals(3, $docs['body']['total']); + + $remainingIds = array_map(fn($doc) => $doc['$id'], $docs['body']['documents']); + sort($remainingIds); + $this->assertEquals(['doc_6', 'doc_7', 'doc_8'], $remainingIds); + } } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php index 68f0afb835..4466d53e30 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -3635,4 +3635,600 @@ class TransactionsTest extends Scope $this->assertEquals('active', $response['body']['status']); } } + + /** + * Test increment and decrement operations in transaction + */ + public function testIncrementDecrementOperations(): void + { + // Create database and table + $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' => 'IncrementDecrementTestDB' + ]); + + $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' => 'CounterTable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add integer columns + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'default' => 0, + ]); + + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'score', + 'required' => false, + 'default' => 100, + ]); + + sleep(2); + + // Create initial row + $row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'counter_row', + 'data' => [ + 'counter' => 10, + 'score' => 50 + ] + ]); + + $this->assertEquals(201, $row['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'] + ])); + + $transactionId = $transaction['body']['$id']; + + // Add increment and decrement operations + $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, + 'tableId' => $tableId, + 'action' => 'increment', + 'rowId' => 'counter_row', + 'data' => [ + 'column' => 'counter', + 'value' => 5, + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'decrement', + 'rowId' => 'counter_row', + 'data' => [ + 'column' => 'score', + 'value' => 20, + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'increment', + 'rowId' => 'counter_row', + 'data' => [ + 'column' => 'counter', + 'value' => 3, + 'max' => 20 + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'decrement', + 'rowId' => 'counter_row', + 'data' => [ + 'column' => 'score', + 'value' => 30, + 'min' => 0 + ] + ] + ] + ]); + + $this->assertEquals(201, $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 final values + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/counter_row", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + // counter: 10 + 5 + 3 = 18 (capped at 20 max) + $this->assertEquals(18, $row['body']['counter']); + // score: 50 - 20 - 100 = -70, but min is 0 + $this->assertEquals(0, $row['body']['score']); + } + + /** + * Test bulk update operations in transaction + */ + public function testBulkUpdateOperations(): void + { + // Create database and table + $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' => 'BulkUpdateTestDB' + ]); + + $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' => 'BulkUpdateTable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add columns + $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' => 50, + 'required' => false, + ]); + + $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' => 'category', + 'size' => 50, + 'required' => false, + ]); + + sleep(2); + + // Create initial rows + for ($i = 1; $i <= 5; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => "row_{$i}", + 'data' => [ + 'status' => 'pending', + 'category' => $i % 2 === 0 ? 'even' : 'odd' + ] + ]); + } + + // 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 bulk update operations + $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, + 'tableId' => $tableId, + 'action' => 'bulkUpdate', + 'data' => [ + 'queries' => [ + Query::equal('category', ['even'])->toString() + ], + 'data' => [ + 'status' => 'approved' + ] + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'bulkUpdate', + 'data' => [ + 'queries' => [ + Query::equal('category', ['odd'])->toString() + ], + 'data' => [ + 'status' => 'rejected' + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $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 updates + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + foreach ($rows['body']['rows'] as $row) { + if ($row['category'] === 'even') { + $this->assertEquals('approved', $row['status']); + } else { + $this->assertEquals('rejected', $row['status']); + } + } + } + + /** + * Test bulk upsert operations in transaction + */ + public function testBulkUpsertOperations(): void + { + // Create database and table + $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' => 'BulkUpsertTestDB' + ]); + + $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' => 'BulkUpsertTable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add columns + $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' => 'name', + 'size' => 100, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'value', + 'required' => false, + ]); + + sleep(2); + + // Create some initial rows + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'existing_1', + 'data' => [ + 'name' => 'Existing Row 1', + 'value' => 10 + ] + ]); + + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'existing_2', + 'data' => [ + 'name' => 'Existing Row 2', + 'value' => 20 + ] + ]); + + // 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 bulk upsert operations + $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, + 'tableId' => $tableId, + 'action' => 'bulkUpsert', + 'data' => [ + [ + '$id' => 'existing_1', + 'name' => 'Updated Row 1', + 'value' => 100 + ], + [ + '$id' => 'new_1', + 'name' => 'New Row 1', + 'value' => 30 + ], + [ + '$id' => 'new_2', + 'name' => 'New Row 2', + 'value' => 40 + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $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 results + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(4, $rows['body']['total']); + + $rowMap = []; + foreach ($rows['body']['rows'] as $row) { + $rowMap[$row['$id']] = $row; + } + + // Verify updated row + $this->assertEquals('Updated Row 1', $rowMap['existing_1']['name']); + $this->assertEquals(100, $rowMap['existing_1']['value']); + + // Verify unchanged row + $this->assertEquals('Existing Row 2', $rowMap['existing_2']['name']); + $this->assertEquals(20, $rowMap['existing_2']['value']); + + // Verify new rows + $this->assertEquals('New Row 1', $rowMap['new_1']['name']); + $this->assertEquals(30, $rowMap['new_1']['value']); + $this->assertEquals('New Row 2', $rowMap['new_2']['name']); + $this->assertEquals(40, $rowMap['new_2']['value']); + } + + /** + * Test bulk delete operations in transaction + */ + public function testBulkDeleteOperations(): void + { + // Create database and table + $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' => 'BulkDeleteTestDB' + ]); + + $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' => 'BulkDeleteTable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add columns + $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' => 'type', + 'size' => 50, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'priority', + 'required' => false, + ]); + + sleep(2); + + // Create initial rows + for ($i = 1; $i <= 10; $i++) { + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => "row_{$i}", + 'data' => [ + 'type' => $i <= 5 ? 'temp' : 'permanent', + 'priority' => $i + ] + ]); + } + + // 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 bulk delete operations + $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, + 'tableId' => $tableId, + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => [ + Query::equal('type', ['temp'])->toString() + ] + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => [ + Query::greaterThan('priority', 8)->toString() + ] + ] + ] + ] + ]); + + $this->assertEquals(201, $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 deletions + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // Should have deleted rows 1-5 (temp) and rows 9-10 (priority > 8) + // Remaining should be rows 6-8 + $this->assertEquals(3, $rows['body']['total']); + + $remainingIds = array_map(fn($row) => $row['$id'], $rows['body']['rows']); + sort($remainingIds); + $this->assertEquals(['row_6', 'row_7', 'row_8'], $remainingIds); + } } From e5a95ed990ec8160a49a9da5292f584bae6dc822 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 13 Sep 2025 00:31:35 +1200 Subject: [PATCH 112/274] Update DB --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index ba8b58fcea..c532f24ef2 100644 --- a/composer.lock +++ b/composer.lock @@ -3638,16 +3638,16 @@ }, { "name": "utopia-php/database", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "44af82dcb44cdaa4b2d7f30528903f186a972ee0" + "reference": "e68f2e358bdfbc682aaad4956d325535664ec7d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/44af82dcb44cdaa4b2d7f30528903f186a972ee0", - "reference": "44af82dcb44cdaa4b2d7f30528903f186a972ee0", + "url": "https://api.github.com/repos/utopia-php/database/zipball/e68f2e358bdfbc682aaad4956d325535664ec7d5", + "reference": "e68f2e358bdfbc682aaad4956d325535664ec7d5", "shasum": "" }, "require": { @@ -3688,9 +3688,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.0.1" + "source": "https://github.com/utopia-php/database/tree/2.0.2" }, - "time": "2025-09-11T08:33:25+00:00" + "time": "2025-09-12T12:29:38+00:00" }, { "name": "utopia-php/detector", From 5c09e8ae00f19b203cd9ce907066fad14b8581ea Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 13 Sep 2025 00:35:03 +1200 Subject: [PATCH 113/274] Lint --- .../Services/Databases/Legacy/Transactions/TransactionsTest.php | 2 +- .../Databases/TablesDB/Transactions/TransactionsTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php index d712c2ca6c..a76d909b5e 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php @@ -4227,7 +4227,7 @@ class TransactionsTest extends Scope // Remaining should be docs 6-8 $this->assertEquals(3, $docs['body']['total']); - $remainingIds = array_map(fn($doc) => $doc['$id'], $docs['body']['documents']); + $remainingIds = array_map(fn ($doc) => $doc['$id'], $docs['body']['documents']); sort($remainingIds); $this->assertEquals(['doc_6', 'doc_7', 'doc_8'], $remainingIds); } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php index 4466d53e30..910b675afc 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsTest.php @@ -4227,7 +4227,7 @@ class TransactionsTest extends Scope // Remaining should be rows 6-8 $this->assertEquals(3, $rows['body']['total']); - $remainingIds = array_map(fn($row) => $row['$id'], $rows['body']['rows']); + $remainingIds = array_map(fn ($row) => $row['$id'], $rows['body']['rows']); sort($remainingIds); $this->assertEquals(['row_6', 'row_7', 'row_8'], $remainingIds); } From 0f9c2001f7a320e20e606db737f9496c4daa6c06 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 13 Sep 2025 02:42:42 +1200 Subject: [PATCH 114/274] Demo SDK --- app/config/platforms.php | 8 +++---- app/config/specs/open-api3-1.8.x-client.json | 4 ++-- app/config/specs/open-api3-1.8.x-console.json | 4 ++-- app/config/specs/open-api3-1.8.x-server.json | 4 ++-- app/config/specs/open-api3-latest-client.json | 8 +++---- .../specs/open-api3-latest-console.json | 8 +++---- app/config/specs/open-api3-latest-server.json | 8 +++---- app/config/specs/swagger2-1.8.x-client.json | 4 ++-- app/config/specs/swagger2-1.8.x-console.json | 4 ++-- app/config/specs/swagger2-1.8.x-server.json | 4 ++-- app/config/specs/swagger2-latest-client.json | 8 +++---- app/config/specs/swagger2-latest-console.json | 8 +++---- app/config/specs/swagger2-latest-server.json | 8 +++---- .../examples/databases/create-document.md | 3 ++- .../examples/databases/create-operations.md | 24 +++++++++++++++++++ .../examples/databases/create-transaction.md | 13 ++++++++++ .../databases/decrement-document-attribute.md | 3 ++- .../examples/databases/delete-document.md | 3 ++- .../examples/databases/delete-transaction.md | 13 ++++++++++ .../examples/databases/get-document.md | 3 ++- .../examples/databases/get-transaction.md | 13 ++++++++++ .../databases/increment-document-attribute.md | 3 ++- .../examples/databases/list-documents.md | 3 ++- .../examples/databases/list-transactions.md | 13 ++++++++++ .../examples/databases/update-document.md | 3 ++- .../examples/databases/update-transaction.md | 15 ++++++++++++ .../examples/databases/upsert-document.md | 3 ++- .../examples/tablesdb/create-operations.md | 24 +++++++++++++++++++ .../examples/tablesdb/create-row.md | 3 ++- .../examples/tablesdb/create-transaction.md | 13 ++++++++++ .../examples/tablesdb/decrement-row-column.md | 3 ++- .../examples/tablesdb/delete-row.md | 3 ++- .../examples/tablesdb/delete-transaction.md | 13 ++++++++++ .../client-web/examples/tablesdb/get-row.md | 3 ++- .../examples/tablesdb/get-transaction.md | 13 ++++++++++ .../examples/tablesdb/increment-row-column.md | 3 ++- .../client-web/examples/tablesdb/list-rows.md | 3 ++- .../examples/tablesdb/list-transactions.md | 13 ++++++++++ .../examples/tablesdb/update-row.md | 3 ++- .../examples/tablesdb/update-transaction.md | 15 ++++++++++++ .../examples/tablesdb/upsert-row.md | 3 ++- .../examples/databases/create-document.md | 3 ++- .../examples/databases/create-documents.md | 3 ++- .../examples/databases/create-operations.md | 23 ++++++++++++++++++ .../examples/databases/create-transaction.md | 12 ++++++++++ .../databases/decrement-document-attribute.md | 3 ++- .../examples/databases/delete-document.md | 3 ++- .../examples/databases/delete-documents.md | 3 ++- .../examples/databases/delete-transaction.md | 12 ++++++++++ .../examples/databases/get-document.md | 3 ++- .../examples/databases/get-transaction.md | 12 ++++++++++ .../databases/increment-document-attribute.md | 3 ++- .../examples/databases/list-documents.md | 3 ++- .../examples/databases/list-transactions.md | 12 ++++++++++ .../examples/databases/update-document.md | 3 ++- .../examples/databases/update-documents.md | 3 ++- .../examples/databases/update-transaction.md | 14 +++++++++++ .../examples/databases/upsert-document.md | 3 ++- .../examples/databases/upsert-documents.md | 3 ++- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 23 ++++++++++++++++++ .../examples/tablesdb/create-row.md | 3 ++- .../examples/tablesdb/create-rows.md | 3 ++- .../examples/tablesdb/create-transaction.md | 12 ++++++++++ .../examples/tablesdb/decrement-row-column.md | 3 ++- .../examples/tablesdb/delete-row.md | 3 ++- .../examples/tablesdb/delete-rows.md | 3 ++- .../examples/tablesdb/delete-transaction.md | 12 ++++++++++ .../examples/tablesdb/get-row.md | 3 ++- .../examples/tablesdb/get-transaction.md | 12 ++++++++++ .../examples/tablesdb/increment-row-column.md | 3 ++- .../examples/tablesdb/list-rows.md | 3 ++- .../examples/tablesdb/list-transactions.md | 12 ++++++++++ .../examples/tablesdb/update-row.md | 3 ++- .../examples/tablesdb/update-rows.md | 3 ++- .../examples/tablesdb/update-transaction.md | 14 +++++++++++ .../examples/tablesdb/upsert-row.md | 3 ++- .../examples/tablesdb/upsert-rows.md | 3 ++- 79 files changed, 474 insertions(+), 82 deletions(-) create mode 100644 docs/examples/1.8.x/client-web/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-web/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-web/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-web/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-web/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-web/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-transaction.md diff --git a/app/config/platforms.php b/app/config/platforms.php index 7623bb896e..5cc62d6e23 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '20.0.0', + 'version' => '20.1.0-rc.1', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -24,7 +24,7 @@ return [ 'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git', 'gitRepoName' => 'sdk-for-web', 'gitUserName' => 'appwrite', - 'gitBranch' => 'dev', + 'gitBranch' => 'feat-txn', 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'), 'demos' => [ [ @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '19.0.0', + 'version' => '19.1.0-rc.1', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -275,7 +275,7 @@ return [ 'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git', 'gitRepoName' => 'sdk-for-node', 'gitUserName' => 'appwrite', - 'gitBranch' => 'dev', + 'gitBranch' => 'feat-txn', 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'), ], [ diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index e94ac895b6..6d5acf8c86 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -5143,7 +5143,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -8285,7 +8285,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 1ff651107b..797a7e72e4 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -5542,7 +5542,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -33677,7 +33677,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 987ae7fece..8e8c33ace4 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -5090,7 +5090,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -24203,7 +24203,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index a24d36fe76..6d5acf8c86 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -5143,7 +5143,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -5212,7 +5212,7 @@ "operations": { "type": "array", "description": "Array of staged operations.", - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } @@ -8285,7 +8285,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" @@ -8354,7 +8354,7 @@ "operations": { "type": "array", "description": "Array of staged operations.", - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 3316be2f8b..797a7e72e4 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -5542,7 +5542,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -5611,7 +5611,7 @@ "operations": { "type": "array", "description": "Array of staged operations.", - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } @@ -33677,7 +33677,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" @@ -33746,7 +33746,7 @@ "operations": { "type": "array", "description": "Array of staged operations.", - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index a2d91def99..8e8c33ace4 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -5090,7 +5090,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -5161,7 +5161,7 @@ "operations": { "type": "array", "description": "Array of staged operations.", - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } @@ -24203,7 +24203,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" @@ -24274,7 +24274,7 @@ "operations": { "type": "array", "description": "Array of staged operations.", - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 7297412924..e87ce88a6c 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -5282,7 +5282,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -8359,7 +8359,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 5e8aad66e4..16744679ec 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -5701,7 +5701,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -33793,7 +33793,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 9dd44734bb..d3f7129c95 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -5237,7 +5237,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -24375,7 +24375,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 4d0e75c6fa..e87ce88a6c 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -5282,7 +5282,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -5350,7 +5350,7 @@ "type": "array", "description": "Array of staged operations.", "default": [], - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } @@ -8359,7 +8359,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" @@ -8427,7 +8427,7 @@ "type": "array", "description": "Array of staged operations.", "default": [], - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index c39987cbaf..16744679ec 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -5701,7 +5701,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -5769,7 +5769,7 @@ "type": "array", "description": "Array of staged operations.", "default": [], - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } @@ -33793,7 +33793,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" @@ -33861,7 +33861,7 @@ "type": "array", "description": "Array of staged operations.", "default": [], - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 7e0785fc0a..d3f7129c95 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -5237,7 +5237,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -5307,7 +5307,7 @@ "type": "array", "description": "Array of staged operations.", "default": [], - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"collectionId\": \"\",\n \"documentId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"collectionId\": \"\",\n\t \"documentId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } @@ -24375,7 +24375,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Add operations to transaction", + "summary": "Create operations scoped to a transaction", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" @@ -24445,7 +24445,7 @@ "type": "array", "description": "Array of staged operations.", "default": [], - "x-example": "[\n {\n \"action\": \"create\",\n \"databaseId\": \"\",\n \"tableId\": \"\",\n \"rowId\": \"\",\n \"data\": {\n \"name\": \"Walter O'Brien\"\n }\n }\n]", + "x-example": "[\n\t {\n\t \"action\": \"create\",\n\t \"databaseId\": \"\",\n\t \"tableId\": \"\",\n\t \"rowId\": \"\",\n\t \"data\": {\n\t \"name\": \"Walter O'Brien\"\n\t }\n\t }\n\t]", "items": { "type": "object" } diff --git a/docs/examples/1.8.x/client-web/examples/databases/create-document.md b/docs/examples/1.8.x/client-web/examples/databases/create-document.md index 08606c9b4f..8417575ebf 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/create-document.md +++ b/docs/examples/1.8.x/client-web/examples/databases/create-document.md @@ -17,7 +17,8 @@ const result = await databases.createDocument({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/create-operations.md b/docs/examples/1.8.x/client-web/examples/databases/create-operations.md new file mode 100644 index 0000000000..2ebc085d44 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/databases/create-operations.md @@ -0,0 +1,24 @@ +import { Client, Databases } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/create-transaction.md b/docs/examples/1.8.x/client-web/examples/databases/create-transaction.md new file mode 100644 index 0000000000..5371412cc9 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/databases/create-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.createTransaction({ + ttl: 60 // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-web/examples/databases/decrement-document-attribute.md index 98629c4e6c..f8e0ae9829 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-web/examples/databases/decrement-document-attribute.md @@ -12,7 +12,8 @@ const result = await databases.decrementDocumentAttribute({ documentId: '', attribute: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/delete-document.md b/docs/examples/1.8.x/client-web/examples/databases/delete-document.md index 4192085653..4d10afdac5 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/client-web/examples/databases/delete-document.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.deleteDocument({ databaseId: '', collectionId: '', - documentId: '' + documentId: '', + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/delete-transaction.md b/docs/examples/1.8.x/client-web/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..afe55c547a --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/databases/delete-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.deleteTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/get-document.md b/docs/examples/1.8.x/client-web/examples/databases/get-document.md index b3a7558907..5a44aeb73e 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/get-document.md +++ b/docs/examples/1.8.x/client-web/examples/databases/get-document.md @@ -10,7 +10,8 @@ const result = await databases.getDocument({ databaseId: '', collectionId: '', documentId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/get-transaction.md b/docs/examples/1.8.x/client-web/examples/databases/get-transaction.md new file mode 100644 index 0000000000..cc51199a76 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/databases/get-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.getTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-web/examples/databases/increment-document-attribute.md index 8adb5d8091..eaf718e98d 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-web/examples/databases/increment-document-attribute.md @@ -12,7 +12,8 @@ const result = await databases.incrementDocumentAttribute({ documentId: '', attribute: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/list-documents.md b/docs/examples/1.8.x/client-web/examples/databases/list-documents.md index fb1d508fed..ece656a644 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/client-web/examples/databases/list-documents.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.listDocuments({ databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/list-transactions.md b/docs/examples/1.8.x/client-web/examples/databases/list-transactions.md new file mode 100644 index 0000000000..f2ce1f7536 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/databases/list-transactions.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.listTransactions({ + queries: [] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/update-document.md b/docs/examples/1.8.x/client-web/examples/databases/update-document.md index bf3554812d..33a6d73a12 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/update-document.md +++ b/docs/examples/1.8.x/client-web/examples/databases/update-document.md @@ -11,7 +11,8 @@ const result = await databases.updateDocument({ collectionId: '', documentId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/update-transaction.md b/docs/examples/1.8.x/client-web/examples/databases/update-transaction.md new file mode 100644 index 0000000000..9274b0f9bf --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/databases/update-transaction.md @@ -0,0 +1,15 @@ +import { Client, Databases } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/databases/upsert-document.md b/docs/examples/1.8.x/client-web/examples/databases/upsert-document.md index c56bc5534d..e14ad5fc6b 100644 --- a/docs/examples/1.8.x/client-web/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-web/examples/databases/upsert-document.md @@ -11,7 +11,8 @@ const result = await databases.upsertDocument({ collectionId: '', documentId: '', data: {}, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/client-web/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..c25b051430 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/create-operations.md @@ -0,0 +1,24 @@ +import { Client, TablesDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/create-row.md b/docs/examples/1.8.x/client-web/examples/tablesdb/create-row.md index 1dd1fe4241..3bbcf89d4f 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/create-row.md @@ -17,7 +17,8 @@ const result = await tablesDB.createRow({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-web/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..17787dc9a3 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/create-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.createTransaction({ + ttl: 60 // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-web/examples/tablesdb/decrement-row-column.md index 59f66d973f..d6c64645f3 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/decrement-row-column.md @@ -12,7 +12,8 @@ const result = await tablesDB.decrementRowColumn({ rowId: '', column: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/client-web/examples/tablesdb/delete-row.md index 637114d413..54c005c702 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/delete-row.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.deleteRow({ databaseId: '', tableId: '', - rowId: '' + rowId: '', + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-web/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..2ff1198225 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/delete-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.deleteTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/get-row.md b/docs/examples/1.8.x/client-web/examples/tablesdb/get-row.md index 4e436432b7..b345d145aa 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/get-row.md @@ -10,7 +10,8 @@ const result = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-web/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..8e2f24cd4c --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/get-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.getTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-web/examples/tablesdb/increment-row-column.md index a7f3a8c312..5baca80c35 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/increment-row-column.md @@ -12,7 +12,8 @@ const result = await tablesDB.incrementRowColumn({ rowId: '', column: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/client-web/examples/tablesdb/list-rows.md index 63149aaba4..c0efd8486c 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/list-rows.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.listRows({ databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-web/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..fbf0908a81 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/list-transactions.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.listTransactions({ + queries: [] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/update-row.md b/docs/examples/1.8.x/client-web/examples/tablesdb/update-row.md index 1dba006762..ecbcd4fc7a 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/update-row.md @@ -11,7 +11,8 @@ const result = await tablesDB.updateRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-web/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..2d987e4235 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/update-transaction.md @@ -0,0 +1,15 @@ +import { Client, TablesDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-web/examples/tablesdb/upsert-row.md index 1add1c45b9..ddac9ff327 100644 --- a/docs/examples/1.8.x/client-web/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-web/examples/tablesdb/upsert-row.md @@ -11,7 +11,8 @@ const result = await tablesDB.upsertRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/create-document.md b/docs/examples/1.8.x/server-nodejs/examples/databases/create-document.md index 175e06301e..6fe77c42be 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/create-document.md @@ -18,5 +18,6 @@ const result = await databases.createDocument({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/create-documents.md b/docs/examples/1.8.x/server-nodejs/examples/databases/create-documents.md index 73d08aa21e..8815d8d90f 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/create-documents.md @@ -10,5 +10,6 @@ const databases = new sdk.Databases(client); const result = await databases.createDocuments({ databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/create-operations.md b/docs/examples/1.8.x/server-nodejs/examples/databases/create-operations.md new file mode 100644 index 0000000000..da8452e3db --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/create-operations.md @@ -0,0 +1,23 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const databases = new sdk.Databases(client); + +const result = await databases.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/databases/create-transaction.md new file mode 100644 index 0000000000..f3da2919f8 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/create-transaction.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const databases = new sdk.Databases(client); + +const result = await databases.createTransaction({ + ttl: 60 // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-nodejs/examples/databases/decrement-document-attribute.md index 87e4d7d25c..c01b250b08 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/decrement-document-attribute.md @@ -13,5 +13,6 @@ const result = await databases.decrementDocumentAttribute({ documentId: '', attribute: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/delete-document.md b/docs/examples/1.8.x/server-nodejs/examples/databases/delete-document.md index 429554b74b..bfc19777f9 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/delete-document.md @@ -10,5 +10,6 @@ const databases = new sdk.Databases(client); const result = await databases.deleteDocument({ databaseId: '', collectionId: '', - documentId: '' + documentId: '', + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-nodejs/examples/databases/delete-documents.md index 0965d8ddaf..9440d20999 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/delete-documents.md @@ -10,5 +10,6 @@ const databases = new sdk.Databases(client); const result = await databases.deleteDocuments({ databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..53d676e74c --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/delete-transaction.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const databases = new sdk.Databases(client); + +const result = await databases.deleteTransaction({ + transactionId: '' +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/get-document.md b/docs/examples/1.8.x/server-nodejs/examples/databases/get-document.md index 7cc71cd0c3..7abea4e44e 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/get-document.md @@ -11,5 +11,6 @@ const result = await databases.getDocument({ databaseId: '', collectionId: '', documentId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/databases/get-transaction.md new file mode 100644 index 0000000000..9b7297c7e7 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/get-transaction.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const databases = new sdk.Databases(client); + +const result = await databases.getTransaction({ + transactionId: '' +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-nodejs/examples/databases/increment-document-attribute.md index 2b47f5a75d..843d163bca 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/increment-document-attribute.md @@ -13,5 +13,6 @@ const result = await databases.incrementDocumentAttribute({ documentId: '', attribute: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/list-documents.md b/docs/examples/1.8.x/server-nodejs/examples/databases/list-documents.md index 148bf83c8f..7405f3e28d 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/list-documents.md @@ -10,5 +10,6 @@ const databases = new sdk.Databases(client); const result = await databases.listDocuments({ databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-nodejs/examples/databases/list-transactions.md new file mode 100644 index 0000000000..9a36eb0f93 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/list-transactions.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const databases = new sdk.Databases(client); + +const result = await databases.listTransactions({ + queries: [] // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/update-document.md b/docs/examples/1.8.x/server-nodejs/examples/databases/update-document.md index 8a9a6856b4..3e953760a1 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/update-document.md @@ -12,5 +12,6 @@ const result = await databases.updateDocument({ collectionId: '', documentId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/update-documents.md b/docs/examples/1.8.x/server-nodejs/examples/databases/update-documents.md index 2cfb8c7800..dc79e433aa 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/update-documents.md @@ -11,5 +11,6 @@ const result = await databases.updateDocuments({ databaseId: '', collectionId: '', data: {}, // optional - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/databases/update-transaction.md new file mode 100644 index 0000000000..57654495ba --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/update-transaction.md @@ -0,0 +1,14 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const databases = new sdk.Databases(client); + +const result = await databases.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-document.md index 5ec3e0541e..0aaec4e6cb 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-document.md @@ -12,5 +12,6 @@ const result = await databases.upsertDocument({ collectionId: '', documentId: '', data: {}, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-documents.md index 8deec536ff..16ed70fae9 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-nodejs/examples/databases/upsert-documents.md @@ -10,5 +10,6 @@ const databases = new sdk.Databases(client); const result = await databases.upsertDocuments({ databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/messaging/create-push.md b/docs/examples/1.8.x/server-nodejs/examples/messaging/create-push.md index c0a7f4713f..4c64813f25 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-nodejs/examples/messaging/create-push.md @@ -16,7 +16,7 @@ const result = await messaging.createPush({ targets: [], // optional data: {}, // optional action: '', // optional - image: '[ID1:ID2]', // optional + image: '', // optional icon: '', // optional sound: '', // optional color: '', // optional diff --git a/docs/examples/1.8.x/server-nodejs/examples/messaging/update-push.md b/docs/examples/1.8.x/server-nodejs/examples/messaging/update-push.md index 5e857f1ff6..6f5899f8c7 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-nodejs/examples/messaging/update-push.md @@ -16,7 +16,7 @@ const result = await messaging.updatePush({ body: '', // optional data: {}, // optional action: '', // optional - image: '[ID1:ID2]', // optional + image: '', // optional icon: '', // optional sound: '', // optional color: '', // optional diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..25492396dd --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-operations.md @@ -0,0 +1,23 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-row.md index 29ddab6652..4468c168e8 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-row.md @@ -18,5 +18,6 @@ const result = await tablesDB.createRow({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-rows.md index 61eb1d1db8..20807c1612 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-rows.md @@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client); const result = await tablesDB.createRows({ databaseId: '', tableId: '', - rows: [] + rows: [], + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..249406e333 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/create-transaction.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.createTransaction({ + ttl: 60 // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/decrement-row-column.md index e3b13a887e..0310399239 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/decrement-row-column.md @@ -13,5 +13,6 @@ const result = await tablesDB.decrementRowColumn({ rowId: '', column: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-row.md index 9802f0d03e..68a965dc97 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-row.md @@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client); const result = await tablesDB.deleteRow({ databaseId: '', tableId: '', - rowId: '' + rowId: '', + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-rows.md index 6f4d7cd68b..ce1d0f4ba1 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-rows.md @@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client); const result = await tablesDB.deleteRows({ databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..28d086b9cf --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/delete-transaction.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.deleteTransaction({ + transactionId: '' +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-row.md index 7ea3d1fca2..fe67e2fda3 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-row.md @@ -11,5 +11,6 @@ const result = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..208368bc00 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/get-transaction.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.getTransaction({ + transactionId: '' +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/increment-row-column.md index f5cac2ebaa..aaa6cd6205 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/increment-row-column.md @@ -13,5 +13,6 @@ const result = await tablesDB.incrementRowColumn({ rowId: '', column: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-rows.md index 6d3b883bd4..3f29781a91 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-rows.md @@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client); const result = await tablesDB.listRows({ databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..3ad0c95425 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/list-transactions.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.listTransactions({ + queries: [] // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-row.md index 1c7f0af197..58583af745 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-row.md @@ -12,5 +12,6 @@ const result = await tablesDB.updateRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-rows.md index 31628d7150..d66fc28a62 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-rows.md @@ -11,5 +11,6 @@ const result = await tablesDB.updateRows({ databaseId: '', tableId: '', data: {}, // optional - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..03501d2cbf --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/update-transaction.md @@ -0,0 +1,14 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-row.md index 9963bb6ced..bfb833356a 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-row.md @@ -12,5 +12,6 @@ const result = await tablesDB.upsertRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-rows.md index cca2b777bb..c985c9515b 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-nodejs/examples/tablesdb/upsert-rows.md @@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client); const result = await tablesDB.upsertRows({ databaseId: '', tableId: '', - rows: [] + rows: [], + transactionId: '' // optional }); From f9ac0b48c8fc92c03895a65dc2ca257ef21bb449 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 15 Sep 2025 10:28:34 +0530 Subject: [PATCH 115/274] fix: use 1.8.x --- app/config/specs/open-api3-1.8.x-client.json | 15 +- app/config/specs/open-api3-1.8.x-console.json | 188 ++++++++++++++++-- app/config/specs/open-api3-1.8.x-server.json | 163 +++++++++++++-- app/config/specs/open-api3-latest-client.json | 15 +- .../specs/open-api3-latest-console.json | 188 ++++++++++++++++-- app/config/specs/open-api3-latest-server.json | 163 +++++++++++++-- app/config/specs/swagger2-1.8.x-client.json | 15 +- app/config/specs/swagger2-1.8.x-console.json | 188 ++++++++++++++++-- app/config/specs/swagger2-1.8.x-server.json | 163 +++++++++++++-- app/config/specs/swagger2-latest-client.json | 15 +- app/config/specs/swagger2-latest-console.json | 188 ++++++++++++++++-- app/config/specs/swagger2-latest-server.json | 163 +++++++++++++-- .../SDK/Specification/Format/OpenAPI3.php | 11 + .../SDK/Specification/Format/Swagger2.php | 11 + src/Appwrite/Utopia/Response/Model.php | 1 + .../Utopia/Response/Model/Attribute.php | 3 +- .../Utopia/Response/Model/Deployment.php | 3 +- .../Utopia/Response/Model/Execution.php | 6 +- .../Utopia/Response/Model/HealthAntivirus.php | 3 +- .../Utopia/Response/Model/HealthStatus.php | 3 +- src/Appwrite/Utopia/Response/Model/Index.php | 3 +- .../Utopia/Response/Model/Platform.php | 3 +- src/Appwrite/Utopia/Response/Model/Rule.php | 6 +- 23 files changed, 1335 insertions(+), 182 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index d226fcc4e1..aabc7d66fb 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -11393,12 +11393,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 02d97fffc7..1e3a876192 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -46461,7 +46461,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46549,7 +46556,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46639,7 +46653,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46729,7 +46750,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46802,7 +46830,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46882,7 +46917,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46972,7 +47014,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47052,7 +47101,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47132,7 +47188,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47212,7 +47275,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47320,7 +47390,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47399,7 +47476,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47490,7 +47574,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48965,7 +49056,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -52191,7 +52289,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -52356,12 +52461,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -53312,7 +53428,16 @@ "type": { "type": "string", "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", - "x-example": "web" + "x-example": "web", + "enum": [ + "web", + "flutter-web", + "flutter-ios", + "flutter-android", + "ios", + "android", + "unity" + ] }, "key": { "type": "string", @@ -53614,7 +53739,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -53662,7 +53792,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ @@ -55827,7 +55961,11 @@ "deploymentResourceType": { "type": "string", "description": "Type of deployment. Possible values are \"function\", \"site\". Used if rule's type is \"deployment\".", - "x-example": "function" + "x-example": "function", + "enum": [ + "function", + "site" + ] }, "deploymentResourceId": { "type": "string", @@ -55842,7 +55980,13 @@ "status": { "type": "string", "description": "Domain verification status. Possible values are \"created\", \"verifying\", \"verified\" and \"unverified\"", - "x-example": "verified" + "x-example": "verified", + "enum": [ + "created", + "verifying", + "verified", + "unverified" + ] }, "logs": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 09d53dbdf0..af0ca8f886 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -35331,7 +35331,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35419,7 +35426,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35509,7 +35523,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35599,7 +35620,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35672,7 +35700,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35752,7 +35787,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35842,7 +35884,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35922,7 +35971,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36002,7 +36058,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36082,7 +36145,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36190,7 +36260,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36269,7 +36346,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36360,7 +36444,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37835,7 +37926,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -40272,7 +40370,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -40437,12 +40542,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -40811,7 +40927,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -40859,7 +40980,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index d226fcc4e1..aabc7d66fb 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -11393,12 +11393,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 02d97fffc7..1e3a876192 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -46461,7 +46461,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46549,7 +46556,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46639,7 +46653,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46729,7 +46750,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46802,7 +46830,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46882,7 +46917,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46972,7 +47014,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47052,7 +47101,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47132,7 +47188,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47212,7 +47275,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47320,7 +47390,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47399,7 +47476,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47490,7 +47574,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48965,7 +49056,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -52191,7 +52289,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -52356,12 +52461,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -53312,7 +53428,16 @@ "type": { "type": "string", "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", - "x-example": "web" + "x-example": "web", + "enum": [ + "web", + "flutter-web", + "flutter-ios", + "flutter-android", + "ios", + "android", + "unity" + ] }, "key": { "type": "string", @@ -53614,7 +53739,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -53662,7 +53792,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ @@ -55827,7 +55961,11 @@ "deploymentResourceType": { "type": "string", "description": "Type of deployment. Possible values are \"function\", \"site\". Used if rule's type is \"deployment\".", - "x-example": "function" + "x-example": "function", + "enum": [ + "function", + "site" + ] }, "deploymentResourceId": { "type": "string", @@ -55842,7 +55980,13 @@ "status": { "type": "string", "description": "Domain verification status. Possible values are \"created\", \"verifying\", \"verified\" and \"unverified\"", - "x-example": "verified" + "x-example": "verified", + "enum": [ + "created", + "verifying", + "verified", + "unverified" + ] }, "logs": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 09d53dbdf0..af0ca8f886 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -35331,7 +35331,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35419,7 +35426,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35509,7 +35523,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35599,7 +35620,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35672,7 +35700,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35752,7 +35787,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35842,7 +35884,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35922,7 +35971,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36002,7 +36058,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36082,7 +36145,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36190,7 +36260,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36269,7 +36346,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36360,7 +36444,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37835,7 +37926,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -40272,7 +40370,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -40437,12 +40542,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -40811,7 +40927,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -40859,7 +40980,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 55911e9556..61428740c1 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -11392,12 +11392,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 6d5721c73b..4f7abe599c 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -46398,7 +46398,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46486,7 +46493,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46576,7 +46590,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46666,7 +46687,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46739,7 +46767,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46819,7 +46854,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46909,7 +46951,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46989,7 +47038,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47069,7 +47125,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47149,7 +47212,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47257,7 +47327,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47336,7 +47413,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47427,7 +47511,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48903,7 +48994,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -52139,7 +52237,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -52304,12 +52409,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -53268,7 +53384,16 @@ "type": { "type": "string", "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", - "x-example": "web" + "x-example": "web", + "enum": [ + "web", + "flutter-web", + "flutter-ios", + "flutter-android", + "ios", + "android", + "unity" + ] }, "key": { "type": "string", @@ -53570,7 +53695,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -53618,7 +53748,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ @@ -55874,7 +56008,11 @@ "deploymentResourceType": { "type": "string", "description": "Type of deployment. Possible values are \"function\", \"site\". Used if rule's type is \"deployment\".", - "x-example": "function" + "x-example": "function", + "enum": [ + "function", + "site" + ] }, "deploymentResourceId": { "type": "string", @@ -55889,7 +56027,13 @@ "status": { "type": "string", "description": "Domain verification status. Possible values are \"created\", \"verifying\", \"verified\" and \"unverified\"", - "x-example": "verified" + "x-example": "verified", + "enum": [ + "created", + "verifying", + "verified", + "unverified" + ] }, "logs": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 98077f1050..10f1fb323d 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -35359,7 +35359,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35447,7 +35454,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35537,7 +35551,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35627,7 +35648,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35700,7 +35728,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35780,7 +35815,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35870,7 +35912,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35950,7 +35999,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36030,7 +36086,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36110,7 +36173,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36218,7 +36288,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36297,7 +36374,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36388,7 +36472,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37864,7 +37955,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -40307,7 +40405,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -40472,12 +40577,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -40848,7 +40964,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -40896,7 +41017,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 55911e9556..61428740c1 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -11392,12 +11392,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 6d5721c73b..4f7abe599c 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -46398,7 +46398,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46486,7 +46493,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46576,7 +46590,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46666,7 +46687,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46739,7 +46767,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46819,7 +46854,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46909,7 +46951,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -46989,7 +47038,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47069,7 +47125,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47149,7 +47212,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47257,7 +47327,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47336,7 +47413,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47427,7 +47511,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48903,7 +48994,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -52139,7 +52237,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -52304,12 +52409,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -53268,7 +53384,16 @@ "type": { "type": "string", "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", - "x-example": "web" + "x-example": "web", + "enum": [ + "web", + "flutter-web", + "flutter-ios", + "flutter-android", + "ios", + "android", + "unity" + ] }, "key": { "type": "string", @@ -53570,7 +53695,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -53618,7 +53748,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ @@ -55874,7 +56008,11 @@ "deploymentResourceType": { "type": "string", "description": "Type of deployment. Possible values are \"function\", \"site\". Used if rule's type is \"deployment\".", - "x-example": "function" + "x-example": "function", + "enum": [ + "function", + "site" + ] }, "deploymentResourceId": { "type": "string", @@ -55889,7 +56027,13 @@ "status": { "type": "string", "description": "Domain verification status. Possible values are \"created\", \"verifying\", \"verified\" and \"unverified\"", - "x-example": "verified" + "x-example": "verified", + "enum": [ + "created", + "verifying", + "verified", + "unverified" + ] }, "logs": { "type": "string", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 98077f1050..10f1fb323d 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -35359,7 +35359,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35447,7 +35454,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35537,7 +35551,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35627,7 +35648,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35700,7 +35728,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35780,7 +35815,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35870,7 +35912,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -35950,7 +35999,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36030,7 +36086,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36110,7 +36173,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36218,7 +36288,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36297,7 +36374,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36388,7 +36472,14 @@ "status": { "type": "string", "description": "Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37864,7 +37955,14 @@ "status": { "type": "string", "description": "Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -40307,7 +40405,14 @@ "status": { "type": "string", "description": "The deployment status. Possible values are \"waiting\", \"processing\", \"building\", \"ready\", and \"failed\".", - "x-example": "ready" + "x-example": "ready", + "enum": [ + "waiting", + "processing", + "building", + "ready", + "failed" + ] }, "buildLogs": { "type": "string", @@ -40472,12 +40577,23 @@ "trigger": { "type": "string", "description": "The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.", - "x-example": "http" + "x-example": "http", + "enum": [ + "http", + "schedule", + "event" + ] }, "status": { "type": "string", "description": "The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.", - "x-example": "processing" + "x-example": "processing", + "enum": [ + "waiting", + "processing", + "completed", + "failed" + ] }, "requestMethod": { "type": "string", @@ -40848,7 +40964,12 @@ "status": { "type": "string", "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", - "x-example": "online" + "x-example": "online", + "enum": [ + "disabled", + "offline", + "online" + ] } }, "required": [ @@ -40896,7 +41017,11 @@ "status": { "type": "string", "description": "Service status. Possible values can are: `pass`, `fail`", - "x-example": "pass" + "x-example": "pass", + "enum": [ + "pass", + "fail" + ] } }, "required": [ diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index c9c2135ab3..c11e55e733 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -682,6 +682,10 @@ class OpenAPI3 extends Format $type = 'string'; break; + case 'enum': + $type = 'string'; + break; + case 'json': $type = 'object'; $output['components']['schemas'][$model->getType()]['properties'][$name]['additionalProperties'] = true; @@ -770,6 +774,13 @@ class OpenAPI3 extends Format if ($items) { $output['components']['schemas'][$model->getType()]['properties'][$name]['items'] = $items; } + if ($rule['type'] === 'enum' && !empty($rule['enum'])) { + if ($rule['array']) { + $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['enum'] = $rule['enum']; + } else { + $output['components']['schemas'][$model->getType()]['properties'][$name]['enum'] = $rule['enum']; + } + } if (!in_array($name, $required)) { $output['components']['schemas'][$model->getType()]['properties'][$name]['nullable'] = true; } diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index a923f40ffa..4e784e8116 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -690,6 +690,10 @@ class Swagger2 extends Format $type = 'string'; break; + case 'enum': + $type = 'string'; + break; + case 'json': $type = 'object'; break; @@ -792,6 +796,13 @@ class Swagger2 extends Format if ($items) { $output['definitions'][$model->getType()]['properties'][$name]['items'] = $items; } + if ($rule['type'] === 'enum' && !empty($rule['enum'])) { + if ($rule['array']) { + $output['definitions'][$model->getType()]['properties'][$name]['items']['enum'] = $rule['enum']; + } else { + $output['definitions'][$model->getType()]['properties'][$name]['enum'] = $rule['enum']; + } + } if (!in_array($name, $required)) { $output['definitions'][$model->getType()]['properties'][$name]['x-nullable'] = true; } diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 04468521b6..59c786ee1f 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -16,6 +16,7 @@ abstract class Model public const TYPE_RELATIONSHIP = 'relationship'; public const TYPE_PAYLOAD = 'payload'; public const TYPE_ARRAY = 'array'; + public const TYPE_ENUM = 'enum'; /** * @var bool diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php index 8c43f8d21c..35de6bacc5 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -23,10 +23,11 @@ class Attribute extends Model 'example' => 'string', ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', 'default' => '', 'example' => 'available', + 'enum' => ['available', 'processing', 'deleting', 'stuck', 'failed'], ]) ->addRule('error', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/Deployment.php b/src/Appwrite/Utopia/Response/Model/Deployment.php index 55c1589af0..45699f65d1 100644 --- a/src/Appwrite/Utopia/Response/Model/Deployment.php +++ b/src/Appwrite/Utopia/Response/Model/Deployment.php @@ -95,10 +95,11 @@ class Deployment extends Model 'example' => '5e5ea5c16897e', ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'The deployment status. Possible values are "waiting", "processing", "building", "ready", and "failed".', 'default' => '', 'example' => 'ready', + 'enum' => ['waiting', 'processing', 'building', 'ready', 'failed'], ]) ->addRule('buildLogs', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/Execution.php b/src/Appwrite/Utopia/Response/Model/Execution.php index 39d2203bf9..f8e62fe824 100644 --- a/src/Appwrite/Utopia/Response/Model/Execution.php +++ b/src/Appwrite/Utopia/Response/Model/Execution.php @@ -52,16 +52,18 @@ class Execution extends Model 'example' => '5e5ea5c16897e', ]) ->addRule('trigger', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.', 'default' => '', 'example' => 'http', + 'enum' => ['http', 'schedule', 'event'], ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.', 'default' => '', 'example' => 'processing', + 'enum' => ['waiting', 'processing', 'completed', 'failed'], ]) ->addRule('requestMethod', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php b/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php index 7a74195371..d790dbc24c 100644 --- a/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php +++ b/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php @@ -17,10 +17,11 @@ class HealthAntivirus extends Model 'example' => '1.0.0', ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Antivirus status. Possible values can are: `disabled`, `offline`, `online`', 'default' => '', 'example' => 'online', + 'enum' => ['disabled', 'offline', 'online'], ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/HealthStatus.php b/src/Appwrite/Utopia/Response/Model/HealthStatus.php index ba340107ac..89438c83f2 100644 --- a/src/Appwrite/Utopia/Response/Model/HealthStatus.php +++ b/src/Appwrite/Utopia/Response/Model/HealthStatus.php @@ -23,10 +23,11 @@ class HealthStatus extends Model 'example' => 128, ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Service status. Possible values can are: `pass`, `fail`', 'default' => '', 'example' => 'pass', + 'enum' => ['pass', 'fail'], ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/Index.php b/src/Appwrite/Utopia/Response/Model/Index.php index 62661738c2..5a4d606408 100644 --- a/src/Appwrite/Utopia/Response/Model/Index.php +++ b/src/Appwrite/Utopia/Response/Model/Index.php @@ -41,10 +41,11 @@ class Index extends Model 'example' => 'primary', ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Index status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', 'default' => '', 'example' => 'available', + 'enum' => ['available', 'processing', 'deleting', 'stuck', 'failed'], ]) ->addRule('error', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/Platform.php b/src/Appwrite/Utopia/Response/Model/Platform.php index 4b8ffb1486..5fc68ee808 100644 --- a/src/Appwrite/Utopia/Response/Model/Platform.php +++ b/src/Appwrite/Utopia/Response/Model/Platform.php @@ -40,10 +40,11 @@ class Platform extends Model 'example' => 'My Web App', ]) ->addRule('type', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.', 'default' => '', 'example' => 'web', + 'enum' => ['web', 'flutter-web', 'flutter-ios', 'flutter-android', 'ios', 'android', 'unity'], ]) ->addRule('key', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/Rule.php b/src/Appwrite/Utopia/Response/Model/Rule.php index 12903b270e..67ba5db94d 100644 --- a/src/Appwrite/Utopia/Response/Model/Rule.php +++ b/src/Appwrite/Utopia/Response/Model/Rule.php @@ -65,10 +65,11 @@ class Rule extends Model 'example' => 'n3u9feiwmf', ]) ->addRule('deploymentResourceType', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Type of deployment. Possible values are "function", "site". Used if rule\'s type is "deployment".', 'default' => '', 'example' => 'function', + 'enum' => ['function', 'site'], ]) ->addRule('deploymentResourceId', [ 'type' => self::TYPE_STRING, @@ -83,10 +84,11 @@ class Rule extends Model 'example' => 'function', ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Domain verification status. Possible values are "created", "verifying", "verified" and "unverified"', 'default' => false, 'example' => 'verified', + 'enum' => ['created', 'verifying', 'verified', 'unverified'], ]) ->addRule('logs', [ 'type' => self::TYPE_STRING, From 0f5cb2611d15b3d43d2ce8e0a9c76456a34bd6b1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 15 Sep 2025 10:32:06 +0530 Subject: [PATCH 116/274] fix: grammer --- app/config/specs/open-api3-1.8.x-console.json | 4 ++-- app/config/specs/open-api3-1.8.x-server.json | 4 ++-- app/config/specs/open-api3-latest-console.json | 4 ++-- app/config/specs/open-api3-latest-server.json | 4 ++-- app/config/specs/swagger2-1.8.x-console.json | 4 ++-- app/config/specs/swagger2-1.8.x-server.json | 4 ++-- app/config/specs/swagger2-latest-console.json | 4 ++-- app/config/specs/swagger2-latest-server.json | 4 ++-- src/Appwrite/Utopia/Response/Model/HealthAntivirus.php | 2 +- src/Appwrite/Utopia/Response/Model/HealthStatus.php | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 1e3a876192..f72da9c18a 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -53738,7 +53738,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -53791,7 +53791,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index af0ca8f886..926b3fa59f 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -40926,7 +40926,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -40979,7 +40979,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 1e3a876192..f72da9c18a 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -53738,7 +53738,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -53791,7 +53791,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index af0ca8f886..926b3fa59f 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -40926,7 +40926,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -40979,7 +40979,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 4f7abe599c..cd6c104c6e 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -53694,7 +53694,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -53747,7 +53747,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 10f1fb323d..f913753279 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -40963,7 +40963,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -41016,7 +41016,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 4f7abe599c..cd6c104c6e 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -53694,7 +53694,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -53747,7 +53747,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 10f1fb323d..f913753279 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -40963,7 +40963,7 @@ }, "status": { "type": "string", - "description": "Antivirus status. Possible values can are: `disabled`, `offline`, `online`", + "description": "Antivirus status. Possible values are: `disabled`, `offline`, `online`", "x-example": "online", "enum": [ "disabled", @@ -41016,7 +41016,7 @@ }, "status": { "type": "string", - "description": "Service status. Possible values can are: `pass`, `fail`", + "description": "Service status. Possible values are: `pass`, `fail`", "x-example": "pass", "enum": [ "pass", diff --git a/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php b/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php index d790dbc24c..29bd420ce5 100644 --- a/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php +++ b/src/Appwrite/Utopia/Response/Model/HealthAntivirus.php @@ -18,7 +18,7 @@ class HealthAntivirus extends Model ]) ->addRule('status', [ 'type' => self::TYPE_ENUM, - 'description' => 'Antivirus status. Possible values can are: `disabled`, `offline`, `online`', + 'description' => 'Antivirus status. Possible values are: `disabled`, `offline`, `online`', 'default' => '', 'example' => 'online', 'enum' => ['disabled', 'offline', 'online'], diff --git a/src/Appwrite/Utopia/Response/Model/HealthStatus.php b/src/Appwrite/Utopia/Response/Model/HealthStatus.php index 89438c83f2..24fb8766ce 100644 --- a/src/Appwrite/Utopia/Response/Model/HealthStatus.php +++ b/src/Appwrite/Utopia/Response/Model/HealthStatus.php @@ -24,7 +24,7 @@ class HealthStatus extends Model ]) ->addRule('status', [ 'type' => self::TYPE_ENUM, - 'description' => 'Service status. Possible values can are: `pass`, `fail`', + 'description' => 'Service status. Possible values are: `pass`, `fail`', 'default' => '', 'example' => 'pass', 'enum' => ['pass', 'fail'], From 40eaf39d91dd6992b8957d67d9516715af575903 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 15 Sep 2025 11:39:00 +0530 Subject: [PATCH 117/274] fix: graphql --- src/Appwrite/GraphQL/Types/Mapper.php | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index f0394b2395..22a2c91d7e 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -6,6 +6,7 @@ use Appwrite\GraphQL\Resolvers; use Appwrite\GraphQL\Types; use Appwrite\SDK\Method; use Exception; +use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; @@ -58,7 +59,8 @@ class Mapper 'json' => Types::json(), 'none' => Types::json(), 'any' => Types::json(), - 'array' => Types::json() + 'array' => Types::json(), + 'enum' => Type::string() ]; foreach ($defaults as $type => $default) { @@ -213,6 +215,8 @@ class Mapper if (\is_array($rule['type'])) { $type = self::getUnionType($escapedKey, $rule); + } elseif ($rule['type'] === 'enum' && !empty($rule['enum'])) { + $type = self::createEnumType($rule, $escapedKey); } else { $type = self::getObjectType($rule); } @@ -387,6 +391,30 @@ class Mapper return $type; } + private static function createEnumType(array $rule, string $fieldName): Type + { + $enumTypeName = \ucfirst($fieldName) . 'Enum'; + + if (Registry::has($enumTypeName)) { + return Registry::get($enumTypeName); + } + + $values = []; + foreach ($rule['enum'] as $enumValue) { + $values[\strtoupper(\str_replace(['-', ' ', '.'], '_', $enumValue))] = [ + 'value' => $enumValue + ]; + } + + $enumType = new EnumType([ + 'name' => $enumTypeName, + 'values' => $values + ]); + + Registry::set($enumTypeName, $enumType); + return $enumType; + } + private static function getObjectType(array $rule): Type { $type = $rule['type']; @@ -460,6 +488,7 @@ class Mapper 'point' => static::model("{$prefix}Point"), 'linestring' => static::model("{$prefix}Line"), 'polygon' => static::model("{$prefix}Polygon"), + 'enum' => static::model("{$prefix}Enum"), default => throw new Exception('Unknown ' . strtolower($prefix) . ' implementation'), }; } From 579f6691f9111047263fa2246e6c17aa1fabf9f4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 15 Sep 2025 11:42:23 +0530 Subject: [PATCH 118/274] fix: default to be created --- src/Appwrite/Utopia/Response/Model/Rule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Rule.php b/src/Appwrite/Utopia/Response/Model/Rule.php index 67ba5db94d..7974bc7f98 100644 --- a/src/Appwrite/Utopia/Response/Model/Rule.php +++ b/src/Appwrite/Utopia/Response/Model/Rule.php @@ -86,7 +86,7 @@ class Rule extends Model ->addRule('status', [ 'type' => self::TYPE_ENUM, 'description' => 'Domain verification status. Possible values are "created", "verifying", "verified" and "unverified"', - 'default' => false, + 'default' => 'created', 'example' => 'verified', 'enum' => ['created', 'verifying', 'verified', 'unverified'], ]) From ef69520f95d0469bf83532208119d8c210ac2157 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 15 Sep 2025 11:51:47 +0530 Subject: [PATCH 119/274] fix: nitpick comments --- app/config/specs/open-api3-1.8.x-client.json | 2 +- app/config/specs/open-api3-1.8.x-console.json | 24 +++++++++---------- app/config/specs/open-api3-1.8.x-server.json | 16 ++++++------- app/config/specs/open-api3-latest-client.json | 2 +- .../specs/open-api3-latest-console.json | 24 +++++++++---------- app/config/specs/open-api3-latest-server.json | 16 ++++++------- app/config/specs/swagger2-1.8.x-client.json | 2 +- app/config/specs/swagger2-1.8.x-console.json | 24 +++++++++---------- app/config/specs/swagger2-1.8.x-server.json | 16 ++++++------- app/config/specs/swagger2-latest-client.json | 2 +- app/config/specs/swagger2-latest-console.json | 24 +++++++++---------- app/config/specs/swagger2-latest-server.json | 16 ++++++------- .../Utopia/Response/Model/Deployment.php | 6 ----- .../Utopia/Response/Model/Execution.php | 2 +- .../Utopia/Response/Model/Platform.php | 2 +- src/Appwrite/Utopia/Response/Model/Rule.php | 4 ++-- 16 files changed, 88 insertions(+), 94 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index aabc7d66fb..d57b9f83b2 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -11423,7 +11423,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "$ref": "#\/components\/schemas\/headers" }, diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index f72da9c18a..1dd58c3261 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -52324,11 +52324,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -52354,6 +52349,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -52381,12 +52381,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -52410,12 +52410,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -52491,7 +52491,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "$ref": "#\/components\/schemas\/headers" }, @@ -53452,7 +53452,7 @@ "hostname": { "type": "string", "description": "Web app hostname. Empty string for other platforms.", - "x-example": true + "x-example": "app.example.com" }, "httpUser": { "type": "string", @@ -53485,7 +53485,7 @@ "type": "web", "key": "com.company.appname", "store": "", - "hostname": true, + "hostname": "app.example.com", "httpUser": "username", "httpPass": "password" } @@ -55975,7 +55975,7 @@ "deploymentVcsProviderBranch": { "type": "string", "description": "Name of Git branch that updates rule. Used if type is \"deployment\"", - "x-example": "function" + "x-example": "main" }, "status": { "type": "string", @@ -56028,7 +56028,7 @@ "deploymentId": "n3u9feiwmf", "deploymentResourceType": "function", "deploymentResourceId": "n3u9feiwmf", - "deploymentVcsProviderBranch": "function", + "deploymentVcsProviderBranch": "main", "status": "verified", "logs": "HTTP challegne failed.", "renewAt": "datetime" diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 926b3fa59f..b437d56bfc 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -40405,11 +40405,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -40435,6 +40430,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -40462,12 +40462,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -40491,12 +40491,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -40572,7 +40572,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "$ref": "#\/components\/schemas\/headers" }, diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index aabc7d66fb..d57b9f83b2 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -11423,7 +11423,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "$ref": "#\/components\/schemas\/headers" }, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index f72da9c18a..1dd58c3261 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -52324,11 +52324,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -52354,6 +52349,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -52381,12 +52381,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -52410,12 +52410,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -52491,7 +52491,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "$ref": "#\/components\/schemas\/headers" }, @@ -53452,7 +53452,7 @@ "hostname": { "type": "string", "description": "Web app hostname. Empty string for other platforms.", - "x-example": true + "x-example": "app.example.com" }, "httpUser": { "type": "string", @@ -53485,7 +53485,7 @@ "type": "web", "key": "com.company.appname", "store": "", - "hostname": true, + "hostname": "app.example.com", "httpUser": "username", "httpPass": "password" } @@ -55975,7 +55975,7 @@ "deploymentVcsProviderBranch": { "type": "string", "description": "Name of Git branch that updates rule. Used if type is \"deployment\"", - "x-example": "function" + "x-example": "main" }, "status": { "type": "string", @@ -56028,7 +56028,7 @@ "deploymentId": "n3u9feiwmf", "deploymentResourceType": "function", "deploymentResourceId": "n3u9feiwmf", - "deploymentVcsProviderBranch": "function", + "deploymentVcsProviderBranch": "main", "status": "verified", "logs": "HTTP challegne failed.", "renewAt": "datetime" diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 926b3fa59f..b437d56bfc 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -40405,11 +40405,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -40435,6 +40430,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -40462,12 +40462,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -40491,12 +40491,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -40572,7 +40572,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "$ref": "#\/components\/schemas\/headers" }, diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 61428740c1..c2628533d0 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -11422,7 +11422,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "type": "object", "$ref": "#\/definitions\/headers" diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index cd6c104c6e..0f91a5433d 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -52272,11 +52272,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -52302,6 +52297,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -52329,12 +52329,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -52358,12 +52358,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -52439,7 +52439,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "type": "object", "$ref": "#\/definitions\/headers" @@ -53408,7 +53408,7 @@ "hostname": { "type": "string", "description": "Web app hostname. Empty string for other platforms.", - "x-example": true + "x-example": "app.example.com" }, "httpUser": { "type": "string", @@ -53441,7 +53441,7 @@ "type": "web", "key": "com.company.appname", "store": "", - "hostname": true, + "hostname": "app.example.com", "httpUser": "username", "httpPass": "password" } @@ -56022,7 +56022,7 @@ "deploymentVcsProviderBranch": { "type": "string", "description": "Name of Git branch that updates rule. Used if type is \"deployment\"", - "x-example": "function" + "x-example": "main" }, "status": { "type": "string", @@ -56075,7 +56075,7 @@ "deploymentId": "n3u9feiwmf", "deploymentResourceType": "function", "deploymentResourceId": "n3u9feiwmf", - "deploymentVcsProviderBranch": "function", + "deploymentVcsProviderBranch": "main", "status": "verified", "logs": "HTTP challegne failed.", "renewAt": "datetime" diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index f913753279..679da75e5d 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -40440,11 +40440,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -40470,6 +40465,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -40497,12 +40497,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -40526,12 +40526,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -40607,7 +40607,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "type": "object", "$ref": "#\/definitions\/headers" diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 61428740c1..c2628533d0 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -11422,7 +11422,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "type": "object", "$ref": "#\/definitions\/headers" diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index cd6c104c6e..0f91a5433d 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -52272,11 +52272,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -52302,6 +52297,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -52329,12 +52329,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -52358,12 +52358,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -52439,7 +52439,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "type": "object", "$ref": "#\/definitions\/headers" @@ -53408,7 +53408,7 @@ "hostname": { "type": "string", "description": "Web app hostname. Empty string for other platforms.", - "x-example": true + "x-example": "app.example.com" }, "httpUser": { "type": "string", @@ -53441,7 +53441,7 @@ "type": "web", "key": "com.company.appname", "store": "", - "hostname": true, + "hostname": "app.example.com", "httpUser": "username", "httpPass": "password" } @@ -56022,7 +56022,7 @@ "deploymentVcsProviderBranch": { "type": "string", "description": "Name of Git branch that updates rule. Used if type is \"deployment\"", - "x-example": "function" + "x-example": "main" }, "status": { "type": "string", @@ -56075,7 +56075,7 @@ "deploymentId": "n3u9feiwmf", "deploymentResourceType": "function", "deploymentResourceId": "n3u9feiwmf", - "deploymentVcsProviderBranch": "function", + "deploymentVcsProviderBranch": "main", "status": "verified", "logs": "HTTP challegne failed.", "renewAt": "datetime" diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index f913753279..679da75e5d 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -40440,11 +40440,6 @@ "description": "The url of the vcs provider repository", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function" }, - "providerBranch": { - "type": "string", - "description": "The branch of the vcs repository", - "x-example": "0.7.x" - }, "providerCommitHash": { "type": "string", "description": "The commit hash of the vcs commit", @@ -40470,6 +40465,11 @@ "description": "The url of the vcs commit", "x-example": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb" }, + "providerBranch": { + "type": "string", + "description": "The branch of the vcs repository", + "x-example": "0.7.x" + }, "providerBranchUrl": { "type": "string", "description": "The branch of the vcs repository", @@ -40497,12 +40497,12 @@ "providerRepositoryName", "providerRepositoryOwner", "providerRepositoryUrl", - "providerBranch", "providerCommitHash", "providerCommitAuthorUrl", "providerCommitAuthor", "providerCommitMessage", "providerCommitUrl", + "providerBranch", "providerBranchUrl" ], "example": { @@ -40526,12 +40526,12 @@ "providerRepositoryName": "database", "providerRepositoryOwner": "utopia", "providerRepositoryUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function", - "providerBranch": "0.7.x", "providerCommitHash": "7c3f25d", "providerCommitAuthorUrl": "https:\/\/github.com\/vermakhushboo", "providerCommitAuthor": "Khushboo Verma", "providerCommitMessage": "Update index.js", "providerCommitUrl": "https:\/\/github.com\/vermakhushboo\/g4-node-function\/commit\/60c0416257a9cbcdd96b2d370c38d8f8d150ccfb", + "providerBranch": "0.7.x", "providerBranchUrl": "https:\/\/github.com\/vermakhushboo\/appwrite\/tree\/0.7.x" } }, @@ -40607,7 +40607,7 @@ }, "requestHeaders": { "type": "array", - "description": "HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", + "description": "HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.", "items": { "type": "object", "$ref": "#\/definitions\/headers" diff --git a/src/Appwrite/Utopia/Response/Model/Deployment.php b/src/Appwrite/Utopia/Response/Model/Deployment.php index 45699f65d1..f0815630b3 100644 --- a/src/Appwrite/Utopia/Response/Model/Deployment.php +++ b/src/Appwrite/Utopia/Response/Model/Deployment.php @@ -131,12 +131,6 @@ class Deployment extends Model 'default' => '', 'example' => 'https://github.com/vermakhushboo/g4-node-function', ]) - ->addRule('providerBranch', [ - 'type' => self::TYPE_STRING, - 'description' => 'The branch name of the vcs provider repository', - 'default' => '', - 'example' => 'main', - ]) ->addRule('providerCommitHash', [ 'type' => self::TYPE_STRING, 'description' => 'The commit hash of the vcs commit', diff --git a/src/Appwrite/Utopia/Response/Model/Execution.php b/src/Appwrite/Utopia/Response/Model/Execution.php index f8e62fe824..f8ee32aa6e 100644 --- a/src/Appwrite/Utopia/Response/Model/Execution.php +++ b/src/Appwrite/Utopia/Response/Model/Execution.php @@ -79,7 +79,7 @@ class Execution extends Model ]) ->addRule('requestHeaders', [ 'type' => Response::MODEL_HEADERS, - 'description' => 'HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.', + 'description' => 'HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.', 'default' => [], 'example' => [['Content-Type' => 'application/json']], 'array' => true, diff --git a/src/Appwrite/Utopia/Response/Model/Platform.php b/src/Appwrite/Utopia/Response/Model/Platform.php index 5fc68ee808..65f3c343d4 100644 --- a/src/Appwrite/Utopia/Response/Model/Platform.php +++ b/src/Appwrite/Utopia/Response/Model/Platform.php @@ -61,7 +61,7 @@ class Platform extends Model 'type' => self::TYPE_STRING, 'description' => 'Web app hostname. Empty string for other platforms.', 'default' => '', - 'example' => true, + 'example' => 'app.example.com', ]) ->addRule('httpUser', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/Rule.php b/src/Appwrite/Utopia/Response/Model/Rule.php index 7974bc7f98..d4b8ffd9e7 100644 --- a/src/Appwrite/Utopia/Response/Model/Rule.php +++ b/src/Appwrite/Utopia/Response/Model/Rule.php @@ -55,7 +55,7 @@ class Rule extends Model ->addRule('redirectStatusCode', [ 'type' => self::TYPE_INTEGER, 'description' => 'Status code to apply during redirect. Used if type is "redirect"', - 'default' => '', + 'default' => 301, 'example' => 301, ]) ->addRule('deploymentId', [ @@ -81,7 +81,7 @@ class Rule extends Model 'type' => self::TYPE_STRING, 'description' => 'Name of Git branch that updates rule. Used if type is "deployment"', 'default' => '', - 'example' => 'function', + 'example' => 'main', ]) ->addRule('status', [ 'type' => self::TYPE_ENUM, From 1f9bc650de2cdd2c8f17f0f6a0b841c9456839c0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 15 Sep 2025 12:13:48 +0530 Subject: [PATCH 120/274] work in progress --- src/Appwrite/GraphQL/Types/Mapper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index 22a2c91d7e..c6df475480 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -216,7 +216,7 @@ class Mapper if (\is_array($rule['type'])) { $type = self::getUnionType($escapedKey, $rule); } elseif ($rule['type'] === 'enum' && !empty($rule['enum'])) { - $type = self::createEnumType($rule, $escapedKey); + $type = self::getEnumType($escapedKey, $rule); } else { $type = self::getObjectType($rule); } @@ -391,9 +391,9 @@ class Mapper return $type; } - private static function createEnumType(array $rule, string $fieldName): Type + private static function getEnumType(string $name, array $rule): Type { - $enumTypeName = \ucfirst($fieldName) . 'Enum'; + $enumTypeName = \ucfirst($name) . 'Enum'; if (Registry::has($enumTypeName)) { return Registry::get($enumTypeName); From 0b9e43c9f82bd84d6390748ec8f55ecdc6791710 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 16 Sep 2025 00:58:23 +0530 Subject: [PATCH 121/274] Branded email for Console auth flows --- app/config/console.php | 40 ++++ .../locale/templates/email-auth-styled.tpl | 224 ++++++++++++++++++ .../locale/templates/email-mfa-challenge.tpl | 2 +- app/config/locale/templates/email-otp.tpl | 2 +- .../locale/templates/email-verification.tpl | 9 + app/controllers/api/account.php | 87 ++++++- 6 files changed, 361 insertions(+), 3 deletions(-) create mode 100644 app/config/locale/templates/email-auth-styled.tpl create mode 100644 app/config/locale/templates/email-verification.tpl diff --git a/app/config/console.php b/app/config/console.php index faacecaa08..164db597e9 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -9,6 +9,8 @@ use Appwrite\Network\Platform; use Utopia\Database\Helpers\ID; use Utopia\System\System; +$localeCodes = include __DIR__ . '/locale/codes.php'; + $console = [ '$id' => ID::custom('console'), '$sequence' => ID::custom('console'), @@ -49,6 +51,44 @@ $console = [ 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') ], + 'templates' => [ + 'email.verification-en' => [ + 'subject' => 'Account Verification', + 'preview' => 'Verify your email to activate your {{project}} account.', + 'heading' => 'Verify your email to start using Appwrite Cloud', + 'hello' => 'Hello {{user}},', + 'body' => 'Thanks for signing up for Appwrite Cloud. Before you can get started, please verify your email address.', + 'footer' => 'If you didn’t create an account, you can ignore this email.', + 'buttonText' => 'Verify email', + 'thanks' => 'Thanks,', + "signature" => "{{project}} team", + ], + 'email.mfaChallenge-en' => [ + 'subject' => 'Verification Code for {{project}}', + 'preview' => 'Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.', + 'heading' => 'Complete two-step verification to use Appwrite Cloud', + 'hello' => 'Hello {{user}},', + 'body' => 'Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.', + 'thanks' => 'Thanks,', + "signature" => "{{project}} team", + ], + 'email.otpSession-en' => [ + 'subject' => 'OTP for {{project}} account login', + 'preview' => 'Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.', + 'heading' => 'Login with OTP to use Appwrite Cloud', + 'hello' => 'Hello {{user}},', + 'body' => 'Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.', + 'thanks' => 'Thanks,', + "signature" => "{{project}} team", + ], + ], + 'customEmails' => true, ]; +foreach ($localeCodes as $localeCode) { + $console['templates']['email.verification-'.$localeCode['code']] = $console['templates']['email.verification-en']; + $console['templates']['email.mfaChallenge-'.$localeCode['code']] = $console['templates']['email.mfaChallenge-en']; + $console['templates']['email.otpSession-'.$localeCode['code']] = $console['templates']['email.otpSession-en']; +} + return $console; diff --git a/app/config/locale/templates/email-auth-styled.tpl b/app/config/locale/templates/email-auth-styled.tpl new file mode 100644 index 0000000000..07572c84da --- /dev/null +++ b/app/config/locale/templates/email-auth-styled.tpl @@ -0,0 +1,224 @@ + + + + + + + + + + +
+ {{preview}} +
{{previewWhitespace}}
+
+ +
+ + + + +
+ +
+ + + + + +
+

{{heading}}

+
+ + + + + +
+{{body}} +
+ + + + + +
+ + + + + + + +
+ + + + + +
+ + + + + + +
Terms +
|
+
Privacy
+

+ © {{year}} Appwrite | 251 Little Falls Drive, Wilmington 19808, + Delaware, United States +

+
+ + \ No newline at end of file diff --git a/app/config/locale/templates/email-mfa-challenge.tpl b/app/config/locale/templates/email-mfa-challenge.tpl index 3e55227055..fdc0f4d498 100644 --- a/app/config/locale/templates/email-mfa-challenge.tpl +++ b/app/config/locale/templates/email-mfa-challenge.tpl @@ -5,7 +5,7 @@
-

{{otp}}

+
{{otp}}
diff --git a/app/config/locale/templates/email-otp.tpl b/app/config/locale/templates/email-otp.tpl index 84802c1603..e0d84005d6 100644 --- a/app/config/locale/templates/email-otp.tpl +++ b/app/config/locale/templates/email-otp.tpl @@ -5,7 +5,7 @@
-

{{otp}}

+
{{otp}}
diff --git a/app/config/locale/templates/email-verification.tpl b/app/config/locale/templates/email-verification.tpl new file mode 100644 index 0000000000..4b68f224db --- /dev/null +++ b/app/config/locale/templates/email-verification.tpl @@ -0,0 +1,9 @@ +

{{hello}}

+

{{body}}

+

{{buttonText}}

+

{{footer}}

+

+ {{thanks}} +
+ {{signature}} +

\ No newline at end of file diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 8aaa5283c4..355ad2ec5d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2295,6 +2295,10 @@ App::post('/v1/account/tokens/email') $preview = $locale->getText("emails.otpSession.preview"); $customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? []; + $customEmails = $project->getAttribute('customEmails', false); + $bodyTemplate = ''; + $heading = ''; + $detector = new Detector($request->getUserAgent('UNKNOWN')); $agentOs = $detector->getOS(); $agentClient = $detector->getClient(); @@ -2360,6 +2364,21 @@ App::post('/v1/account/tokens/email') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); + } else if ($customEmails) { + $subject = $customTemplate['subject']; + $preview = $customTemplate['preview']; + $heading = $customTemplate['heading']; + + $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-otp.tpl'); + $message + ->setParam('{{hello}}', $customTemplate['hello']) + ->setParam('{{description}}', $customTemplate['body'], escapeHtml: false) + ->setParam('{{thanks}}', $customTemplate['thanks']) + ->setParam('{{signature}}', $customTemplate['signature']) + ->setParam('{{clientInfo}}', ''); + + $body = $message->render(); + $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; } $emailVariables = [ @@ -2374,12 +2393,21 @@ App::post('/v1/account/tokens/email') 'phrase' => !empty($phrase) ? $phrase : '', // TODO: remove unnecessary team variable from this email 'team' => '', + 'heading' => $heading, + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, ]; $queueForMails ->setSubject($subject) ->setPreview($preview) ->setBody($body) + ->setBodyTemplate($bodyTemplate) ->setVariables($emailVariables) ->setRecipient($email) ->trigger(); @@ -3602,6 +3630,10 @@ App::post('/v1/account/verification') $senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; + $customEmails = $project->getAttribute('customEmails', false); + $bodyTemplate = ''; + $heading = ''; + if ($smtpEnabled) { if (!empty($smtp['senderEmail'])) { $senderEmail = $smtp['senderEmail']; @@ -3639,6 +3671,22 @@ App::post('/v1/account/verification') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); + } else if ($customEmails) { + $subject = $customTemplate['subject']; + $preview = $customTemplate['preview']; + $heading = $customTemplate['heading']; + + $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-verification.tpl'); + $message + ->setParam('{{hello}}', $customTemplate['hello']) + ->setParam('{{body}}', $customTemplate['body'], escapeHtml: false) + ->setParam('{{buttonText}}', $customTemplate['buttonText']) + ->setParam('{{footer}}', $customTemplate['footer']) + ->setParam('{{thanks}}', $customTemplate['thanks']) + ->setParam('{{signature}}', $customTemplate['signature']); + + $body = $message->render(); + $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; } $emailVariables = [ @@ -3649,12 +3697,21 @@ App::post('/v1/account/verification') 'project' => $projectName, // TODO: remove unnecessary team variable from this email 'team' => '', + 'heading' => $heading, + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, ]; $queueForMails ->setSubject($subject) ->setPreview($preview) ->setBody($body) + ->setBodyTemplate($bodyTemplate) ->setVariables($emailVariables) ->setRecipient($user->getAttribute('email')) ->setName($user->getAttribute('name') ?? '') @@ -4684,6 +4741,10 @@ App::post('/v1/account/mfa/challenge') $senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; + $customEmails = $project->getAttribute('customEmails', false); + $bodyTemplate = ''; + $heading = ''; + if ($smtpEnabled) { if (!empty($smtp['senderEmail'])) { $senderEmail = $smtp['senderEmail']; @@ -4721,6 +4782,21 @@ App::post('/v1/account/mfa/challenge') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); + } else if ($customEmails) { + $subject = $customTemplate['subject']; + $preview = $customTemplate['preview']; + $heading = $customTemplate['heading']; + + $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-mfa-challenge.tpl'); + $message + ->setParam('{{hello}}', $customTemplate['hello']) + ->setParam('{{description}}', $customTemplate['body'], escapeHtml: false) + ->setParam('{{thanks}}', $customTemplate['thanks']) + ->setParam('{{signature}}', $customTemplate['signature']) + ->setParam('{{clientInfo}}', ''); + + $body = $message->render(); + $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; } $emailVariables = [ @@ -4731,13 +4807,22 @@ App::post('/v1/account/mfa/challenge') 'otp' => $code, 'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN', 'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN', - 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN' + 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN', + 'heading' => $heading, + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, ]; $queueForMails ->setSubject($subject) ->setPreview($preview) ->setBody($body) + ->setBodyTemplate($bodyTemplate) ->setVariables($emailVariables) ->setRecipient($user->getAttribute('email')) ->trigger(); From 42d981c0ef813d213f1fec5021c4fd6cc66f5e57 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 16 Sep 2025 13:15:08 +1200 Subject: [PATCH 122/274] Add scopes to roles --- app/config/roles.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/config/roles.php b/app/config/roles.php index 0f0945a2b4..6a4bba2699 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -16,6 +16,8 @@ $member = [ 'documents.write', 'rows.read', 'rows.write', + 'transactions.read', + 'transactions.write', 'files.read', 'files.write', 'projects.read', @@ -41,6 +43,8 @@ $admins = [ 'documents.write', 'rows.read', 'rows.write', + 'transactions.read', + 'transactions.write', 'files.read', 'files.write', 'buckets.read', From fb65d7a965d0dc263fa7c28f8709bb9f6b0522ca Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 16 Sep 2025 13:25:15 +1200 Subject: [PATCH 123/274] Add scoped tests --- .../{TransactionsTest.php => TransactionsBase.php} | 8 +------- .../Transactions/TransactionsConsoleClientTest.php | 13 +++++++++++++ .../Transactions/TransactionsCustomClientTest.php | 13 +++++++++++++ .../Transactions/TransactionsCustomServerTest.php | 13 +++++++++++++ .../{TransactionsTest.php => TransactionsBase.php} | 8 +------- .../Transactions/TransactionsConsoleClientTest.php | 13 +++++++++++++ .../Transactions/TransactionsCustomClientTest.php | 13 +++++++++++++ .../Transactions/TransactionsCustomServerTest.php | 13 +++++++++++++ 8 files changed, 80 insertions(+), 14 deletions(-) rename tests/e2e/Services/Databases/Legacy/Transactions/{TransactionsTest.php => TransactionsBase.php} (99%) create mode 100644 tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php create mode 100644 tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomClientTest.php create mode 100644 tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomServerTest.php rename tests/e2e/Services/Databases/TablesDB/Transactions/{TransactionsTest.php => TransactionsBase.php} (99%) create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsConsoleClientTest.php create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomClientTest.php create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomServerTest.php diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php similarity index 99% rename from tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php rename to tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php index a76d909b5e..f05563123c 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php @@ -3,19 +3,13 @@ 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\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; -class TransactionsTest extends Scope +trait TransactionsBase { - use ProjectCustom; - use SideClient; - /** * Test creating a transaction */ diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php new file mode 100644 index 0000000000..427e79fb3a --- /dev/null +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php @@ -0,0 +1,13 @@ + Date: Tue, 16 Sep 2025 09:48:17 +0530 Subject: [PATCH 124/274] keep graphql backwards --- src/Appwrite/GraphQL/Types/Mapper.php | 29 +-------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index c6df475480..b74e2a7549 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -6,7 +6,6 @@ use Appwrite\GraphQL\Resolvers; use Appwrite\GraphQL\Types; use Appwrite\SDK\Method; use Exception; -use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; @@ -215,8 +214,6 @@ class Mapper if (\is_array($rule['type'])) { $type = self::getUnionType($escapedKey, $rule); - } elseif ($rule['type'] === 'enum' && !empty($rule['enum'])) { - $type = self::getEnumType($escapedKey, $rule); } else { $type = self::getObjectType($rule); } @@ -391,30 +388,6 @@ class Mapper return $type; } - private static function getEnumType(string $name, array $rule): Type - { - $enumTypeName = \ucfirst($name) . 'Enum'; - - if (Registry::has($enumTypeName)) { - return Registry::get($enumTypeName); - } - - $values = []; - foreach ($rule['enum'] as $enumValue) { - $values[\strtoupper(\str_replace(['-', ' ', '.'], '_', $enumValue))] = [ - 'value' => $enumValue - ]; - } - - $enumType = new EnumType([ - 'name' => $enumTypeName, - 'values' => $values - ]); - - Registry::set($enumTypeName, $enumType); - return $enumType; - } - private static function getObjectType(array $rule): Type { $type = $rule['type']; @@ -480,6 +453,7 @@ class Mapper 'ip' => static::model("{$prefix}Ip"), default => static::model("{$prefix}String"), }, + 'enum' => static::model("{$prefix}String"), // TODO: Add enum type (breaking change if added) 'integer' => static::model("{$prefix}Integer"), 'double' => static::model("{$prefix}Float"), 'boolean' => static::model("{$prefix}Boolean"), @@ -488,7 +462,6 @@ class Mapper 'point' => static::model("{$prefix}Point"), 'linestring' => static::model("{$prefix}Line"), 'polygon' => static::model("{$prefix}Polygon"), - 'enum' => static::model("{$prefix}Enum"), default => throw new Exception('Unknown ' . strtolower($prefix) . ' implementation'), }; } From f8ab95b3e15636fa25cb8f346d516d2b5b43ec89 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 16 Sep 2025 11:54:01 +0530 Subject: [PATCH 125/274] tests & lint --- app/config/console.php | 12 +----------- app/controllers/api/account.php | 6 +++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/app/config/console.php b/app/config/console.php index 164db597e9..9690cb8f28 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -71,16 +71,7 @@ $console = [ 'body' => 'Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.', 'thanks' => 'Thanks,', "signature" => "{{project}} team", - ], - 'email.otpSession-en' => [ - 'subject' => 'OTP for {{project}} account login', - 'preview' => 'Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.', - 'heading' => 'Login with OTP to use Appwrite Cloud', - 'hello' => 'Hello {{user}},', - 'body' => 'Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.', - 'thanks' => 'Thanks,', - "signature" => "{{project}} team", - ], + ] ], 'customEmails' => true, ]; @@ -88,7 +79,6 @@ $console = [ foreach ($localeCodes as $localeCode) { $console['templates']['email.verification-'.$localeCode['code']] = $console['templates']['email.verification-en']; $console['templates']['email.mfaChallenge-'.$localeCode['code']] = $console['templates']['email.mfaChallenge-en']; - $console['templates']['email.otpSession-'.$localeCode['code']] = $console['templates']['email.otpSession-en']; } return $console; diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 355ad2ec5d..37ebceba17 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2364,7 +2364,7 @@ App::post('/v1/account/tokens/email') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); - } else if ($customEmails) { + } elseif ($customEmails && !empty($customTemplate)) { $subject = $customTemplate['subject']; $preview = $customTemplate['preview']; $heading = $customTemplate['heading']; @@ -3671,7 +3671,7 @@ App::post('/v1/account/verification') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); - } else if ($customEmails) { + } elseif ($customEmails && !empty($customTemplate)) { $subject = $customTemplate['subject']; $preview = $customTemplate['preview']; $heading = $customTemplate['heading']; @@ -4782,7 +4782,7 @@ App::post('/v1/account/mfa/challenge') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); - } else if ($customEmails) { + } elseif ($customEmails && !empty($customTemplate)) { $subject = $customTemplate['subject']; $preview = $customTemplate['preview']; $heading = $customTemplate['heading']; From e6be9bac0774e38c4ddd9ef5660aa4e4932a0370 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 16 Sep 2025 11:55:43 +0530 Subject: [PATCH 126/274] cleanup --- app/config/locale/templates/email-auth-styled.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/locale/templates/email-auth-styled.tpl b/app/config/locale/templates/email-auth-styled.tpl index 07572c84da..544e58b7d8 100644 --- a/app/config/locale/templates/email-auth-styled.tpl +++ b/app/config/locale/templates/email-auth-styled.tpl @@ -38,7 +38,7 @@ background-color: #ffffff; margin: 0; padding: 0; - } 1Code has comments. Press enter to view. + } a { color: currentColor; word-break: break-all; From 614285f1ae1137ad086c171b8a709b30c06f6663 Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 16 Sep 2025 11:29:13 +0300 Subject: [PATCH 127/274] Add Scope for tests --- .../TablesDB/Transactions/TransactionsConsoleClientTest.php | 3 ++- .../TablesDB/Transactions/TransactionsCustomClientTest.php | 3 ++- .../TablesDB/Transactions/TransactionsCustomServerTest.php | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsConsoleClientTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsConsoleClientTest.php index 553c620f20..2159390fa2 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsConsoleClientTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsConsoleClientTest.php @@ -3,9 +3,10 @@ namespace Tests\E2E\Services\Databases\TablesDB\Transactions; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideConsole; -class TransactionsConsoleClientTest +class TransactionsConsoleClientTest extends Scope { use TransactionsBase; use ProjectCustom; diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomClientTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomClientTest.php index 3b7355faaf..732537fc7b 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomClientTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomClientTest.php @@ -3,9 +3,10 @@ namespace Tests\E2E\Services\Databases\TablesDB\Transactions; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; -class TransactionsCustomClientTest +class TransactionsCustomClientTest extends Scope { use TransactionsBase; use ProjectCustom; diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomServerTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomServerTest.php index 01d4a8d057..57588788b1 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomServerTest.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsCustomServerTest.php @@ -3,9 +3,10 @@ namespace Tests\E2E\Services\Databases\TablesDB\Transactions; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -class TransactionsCustomServerTest +class TransactionsCustomServerTest extends Scope { use TransactionsBase; use ProjectCustom; From 21f25b4ce4241923d52ffcefac3fc487dded330b Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 16 Sep 2025 14:16:02 +0530 Subject: [PATCH 128/274] Auto-allow sites domain for OAuth --- app/init/resources.php | 63 ++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index e4e8fbef5e..99b6e74382 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -151,7 +151,7 @@ App::setResource('queueForMigrations', function (Publisher $publisher) { App::setResource('queueForStatsResources', function (Publisher $publisher) { return new StatsResources($publisher); }, ['publisher']); -App::setResource('platforms', function (Request $request, Document $console, Document $project) { +App::setResource('platforms', function (Request $request, Document $console, Document $project, Database $dbForPlatform) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), 'name' => 'Current Host', @@ -190,11 +190,52 @@ App::setResource('platforms', function (Request $request, Document $console, Doc ], Document::SET_TYPE_APPEND); } + $origin = \parse_url($request->getOrigin(), PHP_URL_HOST); + + if (empty($origin)) { + $origin = \parse_url($request->getReferer(), PHP_URL_HOST); + } + + // Safe if rule with same project ID exists + if (!empty($origin)) { + if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { + $rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? ''))); + } else { + $rule = Authorization::skip( + fn () => $dbForPlatform->find('rules', [ + Query::equal('domain', [$origin]), + Query::limit(1) + ]) + )[0] ?? new Document(); + } + + var_dump($rule); + + if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) { + echo "inside project internal id\n"; + + $project->setAttribute('platforms', [ + '$collection' => ID::custom('platforms'), + 'type' => Platform::TYPE_WEB, + 'name' => $origin, + 'hostname' => $origin, + ], Document::SET_TYPE_APPEND); + } + } + + // Unsafe; Localhost is always safe for ease of local development + $project->setAttribute('platforms', [ + '$collection' => ID::custom('platforms'), + 'type' => Platform::TYPE_WEB, + 'name' => "localhost", + 'hostname' => "localhost", + ], Document::SET_TYPE_APPEND); + return [ ...$console->getAttribute('platforms', []), ...$project->getAttribute('platforms', []), ]; -}, ['request', 'console', 'project']); +}, ['request', 'console', 'project', 'dbForPlatform']); App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) { /** @var Appwrite\Utopia\Request $request */ @@ -1005,24 +1046,6 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef return $referrer; } - // Safe if rule with same project ID exists - if (!empty($origin)) { - if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { - $rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? ''))); - } else { - $rule = Authorization::skip( - fn () => $dbForPlatform->find('rules', [ - Query::equal('domain', [$origin]), - Query::limit(1) - ]) - )[0] ?? new Document(); - } - - if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) { - return $referrer; - } - } - // Unsafe; Localhost is always safe for ease of local development $origin = 'localhost'; $protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME); From b38b2bba0c81d184d67da5cf5ad78ea74494eb14 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 16 Sep 2025 21:13:41 +1200 Subject: [PATCH 129/274] Remove state order on created, rely on sequence order --- src/Appwrite/Databases/TransactionState.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 7d99d61d01..1caae8c629 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -31,7 +31,6 @@ class TransactionState // Fetch operations ordered by sequence to replay in exact order $operations = $this->dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), - Query::orderAsc('$createdAt'), // Ensure operations are processed in order ]); $state = []; From 7d1a1d3ef479160b05fb24df665ad3ade5e7d35d Mon Sep 17 00:00:00 2001 From: fogelito Date: Wed, 17 Sep 2025 08:45:23 +0300 Subject: [PATCH 130/274] use $tenant --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 9d998d05bc..bd34d53b00 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -504,7 +504,7 @@ class StatsUsage extends Action usort($this->statDocuments, function ($a, $b) { // Tenant ASC - $cmp = $a['_tenant'] <=> $b['_tenant']; + $cmp = $a['$tenant'] <=> $b['$tenant']; if ($cmp !== 0) { return $cmp; } From 481578047690d2d49fc699c4dd7eff96bebffd9f Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 17 Sep 2025 18:01:29 +0530 Subject: [PATCH 131/274] Add spatial column validation during required mode and tests for existing data in databases --- .../Collections/Attributes/Action.php | 9 +- .../Databases/Legacy/DatabasesBase.php | 130 +++++++++++++++++ .../Databases/TablesDB/DatabasesBase.php | 138 +++++++++++++++++- 3 files changed, 272 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index f3903d91a7..f8d49734eb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -366,13 +366,20 @@ abstract class Action extends UtopiaAction 'filters' => $filters, 'options' => $options, ]); - + if (!$dbForProject->getAdapter()->getSupportForSpatialIndexNull() && in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) && $attribute->getAttribute('required')) { + $existingDataPresent = $dbForProject->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); + if (count($existingDataPresent)) { + throw new StructureException('Failed to add required spatial column: existing rows present. Make the column optional.'); + } + } $dbForProject->checkAttribute($collection, $attribute); $attribute = $dbForProject->createDocument('attributes', $attribute); } catch (DuplicateException) { throw new Exception($this->getDuplicateException()); } catch (LimitException) { throw new Exception($this->getLimitException()); + } catch (StructureException $e) { + throw new Exception($this->getInvalidStructureException(), $e->getMessage()); } catch (Throwable $e) { $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 8d27aa7230..89c0cb6de0 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -7798,4 +7798,134 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); } + + public function testSpatialColCreateOnExistingData(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Spatial Distance Meters Database' + ]); + + $databaseId = $database['body']['$id']; + + $colId = ID::unique(); + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => $colId, + 'name' => 'spatial-test', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $description = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'description', + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $this->assertEquals(202, $description['headers']['status-code']); + sleep(2); + + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'description' => 'description' + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $document['headers']['status-code']); + + $point = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/point', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'loc', + 'required' => true, + ]); + + $this->assertEquals(400, $point['headers']['status-code']); + + $point = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/point', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'loc', + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(202, $point['headers']['status-code']); + + $line = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/line', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'route', + 'required' => true, + ]); + + $this->assertEquals(400, $line['headers']['status-code']); + + $line = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/line', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'route', + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(202, $line['headers']['status-code']); + + $poly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/polygon', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'area', + 'required' => true, + ]); + + $this->assertEquals(400, $poly['headers']['status-code']); + + $poly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/polygon', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'area', + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(202, $poly['headers']['status-code']); + } } diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index eba7ec96a7..70d4420828 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -3062,7 +3062,7 @@ trait DatabasesBase public function testInvalidRowStructure(): void { - $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + $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'] @@ -3735,7 +3735,7 @@ trait DatabasesBase public function testEnforceTableAndRowPermissions(): void { - $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + $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'] @@ -3928,7 +3928,7 @@ trait DatabasesBase public function testEnforceTablePermissions(): void { - $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + $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'] @@ -4284,7 +4284,7 @@ trait DatabasesBase public function testUpdatePermissionsWithEmptyPayload(): array { // Create Database - $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + $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'] @@ -8828,4 +8828,134 @@ trait DatabasesBase $this->client->call(Client::METHOD_DELETE, "/tablesdb/{$databaseId}", $headers); } + public function testSpatialColCreateOnExistingData(): void + { + $database = $this->client->call(Client::METHOD_POST, '/tablesdb', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Spatial Distance Meters Database' + ]); + + $databaseId = $database['body']['$id']; + + $tableId = ID::unique(); + $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' => $tableId, + 'name' => 'spatial-test', + 'rowSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + + $description = $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' => 'description', + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $this->assertEquals(202, $description['headers']['status-code']); + sleep(2); + + $row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'description' => 'description' + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $row['headers']['status-code']); + + $point = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'loc', + 'required' => true, + ]); + + $this->assertEquals(400, $point['headers']['status-code']); + + $point = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'loc', + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(202, $point['headers']['status-code']); + + $line = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'route', + 'required' => true, + ]); + + $this->assertEquals(400, $line['headers']['status-code']); + + $line = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'route', + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(202, $line['headers']['status-code']); + + $poly = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'area', + 'required' => true, + ]); + + $this->assertEquals(400, $poly['headers']['status-code']); + + $poly = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'area', + 'required' => false, + 'default' => null + ]); + + $this->assertEquals(202, $poly['headers']['status-code']); + } + } From 5b09ebe54b494cd6479612b2ff850f176fd4f13c Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 17 Sep 2025 19:46:06 +0530 Subject: [PATCH 132/274] added cases for with default values --- .../Databases/Legacy/DatabasesBase.php | 100 ++++++++++++++++++ .../Databases/TablesDB/DatabasesBase.php | 100 ++++++++++++++++++ 2 files changed, 200 insertions(+) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 89c0cb6de0..1dab5076ec 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -7928,4 +7928,104 @@ trait DatabasesBase $this->assertEquals(202, $poly['headers']['status-code']); } + + public function testSpatialColCreateOnExistingDataWithDefaults(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Spatial With Defaults Database' + ]); + + $databaseId = $database['body']['$id']; + + $colId = ID::unique(); + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => $colId, + 'name' => 'spatial-test-defaults', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $description = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'description', + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $this->assertEquals(202, $description['headers']['status-code']); + sleep(2); + + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'description' => 'description' + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $document['headers']['status-code']); + + // Test point with default value + $point = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/point', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'loc', + 'required' => false, + 'default' => [0.0, 0.0] + ]); + + $this->assertEquals(202, $point['headers']['status-code']); + + // Test line with default value + $line = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/line', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'route', + 'required' => false, + 'default' => [[0.0, 0.0], [1.0, 1.0]] + ]); + + $this->assertEquals(202, $line['headers']['status-code']); + + // Test polygon with default value + $poly = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/attributes/polygon', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'area', + 'required' => false, + 'default' => [[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]] + ]); + + $this->assertEquals(202, $poly['headers']['status-code']); + } } diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 70d4420828..ebfa0132e6 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -8958,4 +8958,104 @@ trait DatabasesBase $this->assertEquals(202, $poly['headers']['status-code']); } + public function testSpatialColCreateOnExistingDataWithDefaults(): void + { + $database = $this->client->call(Client::METHOD_POST, '/tablesdb', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Spatial With Defaults Database' + ]); + + $databaseId = $database['body']['$id']; + + $tableId = ID::unique(); + $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' => $tableId, + 'name' => 'spatial-test-defaults', + 'rowSecurity' => true, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + + $description = $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' => 'description', + 'size' => 512, + 'required' => false, + 'default' => '', + ]); + + $this->assertEquals(202, $description['headers']['status-code']); + sleep(2); + + $row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'description' => 'description' + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $row['headers']['status-code']); + + // Test point with default value + $point = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/point', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'loc', + 'required' => false, + 'default' => [0.0, 0.0] + ]); + + $this->assertEquals(202, $point['headers']['status-code']); + + // Test line with default value + $line = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/line', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'route', + 'required' => false, + 'default' => [[0.0, 0.0], [1.0, 1.0]] + ]); + + $this->assertEquals(202, $line['headers']['status-code']); + + // Test polygon with default value + $poly = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/polygon', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'area', + 'required' => false, + 'default' => [[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]] + ]); + + $this->assertEquals(202, $poly['headers']['status-code']); + } + } From c5e9ec396929e6683c7f55771046b88604ba2c9e Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 17 Sep 2025 20:00:10 +0530 Subject: [PATCH 133/274] added cases for checking default added or not --- .../Databases/Legacy/DatabasesBase.php | 35 +++++++++++++++++++ .../Databases/TablesDB/DatabasesBase.php | 35 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 1dab5076ec..c9a293dcb0 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -8027,5 +8027,40 @@ trait DatabasesBase ]); $this->assertEquals(202, $poly['headers']['status-code']); + + // Wait for attributes to be available + sleep(2); + + // Create a new document without spatial data to test default values + $newDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $colId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'description' => 'test default values' + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $newDocument['headers']['status-code']); + + $newDocumentId = $newDocument['body']['$id']; + + // Fetch the document to verify default values are applied + $fetchedDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $colId . '/documents/' . $newDocumentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $fetchedDocument['headers']['status-code']); + + // Verify default values are applied + $this->assertEquals([0.0, 0.0], $fetchedDocument['body']['loc']); + $this->assertEquals([[0.0, 0.0], [1.0, 1.0]], $fetchedDocument['body']['route']); + $this->assertEquals([[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]], $fetchedDocument['body']['area']); } } diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index ebfa0132e6..7be23ca136 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -9056,6 +9056,41 @@ trait DatabasesBase ]); $this->assertEquals(202, $poly['headers']['status-code']); + + // Wait for columns to be available + sleep(2); + + // Create a new row without spatial data to test default values + $newRow = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => ID::unique(), + 'data' => [ + 'description' => 'test default values' + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + $this->assertEquals(201, $newRow['headers']['status-code']); + + $newRowId = $newRow['body']['$id']; + + // Fetch the row to verify default values are applied + $fetchedRow = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $newRowId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $fetchedRow['headers']['status-code']); + + // Verify default values are applied + $this->assertEquals([0.0, 0.0], $fetchedRow['body']['loc']); + $this->assertEquals([[0.0, 0.0], [1.0, 1.0]], $fetchedRow['body']['route']); + $this->assertEquals([[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]], $fetchedRow['body']['area']); } } From 52ceddcd5ae1dad4ccf364cb931d0d9634275572 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 17 Sep 2025 20:02:36 +0530 Subject: [PATCH 134/274] linting --- tests/e2e/Services/Databases/Legacy/DatabasesBase.php | 8 ++++---- tests/e2e/Services/Databases/TablesDB/DatabasesBase.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index c9a293dcb0..8b4f78d3ea 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -8027,7 +8027,7 @@ trait DatabasesBase ]); $this->assertEquals(202, $poly['headers']['status-code']); - + // Wait for attributes to be available sleep(2); @@ -8047,7 +8047,7 @@ trait DatabasesBase ] ]); $this->assertEquals(201, $newDocument['headers']['status-code']); - + $newDocumentId = $newDocument['body']['$id']; // Fetch the document to verify default values are applied @@ -8055,9 +8055,9 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - + $this->assertEquals(200, $fetchedDocument['headers']['status-code']); - + // Verify default values are applied $this->assertEquals([0.0, 0.0], $fetchedDocument['body']['loc']); $this->assertEquals([[0.0, 0.0], [1.0, 1.0]], $fetchedDocument['body']['route']); diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 7be23ca136..387a7ec3f0 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -9056,7 +9056,7 @@ trait DatabasesBase ]); $this->assertEquals(202, $poly['headers']['status-code']); - + // Wait for columns to be available sleep(2); @@ -9076,7 +9076,7 @@ trait DatabasesBase ] ]); $this->assertEquals(201, $newRow['headers']['status-code']); - + $newRowId = $newRow['body']['$id']; // Fetch the row to verify default values are applied @@ -9084,9 +9084,9 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - + $this->assertEquals(200, $fetchedRow['headers']['status-code']); - + // Verify default values are applied $this->assertEquals([0.0, 0.0], $fetchedRow['body']['loc']); $this->assertEquals([[0.0, 0.0], [1.0, 1.0]], $fetchedRow['body']['route']); From 0e2c5d93f5951a7f20cb0343593edeeb8791dd9a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 18 Sep 2025 14:54:07 +1200 Subject: [PATCH 135/274] Update check --- composer.lock | 41 +++++++++---------- .../Collections/Attributes/Action.php | 21 ++++++---- .../Collections/Documents/Action.php | 2 +- .../Collections/Documents/Bulk/Update.php | 2 +- .../Collections/Documents/Bulk/Upsert.php | 2 +- .../Collections/Documents/Create.php | 2 +- .../Collections/Documents/Update.php | 2 +- .../Collections/Documents/Upsert.php | 2 +- 8 files changed, 39 insertions(+), 35 deletions(-) diff --git a/composer.lock b/composer.lock index 0ed0d05dc5..d8c17fbc91 100644 --- a/composer.lock +++ b/composer.lock @@ -1352,16 +1352,16 @@ }, { "name": "open-telemetry/gen-otlp-protobuf", - "version": "1.5.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", - "reference": "585bafddd4ae6565de154610b10a787a455c9ba0" + "reference": "673af5b06545b513466081884b47ef15a536edde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/585bafddd4ae6565de154610b10a787a455c9ba0", - "reference": "585bafddd4ae6565de154610b10a787a455c9ba0", + "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/673af5b06545b513466081884b47ef15a536edde", + "reference": "673af5b06545b513466081884b47ef15a536edde", "shasum": "" }, "require": { @@ -1411,7 +1411,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-15T23:07:07+00:00" + "time": "2025-09-17T23:10:12+00:00" }, { "name": "open-telemetry/sdk", @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "1.4.9", + "version": "1.4.10", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "066e2bda7b728bb843776db3640737d7350ba035" + "reference": "5514bb7346e75996d061d08040248fe842f73785" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/066e2bda7b728bb843776db3640737d7350ba035", - "reference": "066e2bda7b728bb843776db3640737d7350ba035", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5514bb7346e75996d061d08040248fe842f73785", + "reference": "5514bb7346e75996d061d08040248fe842f73785", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.4.9" + "source": "https://github.com/utopia-php/database/tree/1.4.10" }, - "time": "2025-09-16T13:31:52+00:00" + "time": "2025-09-18T02:42:25+00:00" }, { "name": "utopia-php/detector", @@ -5278,16 +5278,16 @@ }, { "name": "laravel/pint", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" + "reference": "595de38458c6b0ab4cae4bcc769c2e5c5d5b8e96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "url": "https://api.github.com/repos/laravel/pint/zipball/595de38458c6b0ab4cae4bcc769c2e5c5d5b8e96", + "reference": "595de38458c6b0ab4cae4bcc769c2e5c5d5b8e96", "shasum": "" }, "require": { @@ -5298,9 +5298,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.82.2", - "illuminate/view": "^11.45.1", - "larastan/larastan": "^3.5.0", + "friendsofphp/php-cs-fixer": "^3.87.2", + "illuminate/view": "^11.46.0", + "larastan/larastan": "^3.7.1", "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3.1", @@ -5311,9 +5311,6 @@ ], "type": "project", "autoload": { - "files": [ - "overrides/Runner/Parallel/ProcessFactory.php" - ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -5343,7 +5340,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-07-10T18:09:32+00:00" + "time": "2025-09-17T01:36:44+00:00" }, { "name": "matthiasmullie/minify", diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php index f8d49734eb..22a90d2653 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Action.php @@ -122,7 +122,7 @@ abstract class Action extends UtopiaAction /** * Get the correct invalid structure message. */ - protected function getInvalidStructureException(): string + protected function getStructureException(): string { return $this->isCollectionsAPI() ? Exception::DOCUMENT_INVALID_STRUCTURE @@ -366,9 +366,16 @@ abstract class Action extends UtopiaAction 'filters' => $filters, 'options' => $options, ]); - if (!$dbForProject->getAdapter()->getSupportForSpatialIndexNull() && in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) && $attribute->getAttribute('required')) { - $existingDataPresent = $dbForProject->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); - if (count($existingDataPresent)) { + if ( + !$dbForProject->getAdapter()->getSupportForSpatialIndexNull() && + \in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) && + $attribute->getAttribute('required') + ) { + $hasData = !Authorization::skip(fn () => $dbForProject + ->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence())) + ->isEmpty(); + + if ($hasData) { throw new StructureException('Failed to add required spatial column: existing rows present. Make the column optional.'); } } @@ -379,7 +386,7 @@ abstract class Action extends UtopiaAction } catch (LimitException) { throw new Exception($this->getLimitException()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } catch (Throwable $e) { $dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId); $dbForProject->purgeCachedCollection('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()); @@ -423,7 +430,7 @@ abstract class Action extends UtopiaAction } catch (LimitException) { throw new Exception($this->getLimitException()); } catch (StructureException) { - throw new Exception($this->getInvalidStructureException()); + throw new Exception($this->getStructureException()); } catch (Throwable $e) { $dbForProject->deleteDocument('attributes', $attribute->getId()); throw $e; @@ -587,7 +594,7 @@ abstract class Action extends UtopiaAction } catch (RelationshipException $e) { throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } if ($primaryDocumentOptions['twoWay']) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index d1d0738990..e05e588201 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -160,7 +160,7 @@ abstract class Action extends AppwriteAction /** * Get the correct invalid structure message. */ - protected function getInvalidStructureException(): string + protected function getStructureException(): string { return $this->isCollectionsAPI() ? Exception::DOCUMENT_INVALID_STRUCTURE diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 0f0ae14020..baab45cdd3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -149,7 +149,7 @@ class Update extends Action } catch (RelationshipException $e) { throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } foreach ($documents as $document) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 395e3d757b..4ce3990a38 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -130,7 +130,7 @@ class Upsert extends Action } catch (RelationshipException $e) { throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } foreach ($upserted as $document) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index c03daabd4f..a8af1eda86 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -375,7 +375,7 @@ class Create extends Action } catch (RelationshipException $e) { throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 8382bdd5e9..e510aeb089 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -247,7 +247,7 @@ class Update extends Action } catch (RelationshipException $e) { throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } $collectionsCache = []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 54b1cad950..3ac5e704e3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -258,7 +258,7 @@ class Upsert extends Action } catch (RelationshipException $e) { throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { - throw new Exception($this->getInvalidStructureException(), $e->getMessage()); + throw new Exception($this->getStructureException(), $e->getMessage()); } $collectionsCache = []; From 427c891e0fa28bc822cd3cfab8de41307d0e33fb Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 18 Sep 2025 10:13:23 +0530 Subject: [PATCH 136/274] update cli to 10.0.0 --- app/config/platforms.php | 2 +- composer.lock | 12 ++++++------ docs/sdks/cli/CHANGELOG.md | 9 +++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 7623bb896e..7aec82d1cf 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '9.1.0', + 'version' => '10.0.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, diff --git a/composer.lock b/composer.lock index d8c17fbc91..b31d0f78bd 100644 --- a/composer.lock +++ b/composer.lock @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.3.4", + "version": "1.3.5", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac" + "reference": "6fda9e58b37c9872c1a2a424e5467de8de1bc567" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac", - "reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6fda9e58b37c9872c1a2a424e5467de8de1bc567", + "reference": "6fda9e58b37c9872c1a2a424e5467de8de1bc567", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.3.4" + "source": "https://github.com/appwrite/sdk-generator/tree/1.3.5" }, - "time": "2025-09-08T11:56:04+00:00" + "time": "2025-09-15T04:19:40+00:00" }, { "name": "doctrine/annotations", diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 8964d059a9..000a7e0938 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 10.0.0 + +* **Breaking:** Removed Avatars CLI command and all related subcommands; corresponding examples deleted +* **Feat:** Geo defaults now accept coordinate arrays for Databases and Tables DB, with automatic normalization +* **Feat:** Pull command skips deprecated resources by default and shows clearer totals/messages +* **Feat:** Updated CLI descriptions: Databases marked legacy; added tables-db, projects, and project +* Fix TypeScript type generation now quotes invalid property names to produce valid typings +* Update documentation: Removed Avatars CLI examples and updated help text to reflect new geo defaults and terminology + ## 8.3.0 * **Feat:** Add support for `appwrite.config.json` file From 2021396f39660bc915c7b873771e20c7c28cfa05 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 18 Sep 2025 12:33:42 +0530 Subject: [PATCH 137/274] email otp branded --- app/config/console.php | 16 ++++-- app/config/locale/templates/email-otp.tpl | 4 +- app/controllers/api/account.php | 59 +++++++++++++---------- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/config/console.php b/app/config/console.php index 9690cb8f28..ae6c15fd64 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -71,14 +71,24 @@ $console = [ 'body' => 'Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.', 'thanks' => 'Thanks,', "signature" => "{{project}} team", - ] + ], + 'email.otpSession-en' => [ + 'subject' => 'OTP for {{project}} account login', + 'preview' => 'Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.', + 'heading' => 'Login with OTP to use Appwrite Cloud', + 'hello' => 'Hello {{user}},', + 'body' => 'Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.', + 'thanks' => 'Thanks,', + "signature" => "{{project}} team", + ], ], 'customEmails' => true, ]; foreach ($localeCodes as $localeCode) { - $console['templates']['email.verification-'.$localeCode['code']] = $console['templates']['email.verification-en']; - $console['templates']['email.mfaChallenge-'.$localeCode['code']] = $console['templates']['email.mfaChallenge-en']; + $console['templates']['email.verification-' . $localeCode['code']] = $console['templates']['email.verification-en']; + $console['templates']['email.mfaChallenge-' . $localeCode['code']] = $console['templates']['email.mfaChallenge-en']; + $console['templates']['email.otpSession-' . $localeCode['code']] = $console['templates']['email.otpSession-en']; } return $console; diff --git a/app/config/locale/templates/email-otp.tpl b/app/config/locale/templates/email-otp.tpl index e0d84005d6..aebc512e1b 100644 --- a/app/config/locale/templates/email-otp.tpl +++ b/app/config/locale/templates/email-otp.tpl @@ -5,7 +5,7 @@
-
{{otp}}
+
{{otp}}
@@ -15,6 +15,6 @@

{{thanks}}

{{signature}}

-
+

{{securityPhrase}}

\ No newline at end of file diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 37ebceba17..bde3672774 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -165,7 +165,8 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc ->setVariables($emailVariables) ->setRecipient($email) ->trigger(); -}; +} +; $createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) { @@ -838,7 +839,7 @@ App::patch('/v1/account/sessions/:sessionId') $session ->setAttribute('providerAccessToken', $oauth2->getAccessToken('')) ->setAttribute('providerRefreshToken', $oauth2->getRefreshToken('')) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry(''))); + ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $oauth2->getAccessTokenExpiry(''))); } // Save changes @@ -982,9 +983,11 @@ App::post('/v1/account/sessions/email') ; if ($project->getAttribute('auths', [])['sessionAlerts'] ?? false) { - if ($dbForProject->count('sessions', [ - Query::equal('userId', [$user->getId()]), - ]) !== 1) { + if ( + $dbForProject->count('sessions', [ + Query::equal('userId', [$user->getId()]), + ]) !== 1 + ) { sendSessionAlert($locale, $user, $project, $session, $queueForMails); } } @@ -1098,7 +1101,7 @@ App::post('/v1/account/sessions/anonymous') Authorization::setRole(Role::user($user->getId())->toString()); - $session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [ + $session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), Permission::delete(Role::user($user->getId())), @@ -1659,13 +1662,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'providerEmail' => $email, 'providerAccessToken' => $accessToken, 'providerRefreshToken' => $refreshToken, - 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry), + 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry), ])); } else { $identity ->setAttribute('providerAccessToken', $accessToken) ->setAttribute('providerRefreshToken', $refreshToken) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry)); + ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry)); $dbForProject->updateDocument('identities', $identity->getId(), $identity); } @@ -1735,7 +1738,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'providerUid' => $oauth2ID, 'providerAccessToken' => $accessToken, 'providerRefreshToken' => $refreshToken, - 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry), + 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry), 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -2314,8 +2317,10 @@ App::post('/v1/account/tokens/email') if (!empty($phrase)) { $message->setParam('{{securityPhrase}}', $locale->getText("emails.otpSession.securityPhrase")); + $message->setParam('{{securityPhraseDividerDisplay}}', 'block'); } else { $message->setParam('{{securityPhrase}}', ''); + $message->setParam('{{securityPhraseDividerDisplay}}', 'none'); } $body = $message->render(); @@ -2375,7 +2380,9 @@ App::post('/v1/account/tokens/email') ->setParam('{{description}}', $customTemplate['body'], escapeHtml: false) ->setParam('{{thanks}}', $customTemplate['thanks']) ->setParam('{{signature}}', $customTemplate['signature']) - ->setParam('{{clientInfo}}', ''); + ->setParam('{{clientInfo}}', '') + ->setParam('{{securityPhrase}}', '') + ->setParam('{{securityPhraseDividerDisplay}}', 'none'); $body = $message->render(); $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; @@ -2760,10 +2767,12 @@ App::post('/v1/account/jwts') $response ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic(new Document(['jwt' => $jwt->encode([ - 'userId' => $user->getId(), - 'sessionId' => $current->getId(), - ])]), Response::MODEL_JWT); + ->dynamic(new Document([ + 'jwt' => $jwt->encode([ + 'userId' => $user->getId(), + 'sessionId' => $current->getId(), + ]) + ]), Response::MODEL_JWT); }); App::get('/v1/account/prefs') @@ -3508,12 +3517,12 @@ App::put('/v1/account/recovery') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile - ->setAttribute('password', $newPassword) - ->setAttribute('passwordHistory', $history) - ->setAttribute('passwordUpdate', DateTime::now()) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) - ->setAttribute('emailVerification', true)); + ->setAttribute('password', $newPassword) + ->setAttribute('passwordHistory', $history) + ->setAttribute('passwordUpdate', DateTime::now()) + ->setAttribute('hash', Auth::DEFAULT_ALGO) + ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) + ->setAttribute('emailVerification', true)); $user->setAttributes($profile->getArrayCopy()); @@ -4945,8 +4954,8 @@ App::put('/v1/account/mfa/challenge') $dbForProject->updateDocument('sessions', $session->getId(), $session); $queueForEvents - ->setParam('userId', $user->getId()) - ->setParam('sessionId', $session->getId()); + ->setParam('userId', $user->getId()) + ->setParam('sessionId', $session->getId()); $response->dynamic($session, Response::MODEL_SESSION); }); @@ -5009,7 +5018,7 @@ App::post('/v1/account/targets/push') ], 'providerId' => !empty($providerId) ? $providerId : null, 'providerInternalId' => !empty($providerId) ? $provider->getSequence() : null, - 'providerType' => MESSAGE_TYPE_PUSH, + 'providerType' => MESSAGE_TYPE_PUSH, 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), 'sessionId' => $session->getId(), @@ -5184,8 +5193,8 @@ App::get('/v1/account/identities') $queries[] = Query::equal('userInternalId', [$user->getSequence()]); /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ $cursor = \array_filter($queries, function ($query) { return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); }); From dedc7a35c96f3e4a7bc89ff363fabf2d35e5be86 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 18 Sep 2025 12:38:18 +0530 Subject: [PATCH 138/274] remove debug output --- app/init/resources.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 99b6e74382..4b5452eb46 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -212,8 +212,6 @@ App::setResource('platforms', function (Request $request, Document $console, Doc var_dump($rule); if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) { - echo "inside project internal id\n"; - $project->setAttribute('platforms', [ '$collection' => ID::custom('platforms'), 'type' => Platform::TYPE_WEB, @@ -416,7 +414,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant((int)$project->getSequence()) + ->setTenant((int) $project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database @@ -469,7 +467,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant((int)$project->getSequence()) + ->setTenant((int) $project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database @@ -499,7 +497,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { return function (?Document $project = null) use ($pools, $cache, &$database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant((int)$project->getSequence()); + $database->setTenant((int) $project->getSequence()); return $database; } @@ -514,7 +512,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { // set tenant if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant((int)$project->getSequence()); + $database->setTenant((int) $project->getSequence()); } return $database; @@ -542,7 +540,7 @@ App::setResource('redis', function () { $pass = System::getEnv('_APP_REDIS_PASS', ''); $redis = new \Redis(); - @$redis->pconnect($host, (int)$port); + @$redis->pconnect($host, (int) $port); if ($pass) { $redis->auth($pass); } @@ -755,7 +753,7 @@ App::setResource('schema', function ($utopia, $dbForProject) { // NOTE: `params` and `urls` are not used internally in the `Schema::build` function below! $params = [ 'list' => function (string $databaseId, string $collectionId, array $args) { - return [ 'queries' => $args['queries']]; + return ['queries' => $args['queries']]; }, 'create' => function (string $databaseId, string $collectionId, array $args) { $id = $args['id'] ?? 'unique()'; @@ -1004,7 +1002,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) { } $accessedAt = $token->getAttribute('accessedAt', 0); - if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), - APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) { + if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) { $token->setAttribute('accessedAt', DatabaseDateTime::now()); Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token)); } From a7e9e58312afbb93b49eea2fee1a61c05b2767dc Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 Sep 2025 10:57:47 +0300 Subject: [PATCH 139/274] Add order by --- app/init/database/filters.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/init/database/filters.php b/app/init/database/filters.php index 33f5d8077a..8261cdbc30 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -255,6 +255,8 @@ Database::addFilter( ->find('variables', [ Query::equal('resourceInternalId', [$document->getSequence()]), Query::equal('resourceType', $resourceType), + Query::orderAsc('resourceType'), + Query::orderAsc('$sequence'), Query::limit(APP_LIMIT_SUBQUERY), ]); } From 4da7f04a5c81d8835857cfeacefafb4feb0a0dc4 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 Sep 2025 10:58:27 +0300 Subject: [PATCH 140/274] Add order by --- app/init/database/filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init/database/filters.php b/app/init/database/filters.php index 8261cdbc30..c4cfd1ac81 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -256,7 +256,7 @@ Database::addFilter( Query::equal('resourceInternalId', [$document->getSequence()]), Query::equal('resourceType', $resourceType), Query::orderAsc('resourceType'), - Query::orderAsc('$sequence'), + Query::orderAsc(), Query::limit(APP_LIMIT_SUBQUERY), ]); } From 0812ac97a997012dfa3eaa817bd2f04027ade901 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 18 Sep 2025 14:09:42 +0530 Subject: [PATCH 141/274] tests --- app/config/console.php | 2 +- app/config/locale/templates/email-auth-styled.tpl | 1 + tests/e2e/Services/Account/AccountBase.php | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/config/console.php b/app/config/console.php index ae6c15fd64..6f44368060 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -73,7 +73,7 @@ $console = [ "signature" => "{{project}} team", ], 'email.otpSession-en' => [ - 'subject' => 'OTP for {{project}} account login', + 'subject' => 'OTP for {{project}} Login', 'preview' => 'Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.', 'heading' => 'Login with OTP to use Appwrite Cloud', 'hello' => 'Hello {{user}},', diff --git a/app/config/locale/templates/email-auth-styled.tpl b/app/config/locale/templates/email-auth-styled.tpl index 544e58b7d8..a826c62e95 100644 --- a/app/config/locale/templates/email-auth-styled.tpl +++ b/app/config/locale/templates/email-auth-styled.tpl @@ -144,6 +144,7 @@ Appwrite logo diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 8813e2784f..6cf997e22c 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -152,6 +152,8 @@ trait AccountBase public function testEmailOTPSession(): void { + $isConsoleProject = $this->getProject()['$id'] === 'console'; + $response = $this->client->call(Client::METHOD_POST, '/account/tokens/email', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -183,6 +185,16 @@ trait AccountBase $this->assertNotEmpty($code); $this->assertStringContainsStringIgnoringCase('Use OTP ' . $code . ' to sign in to '. $this->getProject()['name'] . '. Expires in 15 minutes.', $lastEmail['text']); + // Only Console project has branded logo in email. + if ($isConsoleProject) { + $this->assertStringContainsStringIgnoringCase('Appwrite logo', $lastEmail['html']); + } + + // TODO: Remove this once OTP login is supported for Console. + if ($isConsoleProject) { + return; + } + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', From 5fd2dfafb72ab4500cc4e2d3b0279a3bb21583af Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 18 Sep 2025 14:10:56 +0530 Subject: [PATCH 142/274] remove var_dump --- app/init/resources.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 4b5452eb46..211bc52a8e 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -209,8 +209,6 @@ App::setResource('platforms', function (Request $request, Document $console, Doc )[0] ?? new Document(); } - var_dump($rule); - if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) { $project->setAttribute('platforms', [ '$collection' => ID::custom('platforms'), From 4b90998d1e0844b3b1de078f113e1adafbfe34b6 Mon Sep 17 00:00:00 2001 From: Veeresh <75656445+Veera-mulge@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:04:27 +0530 Subject: [PATCH 143/274] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 89390515eb..4cba193d4d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> We just announced inversion queries for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-inversion-queries) +> We just announced API for spatial columns for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-spatial-columns) > Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga) From d9b657796b6f84d239802c44040ade2e3bb92a49 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 18 Sep 2025 17:49:38 +0300 Subject: [PATCH 144/274] bump database 1.5.0 --- composer.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index d8c17fbc91..98089f9ed9 100644 --- a/composer.lock +++ b/composer.lock @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "1.4.10", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "5514bb7346e75996d061d08040248fe842f73785" + "reference": "24c4519b4ac32aee13af31dddd984db2a3b34980" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5514bb7346e75996d061d08040248fe842f73785", - "reference": "5514bb7346e75996d061d08040248fe842f73785", + "url": "https://api.github.com/repos/utopia-php/database/zipball/24c4519b4ac32aee13af31dddd984db2a3b34980", + "reference": "24c4519b4ac32aee13af31dddd984db2a3b34980", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.4.10" + "source": "https://github.com/utopia-php/database/tree/1.5.0" }, - "time": "2025-09-18T02:42:25+00:00" + "time": "2025-09-18T14:42:01+00:00" }, { "name": "utopia-php/detector", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.3.4", + "version": "1.3.5", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac" + "reference": "6fda9e58b37c9872c1a2a424e5467de8de1bc567" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac", - "reference": "d3b420dced42f1eec1f6d0aa98b7bbf8de4042ac", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6fda9e58b37c9872c1a2a424e5467de8de1bc567", + "reference": "6fda9e58b37c9872c1a2a424e5467de8de1bc567", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.3.4" + "source": "https://github.com/appwrite/sdk-generator/tree/1.3.5" }, - "time": "2025-09-08T11:56:04+00:00" + "time": "2025-09-15T04:19:40+00:00" }, { "name": "doctrine/annotations", @@ -8506,7 +8506,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From c1782f6f58998cf5a6f3d5fd270ed697f98999b1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 19 Sep 2025 15:28:41 +1200 Subject: [PATCH 145/274] Don't remove required attributes --- .../Databases/Collections/Documents/XList.php | 37 ------------------- .../Databases/Legacy/DatabasesBase.php | 12 +++--- .../Databases/TablesDB/DatabasesBase.php | 12 +++--- 3 files changed, 12 insertions(+), 49 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 9c8405cf18..546cbeddd4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -158,43 +158,6 @@ class XList extends Action ->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); - // Check if the SELECT query includes the removable attributes - $hasWildcard = false; - $hasSelectQueries = !empty($selectQueries); - $requestedAttributes = []; - - if ($hasSelectQueries) { - foreach ($selectQueries as $query) { - if ($query->getMethod() !== Query::TYPE_SELECT) { - continue; - } - - $values = $query->getValues(); - if (\in_array('*', $values, true)) { - $hasWildcard = true; - break; - } - - // Check which removable attributes are explicitly requested - foreach ($this->removableAttributes['*'] as $attribute) { - if (\in_array($attribute, $values, true)) { - $requestedAttributes[$attribute] = true; - } - } - } - - if (!$hasWildcard) { - foreach ($documents as $document) { - // Remove attributes that are not explicitly requested - foreach ($this->removableAttributes['*'] as $attribute) { - if (!isset($requestedAttributes[$attribute])) { - $document->removeAttribute($attribute); - } - } - } - } - } - $response->dynamic(new Document([ 'total' => $total, // rows or documents diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 8b4f78d3ea..a432bc0acd 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -5270,8 +5270,8 @@ trait DatabasesBase $this->assertEquals(2, count($response['body']['documents'])); $this->assertEquals(null, $response['body']['documents'][0]['fullName']); $this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]); + $this->assertArrayHasKey('$databaseId', $response['body']['documents'][0]); + $this->assertArrayHasKey('$collectionId', $response['body']['documents'][0]); } /** @@ -5291,8 +5291,8 @@ trait DatabasesBase $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayNotHasKey('libraries', $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['documents'][0]); - $this->assertArrayNotHasKey('$collectionId', $response['body']['documents'][0]); + $this->assertArrayHasKey('$databaseId', $response['body']['documents'][0]); + $this->assertArrayHasKey('$collectionId', $response['body']['documents'][0]); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -5305,8 +5305,8 @@ trait DatabasesBase $document = $response['body']['documents'][0]; $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('libraries', $document); - $this->assertArrayNotHasKey('$databaseId', $document); - $this->assertArrayNotHasKey('$collectionId', $document); + $this->assertArrayHasKey('$databaseId', $document); + $this->assertArrayHasKey('$collectionId', $document); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['personCollection'] . '/documents/' . $document['$id'], array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 387a7ec3f0..336193f5d9 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -5197,8 +5197,8 @@ trait DatabasesBase $this->assertEquals(2, count($response['body']['rows'])); $this->assertEquals(null, $response['body']['rows'][0]['fullName']); $this->assertArrayNotHasKey("libraries", $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); + $this->assertArrayHasKey('$databaseId', $response['body']['rows'][0]); + $this->assertArrayHasKey('$tableId', $response['body']['rows'][0]); } /** @@ -5218,8 +5218,8 @@ trait DatabasesBase $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayNotHasKey('libraries', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$databaseId', $response['body']['rows'][0]); - $this->assertArrayNotHasKey('$tableId', $response['body']['rows'][0]); + $this->assertArrayHasKey('$databaseId', $response['body']['rows'][0]); + $this->assertArrayHasKey('$tableId', $response['body']['rows'][0]); $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -5232,8 +5232,8 @@ trait DatabasesBase $row = $response['body']['rows'][0]; $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('libraries', $row); - $this->assertArrayNotHasKey('$databaseId', $row); - $this->assertArrayNotHasKey('$tableId', $row); + $this->assertArrayHasKey('$databaseId', $row); + $this->assertArrayHasKey('$tableId', $row); $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['personCollection'] . '/rows/' . $row['$id'], array_merge([ 'content-type' => 'application/json', From f944ad350ec8e356523b7651058f6ea1259c3c87 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 19 Sep 2025 17:32:34 +1200 Subject: [PATCH 146/274] Catch query exception on bulk update/delete --- .../Http/Databases/Collections/Documents/Bulk/Delete.php | 2 ++ .../Http/Databases/Collections/Documents/Bulk/Update.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index fc4e2a8a91..3467a9d11c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -124,6 +124,8 @@ class Delete extends Action throw new Exception($this->getConflictException()); } catch (RestrictedException) { throw new Exception($this->getRestrictedException()); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } foreach ($documents as $document) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index baab45cdd3..65bd255d32 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -150,6 +150,8 @@ class Update extends Action throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); } catch (StructureException $e) { throw new Exception($this->getStructureException(), $e->getMessage()); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } foreach ($documents as $document) { From 3e8211d6bd199cd731b0a637b6b5a9fe9beb000b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 20 Sep 2025 19:45:44 +0530 Subject: [PATCH 147/274] refactor: improve code readability for schedules --- app/controllers/api/messaging.php | 13 +++++++------ src/Appwrite/Migration/Version/V19.php | 5 +++-- .../Modules/Functions/Http/Functions/Create.php | 3 ++- src/Appwrite/Platform/Tasks/ScheduleExecutions.php | 6 ++++++ src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 7 ++++++- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index d22c5cb2c2..bde93305dd 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -10,6 +10,7 @@ use Appwrite\Extend\Exception; use Appwrite\Messaging\Status as MessageStatus; use Appwrite\Network\Validator\Email; use Appwrite\Permission; +use Appwrite\Platform\Tasks\ScheduleMessages; use Appwrite\Role; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -3102,7 +3103,7 @@ App::post('/v1/messaging/messages/email') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'message', + 'resourceType' => ScheduleMessages::getSupportedResource(), 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3244,7 +3245,7 @@ App::post('/v1/messaging/messages/sms') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'message', + 'resourceType' => ScheduleMessages::getSupportedResource(), 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3462,7 +3463,7 @@ App::post('/v1/messaging/messages/push') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'message', + 'resourceType' => ScheduleMessages::getSupportedResource(), 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3863,7 +3864,7 @@ App::patch('/v1/messaging/messages/email/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'message', + 'resourceType' => ScheduleMessages::getSupportedResource(), 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -4084,7 +4085,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'message', + 'resourceType' => ScheduleMessages::getSupportedResource(), 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -4258,7 +4259,7 @@ App::patch('/v1/messaging/messages/push/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'message', + 'resourceType' => ScheduleMessages::getSupportedResource(), 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/src/Appwrite/Migration/Version/V19.php b/src/Appwrite/Migration/Version/V19.php index d4dda02d75..790a2fdfb7 100644 --- a/src/Appwrite/Migration/Version/V19.php +++ b/src/Appwrite/Migration/Version/V19.php @@ -3,6 +3,7 @@ namespace Appwrite\Migration\Version; use Appwrite\Migration\Migration; +use Appwrite\Platform\Tasks\ScheduleFunctions; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -730,8 +731,8 @@ class V19 extends Migration if (empty($document->getAttribute('scheduleId', null))) { $schedule = $this->dbForPlatform->createDocument('schedules', new Document([ - 'region' => $project->getAttribute('region'), - 'resourceType' => 'function', + 'region' => $this->project->getAttribute('region'), + 'resourceType' => ScheduleFunctions::getSupportedResource(), 'resourceId' => $document->getId(), 'resourceInternalId' => $document->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 21a74f9a81..ccf5abc373 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -11,6 +11,7 @@ use Appwrite\Event\Webhook; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; use Appwrite\Platform\Modules\Compute\Validator\Specification; +use Appwrite\Platform\Tasks\ScheduleFunctions; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -235,7 +236,7 @@ class Create extends Base $schedule = Authorization::skip( fn () => $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => 'function', + 'resourceType' => ScheduleFunctions::getSupportedResource(), 'resourceId' => $function->getId(), 'resourceInternalId' => $function->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 14a4259e17..438341ad47 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -6,6 +6,12 @@ use Appwrite\Event\Func; use Swoole\Coroutine as Co; use Utopia\Database\Database; +/** + * ScheduleExecutions + * + * Handles delayed executions by processing one-time scheduled tasks + * that are executed at a specific future time. + */ class ScheduleExecutions extends ScheduleBase { public const UPDATE_TIMER = 3; // seconds diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 6f072425e4..090adcbccf 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -7,8 +7,13 @@ use Cron\CronExpression; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; -use Utopia\Pools\Group; +/** + * ScheduleFunctions + * + * Handles cron job related executions by processing cron expressions + * and scheduling function executions based on recurring schedules. + */ class ScheduleFunctions extends ScheduleBase { public const UPDATE_TIMER = 10; // seconds From 2f1046db2b1f63b1fd7bd7989d1b20ec74e86d10 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 22 Sep 2025 12:10:55 +0530 Subject: [PATCH 148/274] fix tests --- app/init/resources.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 211bc52a8e..16307edee3 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -219,14 +219,6 @@ App::setResource('platforms', function (Request $request, Document $console, Doc } } - // Unsafe; Localhost is always safe for ease of local development - $project->setAttribute('platforms', [ - '$collection' => ID::custom('platforms'), - 'type' => Platform::TYPE_WEB, - 'name' => "localhost", - 'hostname' => "localhost", - ], Document::SET_TYPE_APPEND); - return [ ...$console->getAttribute('platforms', []), ...$project->getAttribute('platforms', []), From f09d5258ece14f4b3c9cf3ab9d0c82947269b47d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 22 Sep 2025 14:20:58 +0530 Subject: [PATCH 149/274] use constants --- app/init/constants.php | 10 ++++++++++ src/Appwrite/Platform/Tasks/ScheduleExecutions.php | 4 ++-- src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 4 ++-- src/Appwrite/Platform/Tasks/ScheduleMessages.php | 4 ++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index 28cf8a4052..2c94041276 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -279,3 +279,13 @@ const TOKENS_RESOURCE_TYPE_FILES = 'files'; const TOKENS_RESOURCE_TYPE_SITES = 'sites'; const TOKENS_RESOURCE_TYPE_FUNCTIONS = 'functions'; const TOKENS_RESOURCE_TYPE_DATABASES = 'databases'; + +// schedules types +const SCHEDULE_TYPE_EXECUTION = 'execution'; +const SCHEDULE_TYPE_FUNCTION = 'function'; +const SCHEDULE_TYPE_MESSAGE = 'message'; + +// collections types +const COLLECTION_TYPE_EXECUTIONS = 'executions'; +const COLLECTION_TYPE_FUNCTIONS = 'functions'; +const COLLECTION_TYPE_MESSAGES = 'messages'; diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 438341ad47..77cc056280 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -24,12 +24,12 @@ class ScheduleExecutions extends ScheduleBase public static function getSupportedResource(): string { - return 'execution'; + return SCHEDULE_TYPE_EXECUTION; } public static function getCollectionId(): string { - return 'executions'; + return COLLECTION_TYPE_EXECUTIONS; } protected function enqueueResources(Database $dbForPlatform, callable $getProjectDB): void diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 090adcbccf..09c7296f93 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -28,12 +28,12 @@ class ScheduleFunctions extends ScheduleBase public static function getSupportedResource(): string { - return 'function'; + return SCHEDULE_TYPE_FUNCTION; } public static function getCollectionId(): string { - return 'functions'; + return COLLECTION_TYPE_FUNCTIONS; } protected function enqueueResources(Database $dbForPlatform, callable $getProjectDB): void diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index fe4afbe69c..87e5ba5730 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -17,12 +17,12 @@ class ScheduleMessages extends ScheduleBase public static function getSupportedResource(): string { - return 'message'; + return SCHEDULE_TYPE_MESSAGE; } public static function getCollectionId(): string { - return 'messages'; + return COLLECTION_TYPE_MESSAGES; } protected function enqueueResources(Database $dbForPlatform, callable $getProjectDB): void From 0228c74f71569c9f6013116490b00a5bf9b30563 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 22 Sep 2025 14:26:23 +0530 Subject: [PATCH 150/274] Throw error when email is not available for account verification --- app/controllers/api/account.php | 4 ++++ .../Account/AccountCustomClientTest.php | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 8aaa5283c4..09f5036188 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -3544,6 +3544,10 @@ App::post('/v1/account/verification') throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } + if (empty($user->getAttribute('email'))) { + throw new Exception(Exception::USER_EMAIL_NOT_FOUND); + } + $url = htmlentities($url); if ($user->getAttribute('emailVerification')) { throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED); diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index bd3fec8439..5cec3770f7 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -1850,6 +1850,26 @@ class AccountCustomClientTest extends Scope return $session; } + /** + * @depends testCreateAnonymousAccount + */ + public function testCreateAnonymousAccountVerification($session): array + { + $response = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ]), [ + 'url' => 'http://localhost/verification', + ]); + + $this->assertEquals(500, $response['body']['code']); + $this->assertEquals('user_email_not_found', $response['body']['type']); + + return []; + } + /** * @depends testCreateAnonymousAccount */ From c52081dbed972c20686f343f7eb96d9d545dbb94 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 22 Sep 2025 19:58:18 +0530 Subject: [PATCH 151/274] fix: to use correct pattern --- app/init/constants.php | 15 ++++----------- .../Platform/Tasks/ScheduleExecutions.php | 4 ++-- src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 4 ++-- src/Appwrite/Platform/Tasks/ScheduleMessages.php | 4 ++-- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index 2c94041276..5c75dc4ce2 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -262,7 +262,6 @@ const METRIC_SITES_ID_INBOUND = 'sites.{siteInternalId}.inbound'; const METRIC_SITES_ID_OUTBOUND = 'sites.{siteInternalId}.outbound'; // Resource types - const RESOURCE_TYPE_PROJECTS = 'projects'; const RESOURCE_TYPE_FUNCTIONS = 'functions'; const RESOURCE_TYPE_SITES = 'sites'; @@ -274,18 +273,12 @@ const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers'; const RESOURCE_TYPE_MESSAGES = 'messages'; // Resource types for Tokens - const TOKENS_RESOURCE_TYPE_FILES = 'files'; const TOKENS_RESOURCE_TYPE_SITES = 'sites'; const TOKENS_RESOURCE_TYPE_FUNCTIONS = 'functions'; const TOKENS_RESOURCE_TYPE_DATABASES = 'databases'; -// schedules types -const SCHEDULE_TYPE_EXECUTION = 'execution'; -const SCHEDULE_TYPE_FUNCTION = 'function'; -const SCHEDULE_TYPE_MESSAGE = 'message'; - -// collections types -const COLLECTION_TYPE_EXECUTIONS = 'executions'; -const COLLECTION_TYPE_FUNCTIONS = 'functions'; -const COLLECTION_TYPE_MESSAGES = 'messages'; +// Resource types for Schedules +const SCHEDULE_RESOURCE_TYPE_EXECUTION = 'execution'; +const SCHEDULE_RESOURCE_TYPE_FUNCTION = 'function'; +const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message'; diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 77cc056280..83a3f51b03 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -24,12 +24,12 @@ class ScheduleExecutions extends ScheduleBase public static function getSupportedResource(): string { - return SCHEDULE_TYPE_EXECUTION; + return SCHEDULE_RESOURCE_TYPE_EXECUTION; } public static function getCollectionId(): string { - return COLLECTION_TYPE_EXECUTIONS; + return RESOURCE_TYPE_EXECUTIONS; } protected function enqueueResources(Database $dbForPlatform, callable $getProjectDB): void diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 09c7296f93..7fda2f75df 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -28,12 +28,12 @@ class ScheduleFunctions extends ScheduleBase public static function getSupportedResource(): string { - return SCHEDULE_TYPE_FUNCTION; + return SCHEDULE_RESOURCE_TYPE_FUNCTION; } public static function getCollectionId(): string { - return COLLECTION_TYPE_FUNCTIONS; + return RESOURCE_TYPE_FUNCTIONS; } protected function enqueueResources(Database $dbForPlatform, callable $getProjectDB): void diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 87e5ba5730..57f6dd8002 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -17,12 +17,12 @@ class ScheduleMessages extends ScheduleBase public static function getSupportedResource(): string { - return SCHEDULE_TYPE_MESSAGE; + return SCHEDULE_RESOURCE_TYPE_MESSAGE; } public static function getCollectionId(): string { - return COLLECTION_TYPE_MESSAGES; + return RESOURCE_TYPE_MESSAGES; } protected function enqueueResources(Database $dbForPlatform, callable $getProjectDB): void From 232b22b71eca4bc7b900b77b854dd539cfaf540d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 07:35:17 +0530 Subject: [PATCH 152/274] use constants --- app/controllers/api/messaging.php | 13 ++++++------- app/init/constants.php | 1 + src/Appwrite/Migration/Version/V19.php | 3 +-- .../Modules/Functions/Http/Executions/Create.php | 3 +-- .../Modules/Functions/Http/Executions/Delete.php | 3 +-- .../Modules/Functions/Http/Functions/Create.php | 3 +-- 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index bde93305dd..fd1a92e364 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -10,7 +10,6 @@ use Appwrite\Extend\Exception; use Appwrite\Messaging\Status as MessageStatus; use Appwrite\Network\Validator\Email; use Appwrite\Permission; -use Appwrite\Platform\Tasks\ScheduleMessages; use Appwrite\Role; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -3103,7 +3102,7 @@ App::post('/v1/messaging/messages/email') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleMessages::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_MESSAGES, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3245,7 +3244,7 @@ App::post('/v1/messaging/messages/sms') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleMessages::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_MESSAGES, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3463,7 +3462,7 @@ App::post('/v1/messaging/messages/push') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleMessages::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_MESSAGES, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3864,7 +3863,7 @@ App::patch('/v1/messaging/messages/email/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleMessages::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_MESSAGES, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -4085,7 +4084,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleMessages::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_MESSAGES, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -4259,7 +4258,7 @@ App::patch('/v1/messaging/messages/push/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleMessages::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_MESSAGES, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/app/init/constants.php b/app/init/constants.php index 5c75dc4ce2..74f04f25e9 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -271,6 +271,7 @@ const RESOURCE_TYPE_PROVIDERS = 'providers'; const RESOURCE_TYPE_TOPICS = 'topics'; const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers'; const RESOURCE_TYPE_MESSAGES = 'messages'; +const RESOURCE_TYPE_EXECUTIONS = 'executions'; // Resource types for Tokens const TOKENS_RESOURCE_TYPE_FILES = 'files'; diff --git a/src/Appwrite/Migration/Version/V19.php b/src/Appwrite/Migration/Version/V19.php index 790a2fdfb7..f5892f07bd 100644 --- a/src/Appwrite/Migration/Version/V19.php +++ b/src/Appwrite/Migration/Version/V19.php @@ -3,7 +3,6 @@ namespace Appwrite\Migration\Version; use Appwrite\Migration\Migration; -use Appwrite\Platform\Tasks\ScheduleFunctions; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -732,7 +731,7 @@ class V19 extends Migration if (empty($document->getAttribute('scheduleId', null))) { $schedule = $this->dbForPlatform->createDocument('schedules', new Document([ 'region' => $this->project->getAttribute('region'), - 'resourceType' => ScheduleFunctions::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_FUNCTIONS, 'resourceId' => $document->getId(), 'resourceInternalId' => $document->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 1603e8f997..69af3b7d04 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -11,7 +11,6 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Functions\Validator\Headers; use Appwrite\Platform\Modules\Compute\Base; -use Appwrite\Platform\Tasks\ScheduleExecutions; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -313,7 +312,7 @@ class Create extends Base $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleExecutions::getSupportedResource(), + 'resourceType' => SCHEDULE_RESOURCE_TYPE_EXECUTION, 'resourceId' => $execution->getId(), 'resourceInternalId' => $execution->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php index 9c818cfacc..666cb8310c 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Functions\Http\Executions; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; -use Appwrite\Platform\Tasks\ScheduleExecutions; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -100,7 +99,7 @@ class Delete extends Base if ($status === 'scheduled') { $schedule = $dbForPlatform->findOne('schedules', [ Query::equal('resourceId', [$execution->getId()]), - Query::equal('resourceType', [ScheduleExecutions::getSupportedResource()]), + Query::equal('resourceType', [SCHEDULE_RESOURCE_TYPE_EXECUTION]), Query::equal('active', [true]), ]); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index ccf5abc373..13444634fb 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -11,7 +11,6 @@ use Appwrite\Event\Webhook; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; use Appwrite\Platform\Modules\Compute\Validator\Specification; -use Appwrite\Platform\Tasks\ScheduleFunctions; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -236,7 +235,7 @@ class Create extends Base $schedule = Authorization::skip( fn () => $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => ScheduleFunctions::getSupportedResource(), + 'resourceType' => RESOURCE_TYPE_FUNCTIONS, 'resourceId' => $function->getId(), 'resourceInternalId' => $function->getSequence(), 'resourceUpdatedAt' => DateTime::now(), From 734d2ce51240845abafe0e536f538a81a6ec164b Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 07:49:07 +0530 Subject: [PATCH 153/274] fix: use correct constant --- app/controllers/api/messaging.php | 12 ++++++------ src/Appwrite/Migration/Version/V19.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index fd1a92e364..dbc384f3c4 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -3102,7 +3102,7 @@ App::post('/v1/messaging/messages/email') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_MESSAGES, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3244,7 +3244,7 @@ App::post('/v1/messaging/messages/sms') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_MESSAGES, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3462,7 +3462,7 @@ App::post('/v1/messaging/messages/push') case MessageStatus::SCHEDULED: $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_MESSAGES, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -3863,7 +3863,7 @@ App::patch('/v1/messaging/messages/email/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_MESSAGES, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -4084,7 +4084,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_MESSAGES, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), @@ -4258,7 +4258,7 @@ App::patch('/v1/messaging/messages/push/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_MESSAGES, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_MESSAGE, 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), diff --git a/src/Appwrite/Migration/Version/V19.php b/src/Appwrite/Migration/Version/V19.php index f5892f07bd..f5cf84c95e 100644 --- a/src/Appwrite/Migration/Version/V19.php +++ b/src/Appwrite/Migration/Version/V19.php @@ -731,7 +731,7 @@ class V19 extends Migration if (empty($document->getAttribute('scheduleId', null))) { $schedule = $this->dbForPlatform->createDocument('schedules', new Document([ 'region' => $this->project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_FUNCTIONS, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_FUNCTION, 'resourceId' => $document->getId(), 'resourceInternalId' => $document->getSequence(), 'resourceUpdatedAt' => DateTime::now(), From 421ec6f3f8c72eb528aecacae39be1556e16c763 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 07:49:55 +0530 Subject: [PATCH 154/274] fix: use correct constant --- .../Platform/Modules/Functions/Http/Functions/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 13444634fb..b00a2ad2bf 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -235,7 +235,7 @@ class Create extends Base $schedule = Authorization::skip( fn () => $dbForPlatform->createDocument('schedules', new Document([ 'region' => $project->getAttribute('region'), - 'resourceType' => RESOURCE_TYPE_FUNCTIONS, + 'resourceType' => SCHEDULE_RESOURCE_TYPE_FUNCTION, 'resourceId' => $function->getId(), 'resourceInternalId' => $function->getSequence(), 'resourceUpdatedAt' => DateTime::now(), From 84a4ef0bafe46163b5807db0781779e0eead6f00 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 10:47:39 +0530 Subject: [PATCH 155/274] chore: include response model enum names --- app/config/specs/open-api3-1.8.x-console.json | 39 +++++--- app/config/specs/open-api3-1.8.x-server.json | 39 +++++--- .../specs/open-api3-latest-console.json | 39 +++++--- app/config/specs/open-api3-latest-server.json | 39 +++++--- app/config/specs/swagger2-1.8.x-console.json | 39 +++++--- app/config/specs/swagger2-1.8.x-server.json | 39 +++++--- app/config/specs/swagger2-latest-console.json | 39 +++++--- app/config/specs/swagger2-latest-server.json | 39 +++++--- src/Appwrite/SDK/Specification/Format.php | 89 ++++++++++++++++++- .../SDK/Specification/Format/OpenAPI3.php | 14 ++- .../SDK/Specification/Format/Swagger2.php | 14 ++- 11 files changed, 319 insertions(+), 110 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 1dd58c3261..d75265f552 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -46468,7 +46468,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46563,7 +46564,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46660,7 +46662,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46757,7 +46760,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46837,7 +46841,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46924,7 +46929,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47021,7 +47027,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47108,7 +47115,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47195,7 +47203,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47282,7 +47291,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47397,7 +47407,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47483,7 +47494,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47581,7 +47593,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index b437d56bfc..3cb3f01ace 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -35338,7 +35338,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35433,7 +35434,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35530,7 +35532,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35627,7 +35630,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35707,7 +35711,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35794,7 +35799,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35891,7 +35897,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35978,7 +35985,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36065,7 +36073,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36152,7 +36161,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36267,7 +36277,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36353,7 +36364,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36451,7 +36463,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 1dd58c3261..d75265f552 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -46468,7 +46468,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46563,7 +46564,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46660,7 +46662,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46757,7 +46760,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46837,7 +46841,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46924,7 +46929,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47021,7 +47027,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47108,7 +47115,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47195,7 +47203,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47282,7 +47291,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47397,7 +47407,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47483,7 +47494,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47581,7 +47593,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index b437d56bfc..3cb3f01ace 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -35338,7 +35338,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35433,7 +35434,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35530,7 +35532,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35627,7 +35630,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35707,7 +35711,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35794,7 +35799,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35891,7 +35897,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35978,7 +35985,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36065,7 +36073,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36152,7 +36161,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36267,7 +36277,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36353,7 +36364,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36451,7 +36463,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 0f91a5433d..072da6a403 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -46405,7 +46405,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46500,7 +46501,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46597,7 +46599,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46694,7 +46697,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46774,7 +46778,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46861,7 +46866,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46958,7 +46964,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47045,7 +47052,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47132,7 +47140,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47219,7 +47228,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47334,7 +47344,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47420,7 +47431,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47518,7 +47530,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 679da75e5d..3e997b2341 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -35366,7 +35366,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35461,7 +35462,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35558,7 +35560,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35655,7 +35658,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35735,7 +35739,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35822,7 +35827,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35919,7 +35925,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36006,7 +36013,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36093,7 +36101,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36180,7 +36189,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36295,7 +36305,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36381,7 +36392,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36479,7 +36491,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 0f91a5433d..072da6a403 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -46405,7 +46405,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46500,7 +46501,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46597,7 +46599,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46694,7 +46697,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46774,7 +46778,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46861,7 +46866,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -46958,7 +46964,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47045,7 +47052,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47132,7 +47140,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47219,7 +47228,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47334,7 +47344,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47420,7 +47431,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -47518,7 +47530,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 679da75e5d..3e997b2341 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -35366,7 +35366,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35461,7 +35462,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35558,7 +35560,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35655,7 +35658,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35735,7 +35739,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35822,7 +35827,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -35919,7 +35925,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36006,7 +36013,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36093,7 +36101,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36180,7 +36189,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36295,7 +36305,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36381,7 +36392,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", @@ -36479,7 +36491,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "AttributeStatus" }, "error": { "type": "string", diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 825f9bf01d..d52f36c40c 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -112,7 +112,7 @@ abstract class Format return $this->params[$key] ?? $default; } - protected function getEnumName(string $service, string $method, string $param): ?string + protected function getRequestEnumName(string $service, string $method, string $param): ?string { /* `$service` is `$namespace` */ switch ($service) { @@ -450,7 +450,7 @@ abstract class Format return null; } - public function getEnumKeys(string $service, string $method, string $param): array + public function getRequestEnumKeys(string $service, string $method, string $param): array { $values = []; switch ($service) { @@ -543,6 +543,91 @@ abstract class Format return $values; } + public function getResponseEnumName(string $model, string $param): ?string + { + switch ($model) { + case 'attributeString': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeInteger': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeFloat': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeBoolean': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeEmail': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeEnum': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeIp': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeUrl': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeDatetime': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeRelationship': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributePoint': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributeLine': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + case 'attributePolygon': + switch ($param) { + case 'status': + return 'AttributeStatus'; + } + break; + } + return null; + } + protected function getNestedModels(Model $model, array &$usedModels): void { foreach ($model->getRules() as $rule) { diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index c11e55e733..2380f03920 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Response; use Appwrite\SDK\Specification\Format; use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Response\Model\Any; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -559,8 +560,8 @@ class OpenAPI3 extends Format if ($allowed) { $node['schema']['enum'] = $validator->getList(); - $node['schema']['x-enum-name'] = $this->getEnumName($sdk->getNamespace() ?? '', $methodName, $name); - $node['schema']['x-enum-keys'] = $this->getEnumKeys($sdk->getNamespace() ?? '', $methodName, $name); + $node['schema']['x-enum-name'] = $this->getRequestEnumName($sdk->getNamespace() ?? '', $methodName, $name); + $node['schema']['x-enum-keys'] = $this->getRequestEnumKeys($sdk->getNamespace() ?? '', $methodName, $name); } if ($validator->getType() === 'integer') { $node['format'] = 'int32'; @@ -777,8 +778,16 @@ class OpenAPI3 extends Format if ($rule['type'] === 'enum' && !empty($rule['enum'])) { if ($rule['array']) { $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['enum'] = $rule['enum']; + $enumName = $this->getResponseEnumName($model->getType(), $name); + if ($enumName) { + $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['x-enum-name'] = $enumName; + } } else { $output['components']['schemas'][$model->getType()]['properties'][$name]['enum'] = $rule['enum']; + $enumName = $this->getResponseEnumName($model->getType(), $name); + if ($enumName) { + $output['components']['schemas'][$model->getType()]['properties'][$name]['x-enum-name'] = $enumName; + } } } if (!in_array($name, $required)) { @@ -786,6 +795,7 @@ class OpenAPI3 extends Format } } + /** @var Any $model */ if ($model->isAny() && !empty($model->getSampleData())) { $examples = array_merge($examples, $model->getSampleData()); } diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 4e784e8116..ed1217d86c 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Response; use Appwrite\SDK\Specification\Format; use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Response\Model\Any; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -565,8 +566,8 @@ class Swagger2 extends Format if ($allowed && $validator->getType() === 'string') { $node['enum'] = $validator->getList(); - $node['x-enum-name'] = $this->getEnumName($namespace, $methodName, $name); - $node['x-enum-keys'] = $this->getEnumKeys($namespace, $methodName, $name); + $node['x-enum-name'] = $this->getRequestEnumName($namespace, $methodName, $name); + $node['x-enum-keys'] = $this->getRequestEnumKeys($namespace, $methodName, $name); } if ($validator->getType() === 'integer') { @@ -799,8 +800,16 @@ class Swagger2 extends Format if ($rule['type'] === 'enum' && !empty($rule['enum'])) { if ($rule['array']) { $output['definitions'][$model->getType()]['properties'][$name]['items']['enum'] = $rule['enum']; + $enumName = $this->getResponseEnumName($model->getType(), $name); + if ($enumName) { + $output['definitions'][$model->getType()]['properties'][$name]['items']['x-enum-name'] = $enumName; + } } else { $output['definitions'][$model->getType()]['properties'][$name]['enum'] = $rule['enum']; + $enumName = $this->getResponseEnumName($model->getType(), $name); + if ($enumName) { + $output['definitions'][$model->getType()]['properties'][$name]['x-enum-name'] = $enumName; + } } } if (!in_array($name, $required)) { @@ -808,6 +817,7 @@ class Swagger2 extends Format } } + /** @var Any $model */ if ($model->isAny() && !empty($model->getSampleData())) { $examples = array_merge($examples, $model->getSampleData()); } From 17f1cb214fbea4784a316488224d6929a0bcc54d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 10:57:35 +0530 Subject: [PATCH 156/274] override healthstatus --- app/config/specs/open-api3-1.8.x-console.json | 3 ++- app/config/specs/open-api3-1.8.x-server.json | 3 ++- app/config/specs/open-api3-latest-console.json | 3 ++- app/config/specs/open-api3-latest-server.json | 3 ++- app/config/specs/swagger2-1.8.x-console.json | 3 ++- app/config/specs/swagger2-1.8.x-server.json | 3 ++- app/config/specs/swagger2-latest-console.json | 3 ++- app/config/specs/swagger2-latest-server.json | 3 ++- src/Appwrite/SDK/Specification/Format.php | 6 ++++++ 9 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index d75265f552..86b5f3ba6f 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -53809,7 +53809,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 3cb3f01ace..b9270ad857 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -40997,7 +40997,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index d75265f552..86b5f3ba6f 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -53809,7 +53809,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 3cb3f01ace..b9270ad857 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -40997,7 +40997,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 072da6a403..f682fc8670 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -53765,7 +53765,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 3e997b2341..bade0bae90 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -41034,7 +41034,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 072da6a403..f682fc8670 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -53765,7 +53765,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 3e997b2341..bade0bae90 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -41034,7 +41034,8 @@ "enum": [ "pass", "fail" - ] + ], + "x-enum-name": "HealthStatus" } }, "required": [ diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index d52f36c40c..7db5d5f559 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -624,6 +624,12 @@ abstract class Format return 'AttributeStatus'; } break; + case 'healthStatus': + switch ($param) { + case 'status': + return 'HealthStatus'; + } + break; } return null; } From 5d8ac0a5b0628b7b3b9b6b3eca4205eaf5420b91 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 11:31:29 +0530 Subject: [PATCH 157/274] add message status --- app/config/specs/open-api3-1.8.x-console.json | 9 ++++++++- app/config/specs/open-api3-1.8.x-server.json | 9 ++++++++- app/config/specs/open-api3-latest-console.json | 9 ++++++++- app/config/specs/open-api3-latest-server.json | 9 ++++++++- app/config/specs/swagger2-1.8.x-console.json | 9 ++++++++- app/config/specs/swagger2-1.8.x-server.json | 9 ++++++++- app/config/specs/swagger2-latest-console.json | 9 ++++++++- app/config/specs/swagger2-latest-server.json | 9 ++++++++- src/Appwrite/Utopia/Response/Model/Message.php | 6 ++++-- 9 files changed, 68 insertions(+), 10 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 86b5f3ba6f..31471b170c 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -56549,7 +56549,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index b9270ad857..259485c793 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -41463,7 +41463,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 86b5f3ba6f..31471b170c 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -56549,7 +56549,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index b9270ad857..259485c793 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -41463,7 +41463,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index f682fc8670..1abb7c4ef8 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -56598,7 +56598,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index bade0bae90..f350d10c54 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -41502,7 +41502,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index f682fc8670..1abb7c4ef8 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -56598,7 +56598,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index bade0bae90..f350d10c54 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -41502,7 +41502,14 @@ "status": { "type": "string", "description": "Status of delivery.", - "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed." + "x-example": "Message status can be one of the following: draft, processing, scheduled, sent, or failed.", + "enum": [ + "draft", + "processing", + "scheduled", + "sent", + "failed" + ] } }, "required": [ diff --git a/src/Appwrite/Utopia/Response/Model/Message.php b/src/Appwrite/Utopia/Response/Model/Message.php index e52b6836c5..4c1e08b9cb 100644 --- a/src/Appwrite/Utopia/Response/Model/Message.php +++ b/src/Appwrite/Utopia/Response/Model/Message.php @@ -34,6 +34,7 @@ class Message extends Model 'description' => 'Message provider type.', 'default' => '', 'example' => MESSAGE_TYPE_EMAIL, + 'enum' => [MESSAGE_TYPE_EMAIL, MESSAGE_TYPE_SMS, MESSAGE_TYPE_PUSH], ]) ->addRule('topics', [ 'type' => self::TYPE_STRING, @@ -50,7 +51,7 @@ class Message extends Model 'example' => ['5e5ea5c16897e'], ]) ->addRule('targets', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Target IDs set as recipients.', 'default' => '', 'array' => true, @@ -94,10 +95,11 @@ class Message extends Model ], ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Status of delivery.', 'default' => 'draft', 'example' => 'Message status can be one of the following: draft, processing, scheduled, sent, or failed.', + 'enum' => ['draft', 'processing', 'scheduled', 'sent', 'failed'], ]); } From 0995bd6a6edfd49e3138c1a2e172eace7c11de61 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Sep 2025 19:17:06 +1200 Subject: [PATCH 158/274] SDK releases --- app/config/platforms.php | 28 +++--- composer.lock | 94 +++++++++++-------- .../functions/create-duplicate-deployment.md | 2 +- .../functions/create-template-deployment.md | 2 +- .../functions/create-vcs-deployment.md | 2 +- .../functions/update-deployment-status.md | 2 +- .../sites/create-duplicate-deployment.md | 2 +- .../sites/create-template-deployment.md | 2 +- .../examples/sites/create-vcs-deployment.md | 2 +- .../sites/update-deployment-status.md | 2 +- docs/sdks/dart/CHANGELOG.md | 4 + docs/sdks/flutter/CHANGELOG.md | 4 + 12 files changed, 83 insertions(+), 63 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 7aec82d1cf..8a33144b2f 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '20.0.0', + 'version' => '20.1.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '19.0.0', + 'version' => '19.1.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '12.0.0', + 'version' => '12.1.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '10.0.0', + 'version' => '10.1.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -139,7 +139,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.13.0', + 'version' => '0.14.0', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '19.0.0', + 'version' => '19.1.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -281,7 +281,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '17.0.0', + 'version' => '17.1.0', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -300,7 +300,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '13.0.0', + 'version' => '13.1.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '18.0.0', + 'version' => '18.1.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => '0.11.0', + 'version' => '0.12.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, @@ -357,7 +357,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.17.0', + 'version' => '0.18.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '18.0.0', + 'version' => '18.1.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '11.0.0', + 'version' => '11.1.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '12.0.0', + 'version' => '12.1.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/composer.lock b/composer.lock index 98089f9ed9..b9475de5b9 100644 --- a/composer.lock +++ b/composer.lock @@ -1159,20 +1159,20 @@ }, { "name": "open-telemetry/api", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "7692075f486c14d8cfd37fba98a08a5667f089e5" + "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/7692075f486c14d8cfd37fba98a08a5667f089e5", - "reference": "7692075f486c14d8cfd37fba98a08a5667f089e5", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/ee17d937652eca06c2341b6fadc0f74c1c1a5af2", + "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2", "shasum": "" }, "require": { - "open-telemetry/context": "^1.0", + "open-telemetry/context": "^1.4", "php": "^8.1", "psr/log": "^1.1|^2.0|^3.0", "symfony/polyfill-php82": "^1.26" @@ -1225,20 +1225,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-07T23:07:38+00:00" + "time": "2025-09-19T00:05:49+00:00" }, { "name": "open-telemetry/context", - "version": "1.3.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "438f71812242db3f196fb4c717c6f92cbc819be6" + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6", - "reference": "438f71812242db3f196fb4c717c6f92cbc819be6", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/d4c4470b541ce72000d18c339cfee633e4c8e0cf", + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf", "shasum": "" }, "require": { @@ -1284,7 +1284,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-13T01:12:00+00:00" + "time": "2025-09-19T00:05:49+00:00" }, { "name": "open-telemetry/exporter-otlp", @@ -1415,23 +1415,23 @@ }, { "name": "open-telemetry/sdk", - "version": "1.7.1", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06" + "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/52690d4b37ae4f091af773eef3c238ed2bc0aa06", - "reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/105c6e81e3d86150bd5704b00c7e4e165e957b89", + "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.4", - "open-telemetry/context": "^1.0", + "open-telemetry/api": "^1.6", + "open-telemetry/context": "^1.4", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", "php-http/discovery": "^1.14", @@ -1508,7 +1508,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-05T07:17:06+00:00" + "time": "2025-09-19T00:05:49+00:00" }, { "name": "open-telemetry/sem-conv", @@ -1569,16 +1569,16 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105" + "reference": "ce27936c8dfb73e3ab9c94469130428af9752c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/ce27936c8dfb73e3ab9c94469130428af9752c96", + "reference": "ce27936c8dfb73e3ab9c94469130428af9752c96", "shasum": "" }, "require": { @@ -1632,7 +1632,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:18:48+00:00" + "time": "2025-09-22T20:41:46+00:00" }, { "name": "paragonie/random_compat", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.3.5", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "6fda9e58b37c9872c1a2a424e5467de8de1bc567" + "reference": "3583fa6fddb1d1a902b37ff2048527a5827fc008" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6fda9e58b37c9872c1a2a424e5467de8de1bc567", - "reference": "6fda9e58b37c9872c1a2a424e5467de8de1bc567", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/3583fa6fddb1d1a902b37ff2048527a5827fc008", + "reference": "3583fa6fddb1d1a902b37ff2048527a5827fc008", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.3.5" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.0" }, - "time": "2025-09-15T04:19:40+00:00" + "time": "2025-09-23T02:27:10+00:00" }, { "name": "doctrine/annotations", @@ -5278,16 +5278,16 @@ }, { "name": "laravel/pint", - "version": "v1.25.0", + "version": "v1.25.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "595de38458c6b0ab4cae4bcc769c2e5c5d5b8e96" + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/595de38458c6b0ab4cae4bcc769c2e5c5d5b8e96", - "reference": "595de38458c6b0ab4cae4bcc769c2e5c5d5b8e96", + "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", "shasum": "" }, "require": { @@ -5340,7 +5340,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-09-17T01:36:44+00:00" + "time": "2025-09-19T02:57:12+00:00" }, { "name": "matthiasmullie/minify", @@ -6829,16 +6829,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "eb49b981ef0817890129cb70f774506bebe57740" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/eb49b981ef0817890129cb70f774506bebe57740", + "reference": "eb49b981ef0817890129cb70f774506bebe57740", "shasum": "" }, "require": { @@ -6894,15 +6894,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.7" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-22T05:18:21+00:00" }, { "name": "sebastian/global-state", @@ -8506,7 +8518,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docs/examples/1.8.x/server-graphql/examples/functions/create-duplicate-deployment.md b/docs/examples/1.8.x/server-graphql/examples/functions/create-duplicate-deployment.md index cdd92c2a02..bc3587fcec 100644 --- a/docs/examples/1.8.x/server-graphql/examples/functions/create-duplicate-deployment.md +++ b/docs/examples/1.8.x/server-graphql/examples/functions/create-duplicate-deployment.md @@ -24,12 +24,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-graphql/examples/functions/create-template-deployment.md index 12c50c32f3..0ce968e5f4 100644 --- a/docs/examples/1.8.x/server-graphql/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-graphql/examples/functions/create-template-deployment.md @@ -27,12 +27,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-graphql/examples/functions/create-vcs-deployment.md index ebfced2c68..60a78c41ca 100644 --- a/docs/examples/1.8.x/server-graphql/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-graphql/examples/functions/create-vcs-deployment.md @@ -25,12 +25,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/functions/update-deployment-status.md b/docs/examples/1.8.x/server-graphql/examples/functions/update-deployment-status.md index 50df97fd21..68735b35ca 100644 --- a/docs/examples/1.8.x/server-graphql/examples/functions/update-deployment-status.md +++ b/docs/examples/1.8.x/server-graphql/examples/functions/update-deployment-status.md @@ -23,12 +23,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/sites/create-duplicate-deployment.md b/docs/examples/1.8.x/server-graphql/examples/sites/create-duplicate-deployment.md index 6226282651..1b2d3dc131 100644 --- a/docs/examples/1.8.x/server-graphql/examples/sites/create-duplicate-deployment.md +++ b/docs/examples/1.8.x/server-graphql/examples/sites/create-duplicate-deployment.md @@ -23,12 +23,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-graphql/examples/sites/create-template-deployment.md index 72562556e4..f63d8c5e5a 100644 --- a/docs/examples/1.8.x/server-graphql/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-graphql/examples/sites/create-template-deployment.md @@ -27,12 +27,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-graphql/examples/sites/create-vcs-deployment.md index ccc18cf2e0..6c5241e734 100644 --- a/docs/examples/1.8.x/server-graphql/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-graphql/examples/sites/create-vcs-deployment.md @@ -25,12 +25,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/examples/1.8.x/server-graphql/examples/sites/update-deployment-status.md b/docs/examples/1.8.x/server-graphql/examples/sites/update-deployment-status.md index 92751c167f..24064428e2 100644 --- a/docs/examples/1.8.x/server-graphql/examples/sites/update-deployment-status.md +++ b/docs/examples/1.8.x/server-graphql/examples/sites/update-deployment-status.md @@ -23,12 +23,12 @@ mutation { providerRepositoryName providerRepositoryOwner providerRepositoryUrl - providerBranch providerCommitHash providerCommitAuthorUrl providerCommitAuthor providerCommitMessage providerCommitUrl + providerBranch providerBranchUrl } } diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 7033bbdd1d..7e33794153 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 18.1.0 + +* Add `orderRandom` query support + ## 18.0.0 * Rename `CreditCard` enum value `unionChinaPay` to `unionPay` diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 7ff4a445b3..f704415675 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 19.1.0 + +* Add `orderRandom` query support + ## 19.0.0 * Rename `CreditCard` enum value `unionChinaPay` to `unionPay` From 9ede8cfb91bb59b748d87b94fa5e30525192a678 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 16:52:35 +0530 Subject: [PATCH 159/274] fix: healthstatus enum override due to conflict --- app/config/specs/open-api3-1.8.x-console.json | 2 +- app/config/specs/open-api3-1.8.x-server.json | 2 +- app/config/specs/open-api3-latest-console.json | 2 +- app/config/specs/open-api3-latest-server.json | 2 +- app/config/specs/swagger2-1.8.x-console.json | 2 +- app/config/specs/swagger2-1.8.x-server.json | 2 +- app/config/specs/swagger2-latest-console.json | 2 +- app/config/specs/swagger2-latest-server.json | 2 +- src/Appwrite/SDK/Specification/Format.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 31471b170c..ab36ae475c 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -53810,7 +53810,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 259485c793..93a0d9d46b 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -40998,7 +40998,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 31471b170c..ab36ae475c 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -53810,7 +53810,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 259485c793..93a0d9d46b 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -40998,7 +40998,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 1abb7c4ef8..e830f768be 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -53766,7 +53766,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index f350d10c54..0a5fc8fe95 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -41035,7 +41035,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 1abb7c4ef8..e830f768be 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -53766,7 +53766,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index f350d10c54..0a5fc8fe95 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -41035,7 +41035,7 @@ "pass", "fail" ], - "x-enum-name": "HealthStatus" + "x-enum-name": "HealthCheckStatus" } }, "required": [ diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 7db5d5f559..db7335e40f 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -627,7 +627,7 @@ abstract class Format case 'healthStatus': switch ($param) { case 'status': - return 'HealthStatus'; + return 'HealthCheckStatus'; } break; } From 7ea01733ef3d565198df0f012eb1ac88523e764e Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 23 Sep 2025 18:28:29 +0530 Subject: [PATCH 160/274] chore: update afterbuild fn --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 9547a752ef..72736a2c9b 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -899,7 +899,7 @@ class Builds extends Action Console::log('Build details stored'); - $this->afterBuildSuccess($queueForRealtime, $dbForProject, $deployment); + $this->afterBuildSuccess($queueForRealtime, $dbForProject, $deployment, $runtime); $logs = $deployment->getAttribute('buildLogs', ''); /** Screenshot site */ @@ -1392,11 +1392,12 @@ class Builds extends Action * @param Document $deployment * @return void */ - protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment): void + protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime): void { assert($queueForRealtime instanceof Realtime); assert($dbForProject instanceof Database); assert($deployment instanceof Document); + assert(is_array($runtime)); } protected function getRuntime(Document $resource, string $version): array From 5749b4175356a217f740b584ae3741c2cf7b7cc3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 24 Sep 2025 10:00:55 +0530 Subject: [PATCH 161/274] updated to use if throw checks --- .../Modules/Functions/Workers/Builds.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 72736a2c9b..f2f3b132aa 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -1391,13 +1391,22 @@ class Builds extends Action * @param Database $dbForProject * @param Document $deployment * @return void + * @throws Exception */ protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime): void { - assert($queueForRealtime instanceof Realtime); - assert($dbForProject instanceof Database); - assert($deployment instanceof Document); - assert(is_array($runtime)); + if (!($queueForRealtime instanceof Realtime)) { + throw new Exception('queueForRealtime must be an instance of Realtime'); + } + if (!($dbForProject instanceof Database)) { + throw new Exception('dbForProject must be an instance of Database'); + } + if (!($deployment instanceof Document)) { + throw new Exception('deployment must be an instance of Document'); + } + if (!is_array($runtime)) { + throw new Exception('runtime must be an array'); + } } protected function getRuntime(Document $resource, string $version): array From 9d99f5cec254a531f57802e1e75d0ba3149cbc8f Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 24 Sep 2025 11:47:13 +0530 Subject: [PATCH 162/274] chore: update afterbuild pass adapter --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index f2f3b132aa..9dc70718a5 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -899,7 +899,7 @@ class Builds extends Action Console::log('Build details stored'); - $this->afterBuildSuccess($queueForRealtime, $dbForProject, $deployment, $runtime); + $this->afterBuildSuccess($queueForRealtime, $dbForProject, $deployment, $runtime, $adapter); $logs = $deployment->getAttribute('buildLogs', ''); /** Screenshot site */ @@ -1393,7 +1393,7 @@ class Builds extends Action * @return void * @throws Exception */ - protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime): void + protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime, string $adapter): void { if (!($queueForRealtime instanceof Realtime)) { throw new Exception('queueForRealtime must be an instance of Realtime'); @@ -1407,6 +1407,9 @@ class Builds extends Action if (!is_array($runtime)) { throw new Exception('runtime must be an array'); } + if (!is_string($adapter)) { + throw new Exception('adapter must be a string'); + } } protected function getRuntime(Document $resource, string $version): array From 85da691aecfb9434cd1062b5cd47536845ffc79d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 24 Sep 2025 13:05:37 +0530 Subject: [PATCH 163/274] allow null adapter --- .../Platform/Modules/Functions/Workers/Builds.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 9dc70718a5..ce45d6b629 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -1390,10 +1390,12 @@ class Builds extends Action * @param Realtime $queueForRealtime * @param Database $dbForProject * @param Document $deployment + * @param array $runtime + * @param string|null $adapter * @return void * @throws Exception */ - protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime, string $adapter): void + protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment, array $runtime, ?string $adapter): void { if (!($queueForRealtime instanceof Realtime)) { throw new Exception('queueForRealtime must be an instance of Realtime'); @@ -1407,8 +1409,8 @@ class Builds extends Action if (!is_array($runtime)) { throw new Exception('runtime must be an array'); } - if (!is_string($adapter)) { - throw new Exception('adapter must be a string'); + if (!is_string($adapter) && !is_null($adapter)) { + throw new Exception('adapter must be a string or null'); } } From 752368327f63694945de30f2661e1c041f24cb03 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Wed, 24 Sep 2025 13:06:20 +0530 Subject: [PATCH 164/274] feedback --- .../locale/templates/email-mfa-challenge.tpl | 2 +- app/config/locale/templates/email-otp.tpl | 2 +- app/controllers/api/account.php | 63 ++++++++++++------- tests/e2e/Services/Account/AccountBase.php | 2 + 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/app/config/locale/templates/email-mfa-challenge.tpl b/app/config/locale/templates/email-mfa-challenge.tpl index fdc0f4d498..a828e3d299 100644 --- a/app/config/locale/templates/email-mfa-challenge.tpl +++ b/app/config/locale/templates/email-mfa-challenge.tpl @@ -5,7 +5,7 @@
-
{{otp}}
+

{{otp}}

diff --git a/app/config/locale/templates/email-otp.tpl b/app/config/locale/templates/email-otp.tpl index aebc512e1b..e18a4ce725 100644 --- a/app/config/locale/templates/email-otp.tpl +++ b/app/config/locale/templates/email-otp.tpl @@ -5,7 +5,7 @@
-
{{otp}}
+

{{otp}}

diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index bde3672774..3aa9678a72 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2400,16 +2400,21 @@ App::post('/v1/account/tokens/email') 'phrase' => !empty($phrase) ? $phrase : '', // TODO: remove unnecessary team variable from this email 'team' => '', - 'heading' => $heading, - 'accentColor' => APP_EMAIL_ACCENT_COLOR, - 'logoUrl' => APP_EMAIL_LOGO_URL, - 'twitterUrl' => APP_SOCIAL_TWITTER, - 'discordUrl' => APP_SOCIAL_DISCORD, - 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, - 'termsUrl' => APP_EMAIL_TERMS_URL, - 'privacyUrl' => APP_EMAIL_PRIVACY_URL, ]; + if ($customEmails && !empty($customTemplate)) { + $emailVariables = array_merge($emailVariables, [ + 'heading' => $heading, + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, + ]); + } + $queueForMails ->setSubject($subject) ->setPreview($preview) @@ -3706,16 +3711,21 @@ App::post('/v1/account/verification') 'project' => $projectName, // TODO: remove unnecessary team variable from this email 'team' => '', - 'heading' => $heading, - 'accentColor' => APP_EMAIL_ACCENT_COLOR, - 'logoUrl' => APP_EMAIL_LOGO_URL, - 'twitterUrl' => APP_SOCIAL_TWITTER, - 'discordUrl' => APP_SOCIAL_DISCORD, - 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, - 'termsUrl' => APP_EMAIL_TERMS_URL, - 'privacyUrl' => APP_EMAIL_PRIVACY_URL, ]; + if ($customEmails && !empty($customTemplate)) { + $emailVariables = array_merge($emailVariables, [ + 'heading' => $heading, + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, + ]); + } + $queueForMails ->setSubject($subject) ->setPreview($preview) @@ -4817,16 +4827,21 @@ App::post('/v1/account/mfa/challenge') 'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN', 'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN', 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN', - 'heading' => $heading, - 'accentColor' => APP_EMAIL_ACCENT_COLOR, - 'logoUrl' => APP_EMAIL_LOGO_URL, - 'twitterUrl' => APP_SOCIAL_TWITTER, - 'discordUrl' => APP_SOCIAL_DISCORD, - 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, - 'termsUrl' => APP_EMAIL_TERMS_URL, - 'privacyUrl' => APP_EMAIL_PRIVACY_URL, ]; + if ($customEmails && !empty($customTemplate)) { + $emailVariables = array_merge($emailVariables, [ + 'heading' => $heading, + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, + ]); + } + $queueForMails ->setSubject($subject) ->setPreview($preview) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 6cf997e22c..46283e8f86 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -188,6 +188,8 @@ trait AccountBase // Only Console project has branded logo in email. if ($isConsoleProject) { $this->assertStringContainsStringIgnoringCase('Appwrite logo', $lastEmail['html']); + } else { + $this->assertStringNotContainsStringIgnoringCase('Appwrite logo', $lastEmail['html']); } // TODO: Remove this once OTP login is supported for Console. From c00eec948a32befb81968772342cbe55d3665309 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 24 Sep 2025 18:01:43 +0530 Subject: [PATCH 165/274] remove: unnecessary changes. --- .github/labeler.yml | 83 -------------------------- .github/workflows/auto-label-issue.yml | 22 ------- README.md | 2 +- 3 files changed, 1 insertion(+), 106 deletions(-) delete mode 100644 .github/labeler.yml delete mode 100644 .github/workflows/auto-label-issue.yml diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index fb46eb5ba1..0000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,83 +0,0 @@ -# Fixes and upgrades for the Appwrite Auth / Users / Teams services. -"product / auth": - - "(auth|session|login|logout|register|2fa|mfa|users|teams|memberships|invite|oauth|oauth2|sso|jwt)" - -# Fixes and upgrades for the Appwrite Realtime API. -"api / realtime": - - "(realtime|subscribe|websockets)" - -# Console, UI and UX issues -"product / console": - - "(console)" - -# Fixes and upgrades for the Appwrite Storage. -"product / storage": - - "(storage|bucket|file|image|preview|download)" - -# Fixes and upgrades for the Appwrite Database. -"product / databases": - - "(database|collection|tables|attribute|column|document|row|query|queries|indexes|search|filter|sort|pagination)" - -# Fixes and upgrades for the Appwrite Functions. -"product / functions": - - "(function|runtime|deployment|execution|trigger|cron|schedule)" - -# Fixes and upgrades for the Appwrite Docs. -# "product / docs": -# - - -# Fixes and upgrades for the Appwrite Migrations. -"product / migrations": - - "(migrate|migration)" - -# Fixes and upgrades for the Appwrite Messaging. -"product / messaging": - - "(messaging|email|sms|push|provider|topic|target|notification)" - -# Fixes and upgrades for the Appwrite Platform. -# "product / platform": -# - - -# Fixes and upgrades for database relationships -"feature / relationships": - - "(relationship)" - -# Issues found only on Appwrite Cloud -# "product / cloud": -# - - -# Fixes and upgrades for the Appwrite VCS. -"product / vcs": - - "(repo|push|vcs|repository)" - -# Fixes and upgrades for the Appwrite GraphQL API. -"api / graphql": - - "(graphql|gql|mutation)" - -# Fixes and upgrades for the Appwrite Assistant. -"product / assistant": - - "(assistant)" - -# Fixes and upgrades for the Appwrite Domains. -"product / domains": - - "(domain|dns|ssl|certificate)" - -# Fixes and upgrades for the Appwrite Locale. -"product / locale": - - "(locale|i18n|internationalization|localization|l10n|translation|timezone|country)" - -# Fixes and upgrades for the Appwrite Avatars. -"product / avatars": - - "(avatar|initial|flag|icon)" - -# Fixes and upgrades for Appwrite Sites. -"product / sites": - - "(site|web|hosting|domain|ssl|certificate|nextjs|nuxt|react|angular|vue|svelte|astro)" - -# Fixes and upgrades for the Appwrite CLI. -"sdk / cli": - - "(cli|command line)" - -# Issues only found when self-hosting Appwrite -"product / self-hosted": - - "(self-host|self host)" diff --git a/.github/workflows/auto-label-issue.yml b/.github/workflows/auto-label-issue.yml deleted file mode 100644 index e0eb0de98d..0000000000 --- a/.github/workflows/auto-label-issue.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Auto Label Issue - -on: - issues: - types: [opened] - -permissions: - issues: write - contents: read - -jobs: - labeler: - runs-on: ubuntu-latest - steps: - - name: Issue Labeler - uses: github/issue-labeler@v3.4 - with: - configuration-path: .github/labeler.yml - enable-versioned-regex: false - include-title: 1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index c9b8a39fb2..9cd5808e33 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> We just announced API for spatial columns for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-spatial-columns) +> We just announced Timestamp Overrides for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-timestamp-overrides) > Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga) From 4a3cbdafca1deeb3ccd4b71254fa55eb9047e942 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 25 Sep 2025 17:59:39 +1200 Subject: [PATCH 166/274] Move to row-level scope for txn --- app/config/roles.php | 4 ---- app/config/scopes.php | 6 ------ app/config/specs/open-api3-1.8.x-client.json | 20 +++++++++---------- app/config/specs/open-api3-1.8.x-console.json | 20 +++++++++---------- app/config/specs/open-api3-1.8.x-server.json | 20 +++++++++---------- app/config/specs/open-api3-latest-client.json | 20 +++++++++---------- .../specs/open-api3-latest-console.json | 20 +++++++++---------- app/config/specs/open-api3-latest-server.json | 20 +++++++++---------- app/config/specs/swagger2-1.8.x-client.json | 20 +++++++++---------- app/config/specs/swagger2-1.8.x-console.json | 20 +++++++++---------- app/config/specs/swagger2-1.8.x-server.json | 20 +++++++++---------- app/config/specs/swagger2-latest-client.json | 20 +++++++++---------- app/config/specs/swagger2-latest-console.json | 20 +++++++++---------- app/config/specs/swagger2-latest-server.json | 20 +++++++++---------- .../Http/Databases/Transactions/Create.php | 2 +- .../Http/Databases/Transactions/Delete.php | 2 +- .../Transactions/Operations/Create.php | 2 +- .../Http/Databases/Transactions/Update.php | 2 +- .../Http/TablesDB/Transactions/Create.php | 2 +- .../Http/TablesDB/Transactions/Delete.php | 2 +- .../Http/TablesDB/Transactions/Get.php | 2 +- .../Transactions/Operations/Create.php | 2 +- .../Http/TablesDB/Transactions/Update.php | 2 +- .../Http/TablesDB/Transactions/XList.php | 2 +- tests/e2e/Scopes/ProjectCustom.php | 2 -- 25 files changed, 130 insertions(+), 142 deletions(-) diff --git a/app/config/roles.php b/app/config/roles.php index 6a4bba2699..0f0945a2b4 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -16,8 +16,6 @@ $member = [ 'documents.write', 'rows.read', 'rows.write', - 'transactions.read', - 'transactions.write', 'files.read', 'files.write', 'projects.read', @@ -43,8 +41,6 @@ $admins = [ 'documents.write', 'rows.read', 'rows.write', - 'transactions.read', - 'transactions.write', 'files.read', 'files.write', 'buckets.read', diff --git a/app/config/scopes.php b/app/config/scopes.php index 9807c09216..d90ca2eabf 100644 --- a/app/config/scopes.php +++ b/app/config/scopes.php @@ -52,12 +52,6 @@ return [ // List of publicly visible scopes 'indexes.write' => [ 'description' => 'Access to create, update, and delete your project\'s database collection\'s indexes', ], - 'transactions.read' => [ - 'description' => 'Access to read your project\'s database transactions', - ], - 'transactions.write' => [ - 'description' => 'Access to create, update, and delete your project\'s database transactions', - ], 'documents.read' => [ 'description' => 'Access to read your project\'s database documents', ], diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index 6d5acf8c86..4fd19f7d6a 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -4903,7 +4903,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5033,7 +5033,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5109,7 +5109,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5173,7 +5173,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -7980,7 +7980,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -8045,7 +8045,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -8113,7 +8113,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -8175,7 +8175,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -8251,7 +8251,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -8315,7 +8315,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 797a7e72e4..fe014dcd4c 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -5302,7 +5302,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5432,7 +5432,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5508,7 +5508,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5572,7 +5572,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -33372,7 +33372,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -33437,7 +33437,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -33505,7 +33505,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -33567,7 +33567,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -33643,7 +33643,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -33707,7 +33707,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 8e8c33ace4..16ae5e57f9 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -4842,7 +4842,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -4976,7 +4976,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5054,7 +5054,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5120,7 +5120,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -23888,7 +23888,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -23955,7 +23955,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -24025,7 +24025,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -24089,7 +24089,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -24167,7 +24167,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -24233,7 +24233,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 6d5acf8c86..4fd19f7d6a 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -4903,7 +4903,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5033,7 +5033,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5109,7 +5109,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5173,7 +5173,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -7980,7 +7980,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -8045,7 +8045,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -8113,7 +8113,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -8175,7 +8175,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -8251,7 +8251,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -8315,7 +8315,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 797a7e72e4..fe014dcd4c 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -5302,7 +5302,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5432,7 +5432,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5508,7 +5508,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5572,7 +5572,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -33372,7 +33372,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -33437,7 +33437,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -33505,7 +33505,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -33567,7 +33567,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -33643,7 +33643,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -33707,7 +33707,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 8e8c33ace4..16ae5e57f9 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -4842,7 +4842,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -4976,7 +4976,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5054,7 +5054,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -5120,7 +5120,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client", @@ -23888,7 +23888,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -23955,7 +23955,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -24025,7 +24025,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -24089,7 +24089,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -24167,7 +24167,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", @@ -24233,7 +24233,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index e87ce88a6c..1aa47cb4fc 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -5045,7 +5045,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5174,7 +5174,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5251,7 +5251,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5314,7 +5314,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -8057,7 +8057,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -8122,7 +8122,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -8190,7 +8190,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -8251,7 +8251,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -8328,7 +8328,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -8391,7 +8391,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 16744679ec..f88b1254cd 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -5464,7 +5464,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5593,7 +5593,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5670,7 +5670,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5733,7 +5733,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -33491,7 +33491,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -33556,7 +33556,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -33624,7 +33624,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -33685,7 +33685,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -33762,7 +33762,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -33825,7 +33825,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index d3f7129c95..40f38c1067 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -4992,7 +4992,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5125,7 +5125,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5204,7 +5204,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5269,7 +5269,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -24063,7 +24063,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -24130,7 +24130,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -24200,7 +24200,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -24263,7 +24263,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -24342,7 +24342,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -24407,7 +24407,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index e87ce88a6c..1aa47cb4fc 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -5045,7 +5045,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5174,7 +5174,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5251,7 +5251,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5314,7 +5314,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -8057,7 +8057,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -8122,7 +8122,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -8190,7 +8190,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -8251,7 +8251,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -8328,7 +8328,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -8391,7 +8391,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 16744679ec..f88b1254cd 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -5464,7 +5464,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5593,7 +5593,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5670,7 +5670,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5733,7 +5733,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -33491,7 +33491,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -33556,7 +33556,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -33624,7 +33624,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -33685,7 +33685,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -33762,7 +33762,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -33825,7 +33825,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index d3f7129c95..40f38c1067 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -4992,7 +4992,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5125,7 +5125,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5204,7 +5204,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -5269,7 +5269,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "documents.write", "platforms": [ "server", "client" @@ -24063,7 +24063,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -24130,7 +24130,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -24200,7 +24200,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -24263,7 +24263,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -24342,7 +24342,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" @@ -24407,7 +24407,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.write", + "scope": "rows.write", "platforms": [ "server", "client" diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index 4a7e95d5ca..a5866ba7d1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -33,7 +33,7 @@ class Create extends Action ->setHttpPath('/v1/databases/transactions') ->desc('Create transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php index da92ce1b4c..a5d2562572 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Delete.php @@ -32,7 +32,7 @@ class Delete extends Action ->setHttpPath('/v1/databases/transactions/:transactionId') ->desc('Delete transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index d2e438706a..0aca3e3f7c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -38,7 +38,7 @@ class Create extends Action ->setHttpPath('/v1/databases/transactions/:transactionId/operations') ->desc('Create operations scoped to a transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 4509114bfc..9f5e5037bf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -44,7 +44,7 @@ class Update extends Action ->setHttpPath('/v1/databases/transactions/:transactionId') ->desc('Update transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php index 6a28d31621..e6c24b3341 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php @@ -30,7 +30,7 @@ class Create extends TransactionsCreate ->setHttpPath('/v1/tablesdb/transactions') ->desc('Create transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'tablesDB', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php index cad11e795a..28f273f566 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php @@ -30,7 +30,7 @@ class Delete extends TransactionsDelete ->setHttpPath('/v1/tablesdb/transactions/:transactionId') ->desc('Delete transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'tablesDB', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php index 39f6754a1e..bc58783a04 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Get.php @@ -30,7 +30,7 @@ class Get extends TransactionsGet ->setHttpPath('/v1/tablesdb/transactions/:transactionId') ->desc('Get transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'tablesDB', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php index 2280a6f7e3..eab0fed161 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -32,7 +32,7 @@ class Create extends OperationsCreate ->setHttpPath('/v1/tablesdb/transactions/:transactionId/operations') ->desc('Create operations scoped to a transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'tablesDB', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php index d76de0766d..bcfb2db406 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php @@ -31,7 +31,7 @@ class Update extends TransactionsUpdate ->setHttpPath('/v1/tablesdb/transactions/:transactionId') ->desc('Update transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.write') + ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'tablesDB', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php index 67a58d7e5f..cfb630e46d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/XList.php @@ -30,7 +30,7 @@ class XList extends TransactionsList ->setHttpPath('/v1/tablesdb/transactions') ->desc('List transactions') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'tablesDB', diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 49aabaae67..c2b4896814 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -70,8 +70,6 @@ trait ProjectCustom 'databases.write', 'collections.read', 'collections.write', - 'transactions.read', - 'transactions.write', 'tables.read', 'tables.write', 'documents.read', From 7a03084e832a57327e8d3244c25cef1122b98fd5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 25 Sep 2025 19:21:21 +1200 Subject: [PATCH 167/274] Bump SDK version --- app/config/platforms.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 1eb8891b8a..fa2ec09ee2 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '20.1.0-rc.1', + 'version' => '20.2.0-rc.1', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '19.1.0-rc.1', + 'version' => '19.2.0-rc.1', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, From e06c358db494943af8324015059afd194661be23 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 26 Sep 2025 00:50:25 +1200 Subject: [PATCH 168/274] Don't require data for delete operation --- .../Utopia/Database/Validator/Operation.php | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 4632678940..6bc068a60f 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -24,6 +24,20 @@ class Operation extends Validator 'decrement' => true, ]; + /** @var array */ + private array $requiresData = [ + 'create' => true, + 'update' => true, + 'upsert' => true, + 'delete' => false, // Delete doesn't need data + 'increment' => true, + 'decrement' => true, + 'bulkCreate' => true, + 'bulkUpdate' => true, + 'bulkUpsert' => true, + 'bulkDelete' => true, + ]; + /** @var array */ private array $actions = [ 'create' => true, @@ -121,14 +135,23 @@ class Operation extends Validator return false; } - // Data must be present and must be array (can be empty) - if (!\array_key_exists('data', $value)) { - $this->description = "Missing required key: data"; - return false; - } - if (!\is_array($value['data'])) { - $this->description = "Key 'data' must be an array"; - return false; + // Data validation - only required for certain actions + if (isset($this->requiresData[$value['action']]) && $this->requiresData[$value['action']]) { + // Data is required for this action + if (!\array_key_exists('data', $value)) { + $this->description = "Missing required key: data"; + return false; + } + if (!\is_array($value['data'])) { + $this->description = "Key 'data' must be an array"; + return false; + } + } else if (\array_key_exists('data', $value)) { + // Data is optional but if provided, must be an array + if (!\is_array($value['data'])) { + $this->description = "Key 'data' must be an array"; + return false; + } } return true; From 14c64e405b582cb55450444e5a29e71af6d28e47 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 25 Sep 2025 19:45:32 +0530 Subject: [PATCH 169/274] chore: update framework lib --- composer.lock | 60 +++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/composer.lock b/composer.lock index b9475de5b9..f5a8c43b32 100644 --- a/composer.lock +++ b/composer.lock @@ -1569,16 +1569,16 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.8.0", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "ce27936c8dfb73e3ab9c94469130428af9752c96" + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/ce27936c8dfb73e3ab9c94469130428af9752c96", - "reference": "ce27936c8dfb73e3ab9c94469130428af9752c96", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226", + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226", "shasum": "" }, "require": { @@ -1632,7 +1632,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2025-09-22T20:41:46+00:00" + "time": "2025-09-24T15:12:37+00:00" }, { "name": "paragonie/random_compat", @@ -3939,16 +3939,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.27", + "version": "0.33.28", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37" + "reference": "5aaa94d406577b0059ad28c78022606890dc6de0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d9d10a895e85c8c7675220347cc6109db9d3bd37", - "reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37", + "url": "https://api.github.com/repos/utopia-php/http/zipball/5aaa94d406577b0059ad28c78022606890dc6de0", + "reference": "5aaa94d406577b0059ad28c78022606890dc6de0", "shasum": "" }, "require": { @@ -3980,9 +3980,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.27" + "source": "https://github.com/utopia-php/http/tree/0.33.28" }, - "time": "2025-09-07T18:40:53+00:00" + "time": "2025-09-25T10:44:24+00:00" }, { "name": "utopia-php/image", @@ -4187,16 +4187,16 @@ }, { "name": "utopia-php/migration", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4" + "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/c42935a6a4ee3701c68d24244e82ecb39e945ec4", - "reference": "c42935a6a4ee3701c68d24244e82ecb39e945ec4", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/42ff497c5231f5a727d1e229419ff1d2195d8093", + "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093", "shasum": "" }, "require": { @@ -4237,9 +4237,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.1.1" + "source": "https://github.com/utopia-php/migration/tree/1.2.0" }, - "time": "2025-09-10T06:17:20+00:00" + "time": "2025-09-24T10:32:24+00:00" }, { "name": "utopia-php/orchestration", @@ -6230,16 +6230,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.27", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0a9aa4440b6a9528cf360071502628d717af3e0a" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0a9aa4440b6a9528cf360071502628d717af3e0a", - "reference": "0a9aa4440b6a9528cf360071502628d717af3e0a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -6264,7 +6264,7 @@ "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", + "sebastian/exporter": "^4.0.8", "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", @@ -6313,7 +6313,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.27" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -6337,7 +6337,7 @@ "type": "tidelift" } ], - "time": "2025-09-14T06:18:03+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "psr/cache", @@ -6829,16 +6829,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.7", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "eb49b981ef0817890129cb70f774506bebe57740" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/eb49b981ef0817890129cb70f774506bebe57740", - "reference": "eb49b981ef0817890129cb70f774506bebe57740", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -6894,7 +6894,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.7" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { @@ -6914,7 +6914,7 @@ "type": "tidelift" } ], - "time": "2025-09-22T05:18:21+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", From 4e91904608d4d1269a26458e9c2ab229976378e9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 26 Sep 2025 14:25:02 +1200 Subject: [PATCH 170/274] Update database --- composer.lock | 60 +++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/composer.lock b/composer.lock index 1ff64ab5bc..d8fb2b1b4a 100644 --- a/composer.lock +++ b/composer.lock @@ -1569,16 +1569,16 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.8.0", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "ce27936c8dfb73e3ab9c94469130428af9752c96" + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/ce27936c8dfb73e3ab9c94469130428af9752c96", - "reference": "ce27936c8dfb73e3ab9c94469130428af9752c96", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226", + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226", "shasum": "" }, "require": { @@ -1632,7 +1632,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2025-09-22T20:41:46+00:00" + "time": "2025-09-24T15:12:37+00:00" }, { "name": "paragonie/random_compat", @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "732ffef52fd76483722537ee1f4f83726125a7ec" + "reference": "19a3ab2ae99578861dd1a7b4fdbfb62b37d09447" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/732ffef52fd76483722537ee1f4f83726125a7ec", - "reference": "732ffef52fd76483722537ee1f4f83726125a7ec", + "url": "https://api.github.com/repos/utopia-php/database/zipball/19a3ab2ae99578861dd1a7b4fdbfb62b37d09447", + "reference": "19a3ab2ae99578861dd1a7b4fdbfb62b37d09447", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.1.0" + "source": "https://github.com/utopia-php/database/tree/2.2.0" }, - "time": "2025-09-16T13:44:45+00:00" + "time": "2025-09-26T02:23:30+00:00" }, { "name": "utopia-php/detector", @@ -3939,16 +3939,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.27", + "version": "0.33.28", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37" + "reference": "5aaa94d406577b0059ad28c78022606890dc6de0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d9d10a895e85c8c7675220347cc6109db9d3bd37", - "reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37", + "url": "https://api.github.com/repos/utopia-php/http/zipball/5aaa94d406577b0059ad28c78022606890dc6de0", + "reference": "5aaa94d406577b0059ad28c78022606890dc6de0", "shasum": "" }, "require": { @@ -3980,9 +3980,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.27" + "source": "https://github.com/utopia-php/http/tree/0.33.28" }, - "time": "2025-09-07T18:40:53+00:00" + "time": "2025-09-25T10:44:24+00:00" }, { "name": "utopia-php/image", @@ -6230,16 +6230,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.27", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0a9aa4440b6a9528cf360071502628d717af3e0a" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0a9aa4440b6a9528cf360071502628d717af3e0a", - "reference": "0a9aa4440b6a9528cf360071502628d717af3e0a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -6264,7 +6264,7 @@ "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", + "sebastian/exporter": "^4.0.8", "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", @@ -6313,7 +6313,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.27" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -6337,7 +6337,7 @@ "type": "tidelift" } ], - "time": "2025-09-14T06:18:03+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "psr/cache", @@ -6829,16 +6829,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.7", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "eb49b981ef0817890129cb70f774506bebe57740" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/eb49b981ef0817890129cb70f774506bebe57740", - "reference": "eb49b981ef0817890129cb70f774506bebe57740", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -6894,7 +6894,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.7" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { @@ -6914,7 +6914,7 @@ "type": "tidelift" } ], - "time": "2025-09-22T05:18:21+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", From ef147cde21604a76b860d90b3ac50506fc1efef4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 26 Sep 2025 14:28:11 +1200 Subject: [PATCH 171/274] Format --- src/Appwrite/Platform/Workers/StatsUsage.php | 2 +- src/Appwrite/Utopia/Database/Validator/Operation.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index e7e8dba4f3..a2473420a4 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -435,7 +435,7 @@ class StatsUsage extends Action return $cmp; } - unset($this->projects[$sequence]); + unset($this->projects[$sequence]); // Period ASC $cmp = strcmp($a['period'], $b['period']); if ($cmp !== 0) { diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 6bc068a60f..a8de32de79 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -146,7 +146,7 @@ class Operation extends Validator $this->description = "Key 'data' must be an array"; return false; } - } else if (\array_key_exists('data', $value)) { + } elseif (\array_key_exists('data', $value)) { // Data is optional but if provided, must be an array if (!\is_array($value['data'])) { $this->description = "Key 'data' must be an array"; From 372a50f9a573e5410134501dffd609b540c03356 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 26 Sep 2025 14:45:09 +1200 Subject: [PATCH 172/274] Update lock --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 0225722352..d8fb2b1b4a 100644 --- a/composer.lock +++ b/composer.lock @@ -4187,16 +4187,16 @@ }, { "name": "utopia-php/migration", - "version": "1.2.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093" + "reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/42ff497c5231f5a727d1e229419ff1d2195d8093", - "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/6fb6f8f032cd34c3c65728a55d494adeac2ff038", + "reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038", "shasum": "" }, "require": { @@ -4237,9 +4237,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.2.0" + "source": "https://github.com/utopia-php/migration/tree/1.1.0" }, - "time": "2025-09-24T10:32:24+00:00" + "time": "2025-09-10T05:45:30+00:00" }, { "name": "utopia-php/orchestration", From a0f0449c21d903badf4dd5b3d62f996a850749b8 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 26 Sep 2025 12:33:10 +0530 Subject: [PATCH 173/274] fix: enum typing for platform in specs --- app/config/specs/open-api3-1.8.x-console.json | 16 +++++++++---- .../specs/open-api3-latest-console.json | 16 +++++++++---- app/config/specs/swagger2-1.8.x-console.json | 16 +++++++++---- app/config/specs/swagger2-latest-console.json | 16 +++++++++---- app/controllers/api/projects.php | 23 ++++++++++++++++++- .../Utopia/Response/Model/Platform.php | 4 ++-- 6 files changed, 72 insertions(+), 19 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index ab36ae475c..5eac3ca9f6 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -25153,7 +25153,7 @@ "properties": { "type": { "type": "string", - "description": "Platform type.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "x-example": "web", "enum": [ "web", @@ -53440,16 +53440,24 @@ }, "type": { "type": "string", - "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "x-example": "web", "enum": [ "web", "flutter-web", "flutter-ios", "flutter-android", - "ios", + "flutter-linux", + "flutter-macos", + "flutter-windows", + "apple-ios", + "apple-macos", + "apple-watchos", + "apple-tvos", "android", - "unity" + "unity", + "react-native-ios", + "react-native-android" ] }, "key": { diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index ab36ae475c..5eac3ca9f6 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -25153,7 +25153,7 @@ "properties": { "type": { "type": "string", - "description": "Platform type.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "x-example": "web", "enum": [ "web", @@ -53440,16 +53440,24 @@ }, "type": { "type": "string", - "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "x-example": "web", "enum": [ "web", "flutter-web", "flutter-ios", "flutter-android", - "ios", + "flutter-linux", + "flutter-macos", + "flutter-windows", + "apple-ios", + "apple-macos", + "apple-watchos", + "apple-tvos", "android", - "unity" + "unity", + "react-native-ios", + "react-native-android" ] }, "key": { diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index e830f768be..0a6bdf0889 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -25305,7 +25305,7 @@ "properties": { "type": { "type": "string", - "description": "Platform type.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "default": null, "x-example": "web", "enum": [ @@ -53396,16 +53396,24 @@ }, "type": { "type": "string", - "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "x-example": "web", "enum": [ "web", "flutter-web", "flutter-ios", "flutter-android", - "ios", + "flutter-linux", + "flutter-macos", + "flutter-windows", + "apple-ios", + "apple-macos", + "apple-watchos", + "apple-tvos", "android", - "unity" + "unity", + "react-native-ios", + "react-native-android" ] }, "key": { diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index e830f768be..0a6bdf0889 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -25305,7 +25305,7 @@ "properties": { "type": { "type": "string", - "description": "Platform type.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "default": null, "x-example": "web", "enum": [ @@ -53396,16 +53396,24 @@ }, "type": { "type": "string", - "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.", + "description": "Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.", "x-example": "web", "enum": [ "web", "flutter-web", "flutter-ios", "flutter-android", - "ios", + "flutter-linux", + "flutter-macos", + "flutter-windows", + "apple-ios", + "apple-macos", + "apple-watchos", + "apple-tvos", "android", - "unity" + "unity", + "react-native-ios", + "react-native-android" ] }, "key": { diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index ea3a00dcb6..80d407322e 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1756,7 +1756,28 @@ App::post('/v1/projects/:projectId/platforms') ] )) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('type', null, new WhiteList([Platform::TYPE_WEB, Platform::TYPE_FLUTTER_WEB, Platform::TYPE_FLUTTER_IOS, Platform::TYPE_FLUTTER_ANDROID, Platform::TYPE_FLUTTER_LINUX, Platform::TYPE_FLUTTER_MACOS, Platform::TYPE_FLUTTER_WINDOWS, Platform::TYPE_APPLE_IOS, Platform::TYPE_APPLE_MACOS, Platform::TYPE_APPLE_WATCHOS, Platform::TYPE_APPLE_TVOS, Platform::TYPE_ANDROID, Platform::TYPE_UNITY, Platform::TYPE_REACT_NATIVE_IOS, Platform::TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.') + ->param( + 'type', + null, + new WhiteList([ + Platform::TYPE_WEB, + Platform::TYPE_FLUTTER_WEB, + Platform::TYPE_FLUTTER_IOS, + Platform::TYPE_FLUTTER_ANDROID, + Platform::TYPE_FLUTTER_LINUX, + Platform::TYPE_FLUTTER_MACOS, + Platform::TYPE_FLUTTER_WINDOWS, + Platform::TYPE_APPLE_IOS, + Platform::TYPE_APPLE_MACOS, + Platform::TYPE_APPLE_WATCHOS, + Platform::TYPE_APPLE_TVOS, + Platform::TYPE_ANDROID, + Platform::TYPE_UNITY, + Platform::TYPE_REACT_NATIVE_IOS, + Platform::TYPE_REACT_NATIVE_ANDROID, + ], true), + 'Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.' + ) ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true) ->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true) diff --git a/src/Appwrite/Utopia/Response/Model/Platform.php b/src/Appwrite/Utopia/Response/Model/Platform.php index 65f3c343d4..151e43780d 100644 --- a/src/Appwrite/Utopia/Response/Model/Platform.php +++ b/src/Appwrite/Utopia/Response/Model/Platform.php @@ -41,10 +41,10 @@ class Platform extends Model ]) ->addRule('type', [ 'type' => self::TYPE_ENUM, - 'description' => 'Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, ios, android, and unity.', + 'description' => 'Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.', 'default' => '', 'example' => 'web', - 'enum' => ['web', 'flutter-web', 'flutter-ios', 'flutter-android', 'ios', 'android', 'unity'], + 'enum' => ['web', 'flutter-web', 'flutter-ios', 'flutter-android', 'flutter-linux', 'flutter-macos', 'flutter-windows', 'apple-ios', 'apple-macos', 'apple-watchos', 'apple-tvos', 'android', 'unity', 'react-native-ios', 'react-native-android'], ]) ->addRule('key', [ 'type' => self::TYPE_STRING, From a0e8711d9b1ebcaa7b34fd4dc467cf12052228e3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 26 Sep 2025 15:33:37 +0530 Subject: [PATCH 174/274] chore: update sdks add response models --- app/config/platforms.php | 28 ++++++++++++++-------------- composer.json | 2 +- composer.lock | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 8a33144b2f..831f6b9db8 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '20.1.0', + 'version' => '20.2.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '19.1.0', + 'version' => '19.2.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '12.1.0', + 'version' => '12.2.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '10.1.0', + 'version' => '10.2.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -139,7 +139,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.14.0', + 'version' => '0.15.0', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '19.1.0', + 'version' => '19.2.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -281,7 +281,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '17.1.0', + 'version' => '17.2.0', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -300,7 +300,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '13.1.0', + 'version' => '13.2.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '18.1.0', + 'version' => '18.2.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => '0.12.0', + 'version' => '0.13.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, @@ -357,7 +357,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.18.0', + 'version' => '0.19.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '18.1.0', + 'version' => '18.2.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '11.1.0', + 'version' => '11.2.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '12.1.0', + 'version' => '12.2.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/composer.json b/composer.json index 1dc7288441..626b156f2f 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ }, "require-dev": { "ext-fileinfo": "*", - "appwrite/sdk-generator": "*", + "appwrite/sdk-generator": "dev-feat-enum-support", "phpunit/phpunit": "9.*", "swoole/ide-helper": "5.1.2", "phpstan/phpstan": "1.8.*", diff --git a/composer.lock b/composer.lock index f5a8c43b32..71e4d97cec 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7553e976312b0423cc31544abb91caec", + "content-hash": "c7b1f0f5834dbeb48c5f9880c217ebef", "packages": [ { "name": "adhocore/jwt", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.4.0", + "version": "dev-feat-enum-support", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "3583fa6fddb1d1a902b37ff2048527a5827fc008" + "reference": "80b76ff4d97e7c5ca27abc540cc5f7ca8d56ee4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/3583fa6fddb1d1a902b37ff2048527a5827fc008", - "reference": "3583fa6fddb1d1a902b37ff2048527a5827fc008", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/80b76ff4d97e7c5ca27abc540cc5f7ca8d56ee4d", + "reference": "80b76ff4d97e7c5ca27abc540cc5f7ca8d56ee4d", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.4.0" + "source": "https://github.com/appwrite/sdk-generator/tree/feat-enum-support" }, - "time": "2025-09-23T02:27:10+00:00" + "time": "2025-09-26T08:58:30+00:00" }, { "name": "doctrine/annotations", @@ -8518,7 +8518,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "appwrite/sdk-generator": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From d5e1f9952ae05efab0b1320395949ea19bf903e2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 29 Sep 2025 11:26:00 +0530 Subject: [PATCH 175/274] chore: add enums for database type and column status --- app/config/specs/open-api3-1.8.x-console.json | 123 ++++++++++++++++-- app/config/specs/open-api3-1.8.x-server.json | 123 ++++++++++++++++-- .../specs/open-api3-latest-console.json | 123 ++++++++++++++++-- app/config/specs/open-api3-latest-server.json | 123 ++++++++++++++++-- app/config/specs/swagger2-1.8.x-console.json | 123 ++++++++++++++++-- app/config/specs/swagger2-1.8.x-server.json | 123 ++++++++++++++++-- app/config/specs/swagger2-latest-console.json | 123 ++++++++++++++++-- app/config/specs/swagger2-latest-server.json | 123 ++++++++++++++++-- src/Appwrite/Utopia/Response/Model/Column.php | 3 +- .../Utopia/Response/Model/Database.php | 3 +- 10 files changed, 876 insertions(+), 114 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 5eac3ca9f6..4a27d5a49b 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -46223,7 +46223,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -47910,7 +47914,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47998,7 +48009,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48088,7 +48106,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48178,7 +48203,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48251,7 +48283,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48331,7 +48370,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48421,7 +48467,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48501,7 +48554,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48581,7 +48641,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48661,7 +48728,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48769,7 +48843,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48848,7 +48929,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48939,7 +49027,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 93a0d9d46b..50f668ea64 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -35093,7 +35093,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -36780,7 +36784,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36868,7 +36879,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36958,7 +36976,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37048,7 +37073,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37121,7 +37153,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37201,7 +37240,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37291,7 +37337,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37371,7 +37424,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37451,7 +37511,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37531,7 +37598,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37639,7 +37713,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37718,7 +37799,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37809,7 +37897,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 5eac3ca9f6..4a27d5a49b 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -46223,7 +46223,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -47910,7 +47914,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47998,7 +48009,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48088,7 +48106,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48178,7 +48203,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48251,7 +48283,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48331,7 +48370,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48421,7 +48467,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48501,7 +48554,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48581,7 +48641,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48661,7 +48728,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48769,7 +48843,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48848,7 +48929,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48939,7 +49027,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 93a0d9d46b..50f668ea64 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -35093,7 +35093,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -36780,7 +36784,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36868,7 +36879,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36958,7 +36976,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37048,7 +37073,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37121,7 +37153,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37201,7 +37240,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37291,7 +37337,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37371,7 +37424,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37451,7 +37511,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37531,7 +37598,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37639,7 +37713,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37718,7 +37799,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37809,7 +37897,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 0a6bdf0889..aa0f0ec520 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -46159,7 +46159,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -47848,7 +47852,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47936,7 +47947,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48026,7 +48044,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48116,7 +48141,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48189,7 +48221,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48269,7 +48308,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48359,7 +48405,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48439,7 +48492,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48519,7 +48579,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48599,7 +48666,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48707,7 +48781,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48786,7 +48867,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48877,7 +48965,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 0a5fc8fe95..b1b10f4543 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -35120,7 +35120,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -36809,7 +36813,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36897,7 +36908,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36987,7 +37005,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37077,7 +37102,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37150,7 +37182,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37230,7 +37269,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37320,7 +37366,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37400,7 +37453,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37480,7 +37540,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37560,7 +37627,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37668,7 +37742,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37747,7 +37828,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37838,7 +37926,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 0a6bdf0889..aa0f0ec520 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -46159,7 +46159,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -47848,7 +47852,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -47936,7 +47947,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48026,7 +48044,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48116,7 +48141,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48189,7 +48221,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48269,7 +48308,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48359,7 +48405,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48439,7 +48492,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48519,7 +48579,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48599,7 +48666,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48707,7 +48781,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48786,7 +48867,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -48877,7 +48965,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 0a5fc8fe95..b1b10f4543 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -35120,7 +35120,11 @@ "type": { "type": "string", "description": "Database type.", - "x-example": "legacy" + "x-example": "legacy", + "enum": [ + "legacy", + "tablesdb" + ] } }, "required": [ @@ -36809,7 +36813,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36897,7 +36908,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -36987,7 +37005,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37077,7 +37102,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37150,7 +37182,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37230,7 +37269,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37320,7 +37366,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37400,7 +37453,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37480,7 +37540,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37560,7 +37627,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37668,7 +37742,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37747,7 +37828,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", @@ -37838,7 +37926,14 @@ "status": { "type": "string", "description": "Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`", - "x-example": "available" + "x-example": "available", + "enum": [ + "available", + "processing", + "deleting", + "stuck", + "failed" + ] }, "error": { "type": "string", diff --git a/src/Appwrite/Utopia/Response/Model/Column.php b/src/Appwrite/Utopia/Response/Model/Column.php index 5562de39f2..cae8d1fadb 100644 --- a/src/Appwrite/Utopia/Response/Model/Column.php +++ b/src/Appwrite/Utopia/Response/Model/Column.php @@ -23,10 +23,11 @@ class Column extends Model 'example' => 'string', ]) ->addRule('status', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Column status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`', 'default' => '', 'example' => 'available', + 'enum' => ['available', 'processing', 'deleting', 'stuck', 'failed'], ]) ->addRule('error', [ 'type' => self::TYPE_STRING, diff --git a/src/Appwrite/Utopia/Response/Model/Database.php b/src/Appwrite/Utopia/Response/Model/Database.php index 44a0d52af8..59f32b3162 100644 --- a/src/Appwrite/Utopia/Response/Model/Database.php +++ b/src/Appwrite/Utopia/Response/Model/Database.php @@ -41,10 +41,11 @@ class Database extends Model 'example' => false, ]) ->addRule('type', [ - 'type' => self::TYPE_STRING, + 'type' => self::TYPE_ENUM, 'description' => 'Database type.', 'default' => 'legacy', 'example' => 'legacy', + 'enum' => ['legacy', 'tablesdb'], ]) ; } From 5245ff7167dcbbe72e798a4a1eb819eaf460afcf Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 29 Sep 2025 11:29:40 +0530 Subject: [PATCH 176/274] update enum name --- app/config/specs/open-api3-1.8.x-console.json | 39 ++++++---- app/config/specs/open-api3-1.8.x-server.json | 39 ++++++---- .../specs/open-api3-latest-console.json | 39 ++++++---- app/config/specs/open-api3-latest-server.json | 39 ++++++---- app/config/specs/swagger2-1.8.x-console.json | 39 ++++++---- app/config/specs/swagger2-1.8.x-server.json | 39 ++++++---- app/config/specs/swagger2-latest-console.json | 39 ++++++---- app/config/specs/swagger2-latest-server.json | 39 ++++++---- src/Appwrite/SDK/Specification/Format.php | 78 +++++++++++++++++++ 9 files changed, 286 insertions(+), 104 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 4a27d5a49b..bdff664cbc 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -47921,7 +47921,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48016,7 +48017,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48113,7 +48115,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48210,7 +48213,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48290,7 +48294,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48377,7 +48382,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48474,7 +48480,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48561,7 +48568,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48648,7 +48656,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48735,7 +48744,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48850,7 +48860,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48936,7 +48947,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -49034,7 +49046,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 50f668ea64..6b766dbdee 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -36791,7 +36791,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -36886,7 +36887,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -36983,7 +36985,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37080,7 +37083,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37160,7 +37164,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37247,7 +37252,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37344,7 +37350,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37431,7 +37438,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37518,7 +37526,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37605,7 +37614,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37720,7 +37730,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37806,7 +37817,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37904,7 +37916,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 4a27d5a49b..bdff664cbc 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -47921,7 +47921,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48016,7 +48017,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48113,7 +48115,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48210,7 +48213,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48290,7 +48294,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48377,7 +48382,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48474,7 +48480,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48561,7 +48568,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48648,7 +48656,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48735,7 +48744,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48850,7 +48860,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48936,7 +48947,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -49034,7 +49046,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 50f668ea64..6b766dbdee 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -36791,7 +36791,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -36886,7 +36887,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -36983,7 +36985,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37080,7 +37083,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37160,7 +37164,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37247,7 +37252,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37344,7 +37350,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37431,7 +37438,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37518,7 +37526,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37605,7 +37614,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37720,7 +37730,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37806,7 +37817,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37904,7 +37916,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index aa0f0ec520..ee3702d27d 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -47859,7 +47859,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -47954,7 +47955,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48051,7 +48053,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48148,7 +48151,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48228,7 +48232,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48315,7 +48320,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48412,7 +48418,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48499,7 +48506,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48586,7 +48594,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48673,7 +48682,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48788,7 +48798,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48874,7 +48885,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48972,7 +48984,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index b1b10f4543..ff5056b35a 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -36820,7 +36820,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -36915,7 +36916,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37012,7 +37014,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37109,7 +37112,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37189,7 +37193,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37276,7 +37281,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37373,7 +37379,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37460,7 +37467,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37547,7 +37555,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37634,7 +37643,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37749,7 +37759,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37835,7 +37846,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37933,7 +37945,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index aa0f0ec520..ee3702d27d 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -47859,7 +47859,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -47954,7 +47955,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48051,7 +48053,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48148,7 +48151,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48228,7 +48232,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48315,7 +48320,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48412,7 +48418,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48499,7 +48506,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48586,7 +48594,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48673,7 +48682,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48788,7 +48798,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48874,7 +48885,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -48972,7 +48984,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index b1b10f4543..ff5056b35a 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -36820,7 +36820,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -36915,7 +36916,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37012,7 +37014,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37109,7 +37112,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37189,7 +37193,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37276,7 +37281,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37373,7 +37379,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37460,7 +37467,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37547,7 +37555,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37634,7 +37643,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37749,7 +37759,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37835,7 +37846,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", @@ -37933,7 +37945,8 @@ "deleting", "stuck", "failed" - ] + ], + "x-enum-name": "ColumnStatus" }, "error": { "type": "string", diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index db7335e40f..c687df143a 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -624,6 +624,84 @@ abstract class Format return 'AttributeStatus'; } break; + case 'columnString': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnInteger': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnFloat': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnBoolean': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnEmail': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnEnum': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnIp': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnUrl': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnDatetime': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnRelationship': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnPoint': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnLine': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; + case 'columnPolygon': + switch ($param) { + case 'status': + return 'ColumnStatus'; + } + break; case 'healthStatus': switch ($param) { case 'status': From b614045d97fd99b0ddee5e08b672f3f91862cf9f Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 30 Sep 2025 18:08:49 +0400 Subject: [PATCH 177/274] fix(builds-worker): Use outputDirectory attribute from deployment or resource --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index ce45d6b629..9fcf64814c 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -692,9 +692,11 @@ class Builds extends Action // Start separation, enter build folder $listFilesCommand .= 'echo "{APPWRITE_DETECTION_SEPARATOR_START}" && cd /usr/local/build'; + $outputDirectory = $deployment->getAttribute('outputDirectory') ?? $resource->getAttribute('outputDirectory'); + // Enter output directory, if set - if (!empty($resource->getAttribute('outputDirectory', ''))) { - $listFilesCommand .= ' && cd ' . \escapeshellarg($resource->getAttribute('outputDirectory', '')); + if ($outputDirectory !== null) { + $listFilesCommand .= ' && cd ' . \escapeshellarg($resource->getAttribute('outputDirectory')); } // Print files, and end separation From 04d66938037f03c5c695703c3b5679bc66c8b28e Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 30 Sep 2025 18:10:53 +0400 Subject: [PATCH 178/274] Fix output directory path validation and position --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 9fcf64814c..f4b5056bb2 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -692,10 +692,9 @@ class Builds extends Action // Start separation, enter build folder $listFilesCommand .= 'echo "{APPWRITE_DETECTION_SEPARATOR_START}" && cd /usr/local/build'; - $outputDirectory = $deployment->getAttribute('outputDirectory') ?? $resource->getAttribute('outputDirectory'); - // Enter output directory, if set - if ($outputDirectory !== null) { + $outputDirectory = $deployment->getAttribute('outputDirectory') ?? $resource->getAttribute('outputDirectory'); + if (!empty($outputDirectory)) { $listFilesCommand .= ' && cd ' . \escapeshellarg($resource->getAttribute('outputDirectory')); } From 4b985441123a19e61d5a0926a7f06edcf9fc1258 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 30 Sep 2025 18:31:37 +0400 Subject: [PATCH 179/274] Move outputDirectory initialization before conditional flow --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index f4b5056bb2..fcbaf1acb1 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -686,6 +686,7 @@ class Builds extends Action if ($version === 'v2') { $command = 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh'; } else { + $outputDirectory = $deployment->getAttribute('outputDirectory') ?? $resource->getAttribute('outputDirectory'); if ($resource->getCollection() === 'sites') { $listFilesCommand = ''; @@ -693,7 +694,6 @@ class Builds extends Action $listFilesCommand .= 'echo "{APPWRITE_DETECTION_SEPARATOR_START}" && cd /usr/local/build'; // Enter output directory, if set - $outputDirectory = $deployment->getAttribute('outputDirectory') ?? $resource->getAttribute('outputDirectory'); if (!empty($outputDirectory)) { $listFilesCommand .= ' && cd ' . \escapeshellarg($resource->getAttribute('outputDirectory')); } @@ -726,7 +726,7 @@ class Builds extends Action destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}", variables: $vars, command: $command, - outputDirectory: $resource->getAttribute('outputDirectory', '') + outputDirectory: $outputDirectory ?? '' ); Console::log('createRuntime finished'); From debdfeedf1bd35b8797384d2e6e9b22472050493 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 30 Sep 2025 18:33:22 +0400 Subject: [PATCH 180/274] Fix variable reference in function build command --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index fcbaf1acb1..e042099a97 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -695,7 +695,7 @@ class Builds extends Action // Enter output directory, if set if (!empty($outputDirectory)) { - $listFilesCommand .= ' && cd ' . \escapeshellarg($resource->getAttribute('outputDirectory')); + $listFilesCommand .= ' && cd ' . \escapeshellarg($outputDirectory); } // Print files, and end separation From b7604a5742d63863fc0ec00255d26b02eb858614 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 30 Sep 2025 19:51:17 +0400 Subject: [PATCH 181/274] Fix buildOutput attribute name in deployment check --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index e042099a97..d6385c1f40 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -686,7 +686,7 @@ class Builds extends Action if ($version === 'v2') { $command = 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh'; } else { - $outputDirectory = $deployment->getAttribute('outputDirectory') ?? $resource->getAttribute('outputDirectory'); + $outputDirectory = $deployment->getAttribute('buildOutput') ?? $resource->getAttribute('outputDirectory'); if ($resource->getCollection() === 'sites') { $listFilesCommand = ''; From 3dd099bd9476cd46f86dc3b85255a884fed1603c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 1 Oct 2025 17:05:12 +1300 Subject: [PATCH 182/274] Update database for nested selection fix --- composer.lock | 73 +++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/composer.lock b/composer.lock index f5a8c43b32..ab8887de49 100644 --- a/composer.lock +++ b/composer.lock @@ -2596,16 +2596,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", "shasum": "" }, "require": { @@ -2672,7 +2672,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.3" + "source": "https://github.com/symfony/http-client/tree/v7.3.4" }, "funding": [ { @@ -2692,7 +2692,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T07:45:05+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/http-client-contracts", @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "24c4519b4ac32aee13af31dddd984db2a3b34980" + "reference": "7ec0238bf0197a38c66f392c65355d5e7eed8248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/24c4519b4ac32aee13af31dddd984db2a3b34980", - "reference": "24c4519b4ac32aee13af31dddd984db2a3b34980", + "url": "https://api.github.com/repos/utopia-php/database/zipball/7ec0238bf0197a38c66f392c65355d5e7eed8248", + "reference": "7ec0238bf0197a38c66f392c65355d5e7eed8248", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.5.0" + "source": "https://github.com/utopia-php/database/tree/1.5.1" }, - "time": "2025-09-18T14:42:01+00:00" + "time": "2025-10-01T03:51:58+00:00" }, { "name": "utopia-php/detector", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.4.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "3583fa6fddb1d1a902b37ff2048527a5827fc008" + "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/3583fa6fddb1d1a902b37ff2048527a5827fc008", - "reference": "3583fa6fddb1d1a902b37ff2048527a5827fc008", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07a7d6276bd684b49469ad7b9e8c3c962121c6fd", + "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.4.0" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.2" }, - "time": "2025-09-23T02:27:10+00:00" + "time": "2025-10-01T03:23:04+00:00" }, { "name": "doctrine/annotations", @@ -7497,16 +7497,16 @@ }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -7571,7 +7571,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -7591,7 +7591,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/filesystem", @@ -8134,16 +8134,16 @@ }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -8175,7 +8175,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -8195,20 +8195,20 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -8223,7 +8223,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -8266,7 +8265,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -8286,7 +8285,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "textalk/websocket", From 397c41e68bd90005d03c347da72d7470d52c5988 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Oct 2025 09:46:20 +0530 Subject: [PATCH 183/274] update cli --- app/config/platforms.php | 2 +- composer.json | 2 +- composer.lock | 67 ++++++++++++++++++-------------------- docs/sdks/cli/CHANGELOG.md | 7 ++++ 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 831f6b9db8..5b42381da3 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '10.0.0', + 'version' => '10.0.1', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, diff --git a/composer.json b/composer.json index 626b156f2f..1dc7288441 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ }, "require-dev": { "ext-fileinfo": "*", - "appwrite/sdk-generator": "dev-feat-enum-support", + "appwrite/sdk-generator": "*", "phpunit/phpunit": "9.*", "swoole/ide-helper": "5.1.2", "phpstan/phpstan": "1.8.*", diff --git a/composer.lock b/composer.lock index 71e4d97cec..61f1dc2355 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c7b1f0f5834dbeb48c5f9880c217ebef", + "content-hash": "7553e976312b0423cc31544abb91caec", "packages": [ { "name": "adhocore/jwt", @@ -2596,16 +2596,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", "shasum": "" }, "require": { @@ -2672,7 +2672,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.3" + "source": "https://github.com/symfony/http-client/tree/v7.3.4" }, "funding": [ { @@ -2692,7 +2692,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T07:45:05+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/http-client-contracts", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "dev-feat-enum-support", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "80b76ff4d97e7c5ca27abc540cc5f7ca8d56ee4d" + "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/80b76ff4d97e7c5ca27abc540cc5f7ca8d56ee4d", - "reference": "80b76ff4d97e7c5ca27abc540cc5f7ca8d56ee4d", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07a7d6276bd684b49469ad7b9e8c3c962121c6fd", + "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/feat-enum-support" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.2" }, - "time": "2025-09-26T08:58:30+00:00" + "time": "2025-10-01T03:23:04+00:00" }, { "name": "doctrine/annotations", @@ -7497,16 +7497,16 @@ }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -7571,7 +7571,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -7591,7 +7591,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/filesystem", @@ -8134,16 +8134,16 @@ }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -8175,7 +8175,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -8195,20 +8195,20 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -8223,7 +8223,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -8266,7 +8265,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -8286,7 +8285,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "textalk/websocket", @@ -8518,9 +8517,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "appwrite/sdk-generator": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 000a7e0938..5af193d491 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 10.0.1 + +* Fix CLI Dart model generation issues +* Fix row permissions and security sync +* Fix error when pushing columns with relationships +* Fix resource name from attributes to columns for TablesDB indexes + ## 10.0.0 * **Breaking:** Removed Avatars CLI command and all related subcommands; corresponding examples deleted From 3d43f851451a27fcb716378951e87d2d25f4006c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Oct 2025 10:06:55 +0530 Subject: [PATCH 184/274] add temp hack --- src/Appwrite/Platform/Tasks/SDKs.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index e5c18338bf..90aa07f726 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -298,6 +298,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $repoBranch = $language['repoBranch'] ?? 'main'; if ($git && !empty($gitUrl)) { + // TODO: fix the temporary 2>/dev/null || true - added due to permission issues when removing files \exec('rm -rf ' . $target . ' && \ mkdir -p ' . $target . ' && \ cd ' . $target . ' && \ @@ -309,7 +310,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND git checkout ' . $gitBranch . ' || git checkout -b ' . $gitBranch . ' && \ git fetch origin ' . $gitBranch . ' || git push -u origin ' . $gitBranch . ' && \ git pull origin ' . $gitBranch . ' && \ - rm -rf ' . $target . '/* && \ + rm -rf ' . $target . '/* 2>/dev/null || true && \ cp -r ' . $result . '/. ' . $target . '/ && \ git add . && \ git commit -m "' . $message . '" && \ From b1757250d1960c3dc3f659808edccc62bb5285ab Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 1 Oct 2025 17:47:42 +1300 Subject: [PATCH 185/274] Fix update --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index ab8887de49..95288fc01d 100644 --- a/composer.lock +++ b/composer.lock @@ -3639,12 +3639,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "7ec0238bf0197a38c66f392c65355d5e7eed8248" + "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/7ec0238bf0197a38c66f392c65355d5e7eed8248", - "reference": "7ec0238bf0197a38c66f392c65355d5e7eed8248", + "url": "https://api.github.com/repos/utopia-php/database/zipball/56efe4daaf23abb753553acffccdcc04cd6178c9", + "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9", "shasum": "" }, "require": { @@ -3687,7 +3687,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/1.5.1" }, - "time": "2025-10-01T03:51:58+00:00" + "time": "2025-10-01T04:44:14+00:00" }, { "name": "utopia-php/detector", From b4dd1df0c745b52fa40a2435c8c4cbe6f36084e5 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Oct 2025 11:56:48 +0530 Subject: [PATCH 186/274] update composer --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 61f1dc2355..95288fc01d 100644 --- a/composer.lock +++ b/composer.lock @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "24c4519b4ac32aee13af31dddd984db2a3b34980" + "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/24c4519b4ac32aee13af31dddd984db2a3b34980", - "reference": "24c4519b4ac32aee13af31dddd984db2a3b34980", + "url": "https://api.github.com/repos/utopia-php/database/zipball/56efe4daaf23abb753553acffccdcc04cd6178c9", + "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.5.0" + "source": "https://github.com/utopia-php/database/tree/1.5.1" }, - "time": "2025-09-18T14:42:01+00:00" + "time": "2025-10-01T04:44:14+00:00" }, { "name": "utopia-php/detector", From 558904e103cfe68cbf6ad1394559bedf9393387b Mon Sep 17 00:00:00 2001 From: Priyanshu Thapliyal <114170980+Priyanshuthapliyal2005@users.noreply.github.com> Date: Wed, 1 Oct 2025 08:58:22 +0000 Subject: [PATCH 187/274] fix(listTemplates): fix invalid template links in Create temporary deployment endpoint --- app/config/specs/open-api3-1.8.x-console.json | 8 ++++---- app/config/specs/open-api3-1.8.x-server.json | 8 ++++---- app/config/specs/open-api3-latest-console.json | 8 ++++---- app/config/specs/open-api3-latest-server.json | 8 ++++---- app/config/specs/swagger2-1.8.x-console.json | 8 ++++---- app/config/specs/swagger2-1.8.x-server.json | 8 ++++---- app/config/specs/swagger2-latest-console.json | 8 ++++---- app/config/specs/swagger2-latest-server.json | 8 ++++---- .../Functions/Http/Deployments/Template/Create.php | 2 +- .../Modules/Sites/Http/Deployments/Template/Create.php | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index bdff664cbc..c58e0b87e9 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -12854,7 +12854,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -12875,7 +12875,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -30132,7 +30132,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -30153,7 +30153,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 6b766dbdee..786c103ba8 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -11635,7 +11635,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -11656,7 +11656,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -20838,7 +20838,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -20859,7 +20859,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index bdff664cbc..c58e0b87e9 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -12854,7 +12854,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -12875,7 +12875,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -30132,7 +30132,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -30153,7 +30153,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 6b766dbdee..786c103ba8 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -11635,7 +11635,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -11656,7 +11656,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -20838,7 +20838,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -20859,7 +20859,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index ee3702d27d..4c3abc01d2 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -12856,7 +12856,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -12873,7 +12873,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -30314,7 +30314,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -30331,7 +30331,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index ff5056b35a..ce45d12606 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -11662,7 +11662,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -11679,7 +11679,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -21063,7 +21063,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -21080,7 +21080,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index ee3702d27d..4c3abc01d2 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -12856,7 +12856,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -12873,7 +12873,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -30314,7 +30314,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -30331,7 +30331,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index ff5056b35a..ce45d12606 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -11662,7 +11662,7 @@ "tags": [ "functions" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -11679,7 +11679,7 @@ "cookies": false, "type": "", "demo": "functions\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/functions\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -21063,7 +21063,7 @@ "tags": [ "sites" ], - "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "description": "Create a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "responses": { "202": { "description": "Deployment", @@ -21080,7 +21080,7 @@ "cookies": false, "type": "", "demo": "sites\/create-template-deployment.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/products\/sites\/templates) to find the template details.", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index 4d93c8e8cd..bbe84c56ee 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -51,7 +51,7 @@ class Create extends Base description: << Date: Wed, 1 Oct 2025 18:17:35 +0530 Subject: [PATCH 188/274] fix: test file --- .../functions/log-error-truncation/index.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/resources/functions/log-error-truncation/index.js b/tests/resources/functions/log-error-truncation/index.js index 7e94fa5028..35747095f5 100644 --- a/tests/resources/functions/log-error-truncation/index.js +++ b/tests/resources/functions/log-error-truncation/index.js @@ -2,8 +2,21 @@ module.exports = async(context) => { // Create a string that is 1000001 characters long (exceeds the 1000000 limit) const longString = 'z' + 'a'.repeat(1000000); - context.log(longString); - context.error(longString); + // Split the string into chunks of 8000 characters (max limit for each log and error) + const chunkSize = 8000; + const chunks = []; + + for (let i = 0; i < longString.length; i += chunkSize) { + chunks.push(longString.slice(i, i + chunkSize)); + } + + chunks.forEach((chunk, index) => { + context.log(chunk); + }); + + chunks.forEach((chunk, index) => { + context.error(chunk); + }); return context.res.json({ motto: 'Build like a team of hundreds_', From 813bc0d9b331df653c3a9f5d41fc4bb465997909 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 1 Oct 2025 18:25:32 +0530 Subject: [PATCH 189/274] fix: wrong audit resource. --- .../Http/Databases/Collections/Documents/Logs/XList.php | 5 +---- .../Databases/Http/Databases/Collections/Logs/XList.php | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index 93c8639520..ed9378f644 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -108,10 +108,7 @@ class XList extends Action $audit = new Audit($dbForProject); $type = $this->getCollectionsEventsContext(); $context = $this->getContext(); - $resource = match ($context) { - ROWS => "database/$databaseId/grid/$type/$collectionId/$context/{$document->getId()}", - default => "database/$databaseId/$type/$collectionId/$context/{$document->getId()}", - }; + $resource = "database/$databaseId/$type/$collectionId/$context/{$document->getId()}"; $logs = $audit->getLogsByResource($resource, $queries); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 1309793234..fefc5ba5a0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -104,11 +104,7 @@ class XList extends Action $audit = new Audit($dbForProject); $context = $this->getContext(); - $resource = match ($context) { - TABLES => "database/$databaseId/grid/$context/$collectionId", - default => "database/$databaseId/$context/$collectionId", - }; - + $resource = "database/$databaseId/$context/$collectionId"; $logs = $audit->getLogsByResource($resource, $queries); $output = []; From 26ae33522df1159d9d1b6ab4135d6fbadf7f3bd0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 21:36:33 +1300 Subject: [PATCH 190/274] Fix txn read scope --- .../Modules/Databases/Http/Databases/Transactions/Get.php | 2 +- .../Modules/Databases/Http/Databases/Transactions/XList.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php index 43406a6751..1d4d22baa1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Get.php @@ -31,7 +31,7 @@ class Get extends Action ->setHttpPath('/v1/databases/transactions/:transactionId') ->desc('Get transaction') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php index 05ef5ae9e8..33c66b90c7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/XList.php @@ -34,7 +34,7 @@ class XList extends Action ->setHttpPath('/v1/databases/transactions') ->desc('List transactions') ->groups(['api', 'database', 'transactions']) - ->label('scope', 'transactions.read') + ->label('scope', 'rows.read') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('sdk', new Method( namespace: 'databases', From ae661e996157e98230a0bb9d2508cf4e2afb9057 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 21:37:54 +1300 Subject: [PATCH 191/274] Fix missing bulk map --- .../Modules/Databases/Http/Databases/Transactions/Update.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 9f5e5037bf..214052a173 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -545,7 +545,7 @@ class Update extends Action $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { $dbForProject->createDocuments( $collectionId, - $data, + \array_map(fn($doc) => new Document($doc), $data), onNext: function (Document $document) use (&$state, $collectionId) { $state[$collectionId][$document->getId()] = $document; } @@ -603,7 +603,7 @@ class Update extends Action // Run bulk upsert without timestamp wrapper, checking manually in callback $dbForProject->upsertDocuments( $collectionId, - $data, + \array_map(fn($doc) => new Document($doc), $data), onNext: function (Document $upserted, ?Document $old) use (&$state, $collectionId, $createdAt) { if ($old !== null) { // This is an update - check if document was created/modified in this transaction From 490341af090b1672fb7fa7c7fcdbfaf3c499d33e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 21:46:28 +1300 Subject: [PATCH 192/274] Fix log limit --- .../Modules/Databases/Http/Databases/Transactions/Update.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 214052a173..ac2efc31cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -133,6 +133,7 @@ class Update extends Action // Fetch operations ordered by sequence by default to replay operations in exact order they were created $operations = $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::limit(PHP_INT_MAX), ]); // Track transaction state for cross-operation visibility From f667149556f1530e0f01f56f2a52b25b657e1bc2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 22:12:02 +1300 Subject: [PATCH 193/274] Trigger CI From 473be9284d8c3fb0767fd0df8ced1681a12906bd Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 22:24:54 +1300 Subject: [PATCH 194/274] Check convert --- .../Http/Databases/Transactions/Update.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index ac2efc31cf..afa466c366 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -544,9 +544,14 @@ class Update extends Action array &$state ): void { $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { + // Convert data arrays to Document objects if needed + $documents = \array_map(function ($doc) { + return $doc instanceof Document ? $doc : new Document($doc); + }, $data); + $dbForProject->createDocuments( $collectionId, - \array_map(fn($doc) => new Document($doc), $data), + $documents, onNext: function (Document $document) use (&$state, $collectionId) { $state[$collectionId][$document->getId()] = $document; } @@ -601,10 +606,15 @@ class Update extends Action \DateTime $createdAt, array &$state ): void { + // Convert data arrays to Document objects if needed + $documents = \array_map(function ($doc) { + return $doc instanceof Document ? $doc : new Document($doc); + }, $data); + // Run bulk upsert without timestamp wrapper, checking manually in callback $dbForProject->upsertDocuments( $collectionId, - \array_map(fn($doc) => new Document($doc), $data), + $documents, onNext: function (Document $upserted, ?Document $old) use (&$state, $collectionId, $createdAt) { if ($old !== null) { // This is an update - check if document was created/modified in this transaction From ca1c069e65d38a269111cf8f29504a8fd1a61bf4 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 2 Oct 2025 14:58:55 +0530 Subject: [PATCH 195/274] Handle OIDC well-known endpoint errors --- src/Appwrite/Auth/OAuth2/Oidc.php | 3 + .../Account/AccountCustomClientTest.php | 71 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/Appwrite/Auth/OAuth2/Oidc.php b/src/Appwrite/Auth/OAuth2/Oidc.php index 670169fe89..c9810c48eb 100644 --- a/src/Appwrite/Auth/OAuth2/Oidc.php +++ b/src/Appwrite/Auth/OAuth2/Oidc.php @@ -273,6 +273,9 @@ class Oidc extends OAuth2 { if (empty($this->wellKnownConfiguration)) { $response = $this->request('GET', $this->getWellKnownEndpoint()); + if (empty($response)) { + throw new Exception('Invalid well-known configuration'); + } $this->wellKnownConfiguration = \json_decode($response, true); } diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index bd3fec8439..8322508cf6 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -1539,6 +1539,77 @@ class AccountCustomClientTest extends Scope return []; } + public function testCreateOidcOAuth2Token(): array + { + $provider = 'oidc'; + $appId = '1'; + + // Valid well-known configuration + $secret = '{ + "wellKnownEndpoint": "https://accounts.google.com/.well-known/openid-configuration", + "authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth", + "tokenEndpoint": "https://oauth2.googleapis.com/token", + "userinfoEndpoint": "https://openidconnect.googleapis.com/v1/userinfo" + }'; + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'provider' => $provider, + 'appId' => $appId, + 'secret' => $secret, + 'enabled' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/account/tokens/oauth2/' . $provider, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'provider' => $provider, + 'success' => 'http://localhost/v1/mock/tests/general/oauth2/success', + 'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure', + ], true, false); + + $this->assertEquals(301, $response['headers']['status-code']); + + // Invalid well-known configuration + $secret = '{}'; + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'provider' => $provider, + 'appId' => $appId, + 'secret' => $secret, + 'enabled' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/account/tokens/oauth2/' . $provider, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'provider' => $provider, + 'success' => 'http://localhost/v1/mock/tests/general/oauth2/success', + 'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure', + ]); + + $this->assertEquals(500, $response['headers']['status-code']); + + return []; + } + public function testBlockedAccount(): array { $email = uniqid() . 'user@localhost.test'; From 93d8f1cfe7f3eb66fc6c05b3236421090231978e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 23:03:52 +1300 Subject: [PATCH 196/274] Update src/Appwrite/Databases/TransactionState.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/Appwrite/Databases/TransactionState.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 1caae8c629..495a7b70ef 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -49,15 +49,15 @@ class TransactionState switch ($action) { case 'create': - if ($documentId) { - $state[$collectionId][$documentId] = [ + $docId = $documentId ?? ($data['$id'] ?? null); + if ($docId) { + $state[$collectionId][$docId] = [ 'action' => 'create', 'document' => new Document($data), 'exists' => true ]; } break; - case 'update': if (isset($state[$collectionId][$documentId])) { // Update existing document in transaction state From 9813b98133bc0682c195a8c6bc5f64329cfb056d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 23:05:43 +1300 Subject: [PATCH 197/274] Update src/Appwrite/Databases/TransactionState.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/Appwrite/Databases/TransactionState.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 495a7b70ef..c20eac3d1a 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -79,7 +79,9 @@ class TransactionState break; case 'upsert': - $state[$collectionId][$documentId] = [ + $docId = $documentId ?? ($data['$id'] ?? null); + if (!$docId) { break; } + $state[$collectionId][$docId] = [ 'action' => 'upsert', 'document' => new Document($data), 'exists' => true From 51d5ddb4637d0de70e21eb2ccc96842f40ad0415 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 23:17:17 +1300 Subject: [PATCH 198/274] Update src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Http/Databases/Collections/Documents/Bulk/Upsert.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 6123454a8b..e5ae97ba7c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -124,7 +124,7 @@ class Upsert extends Action if (($existing + 1) > $maxBatch) { throw new Exception( Exception::TRANSACTION_LIMIT_EXCEEDED, - 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch ); } From 6581d68e6d9daf40b7cbf44f065fdb64862ac477 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 23:20:02 +1300 Subject: [PATCH 199/274] Update src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Http/Databases/Collections/Documents/Bulk/Upsert.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index e5ae97ba7c..2c8d26020d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -144,7 +144,7 @@ class Upsert extends Action 'transactions', $transactionId, 'operations', - \count($staged) + 1 ); }); From a9a97b2fe0b735fa006d7f3d9bbd346be41f0709 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 23:20:22 +1300 Subject: [PATCH 200/274] Update src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Databases/Http/Databases/Collections/Documents/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 67a54068b8..902a3585ba 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -381,7 +381,7 @@ class Create extends Action if (($existing + 1) > $maxBatch) { throw new Exception( Exception::TRANSACTION_LIMIT_EXCEEDED, - 'Transaction already has ' . $existing . ' operations, adding ' . \count($documents) . ' would exceed the maximum of ' . $maxBatch + 'Transaction already has ' . $existing . ' operations, adding 1 would exceed the maximum of ' . $maxBatch ); } From 45da32d5d9129c8b0815d2ec0b7a82d2af9e82e2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 2 Oct 2025 23:57:30 +1300 Subject: [PATCH 201/274] Update src/Appwrite/Utopia/Database/Validator/Operation.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Utopia/Database/Validator/Operation.php | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index a8de32de79..5cc3ca2135 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -127,14 +127,25 @@ class Operation extends Validator } // If action requires documentId, it must be present - if ( - isset($this->requiresDocumentId[$value['action']]) && - !\array_key_exists($this->documentIdName, $value) - ) { +- if ( +- isset($this->requiresDocumentId[$value['action']]) && +- !\array_key_exists($this->documentIdName, $value) +- ) { +- $this->description = "Key '$this->documentIdName' is required for action '{$value['action']}'"; +- return false; + $actionRequiresDocumentId = ($this->requiresDocumentId[$value['action']] ?? false) === true; + if ($actionRequiresDocumentId && !\array_key_exists($this->documentIdName, $value)) { $this->description = "Key '$this->documentIdName' is required for action '{$value['action']}'"; return false; } + if (\array_key_exists($this->documentIdName, $value)) { + if (!\is_string($value[$this->documentIdName]) || \trim($value[$this->documentIdName]) === '') { + $this->description = "Key '$this->documentIdName' must be a non-empty string"; + return false; + } + } + // Data validation - only required for certain actions if (isset($this->requiresData[$value['action']]) && $this->requiresData[$value['action']]) { // Data is required for this action @@ -146,7 +157,7 @@ class Operation extends Validator $this->description = "Key 'data' must be an array"; return false; } - } elseif (\array_key_exists('data', $value)) { + } else if (\array_key_exists('data', $value)) { // Data is optional but if provided, must be an array if (!\is_array($value['data'])) { $this->description = "Key 'data' must be an array"; From 36a8365ebbea6457cb53ea57d18af34599244930 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:03:20 +1300 Subject: [PATCH 202/274] Count from state --- src/Appwrite/Databases/TransactionState.php | 265 +++++++++++++++++- .../Databases/Collections/Documents/XList.php | 2 +- 2 files changed, 258 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 1caae8c629..6d51356f89 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -18,6 +18,56 @@ class TransactionState $this->dbForProject = $dbForProject; } + /** + * Apply projection (select) semantics from queries to a document + */ + private function applyProjection(Document $doc, array $queries): Document + { + if (empty($queries)) { + return $doc; + } + + // Extract selections from queries + $selections = []; + foreach ($queries as $query) { + if ($query->getMethod() === Query::TYPE_SELECT) { + $values = $query->getValues(); + foreach ($values as $value) { + // Skip relationship selections (containing '.') + if (!\str_contains($value, '.')) { + $selections[] = $value; + } + } + } + } + + // If no selections or wildcard present, return document as-is + if (empty($selections) || \in_array('*', $selections)) { + return $doc; + } + + // Create a new document with only selected attributes + $projected = new Document(); + + // Always preserve internal attributes + $projected->setAttribute('$id', $doc->getId()); + $projected->setAttribute('$collection', $doc->getCollection()); + $projected->setAttribute('$createdAt', $doc->getCreatedAt()); + $projected->setAttribute('$updatedAt', $doc->getUpdatedAt()); + if ($doc->offsetExists('$permissions')) { + $projected->setAttribute('$permissions', $doc->getPermissions()); + } + + // Add selected attributes + foreach ($selections as $attribute) { + if ($doc->offsetExists($attribute)) { + $projected->setAttribute($attribute, $doc->getAttribute($attribute)); + } + } + + return $projected; + } + /** * Get the current state of a transaction by replaying its operations */ @@ -67,7 +117,11 @@ class TransactionState $existingDocument->setAttribute($key, $value); } } - $state[$collectionId][$documentId]['action'] = 'update'; + // Only set action to 'update' if it's not already 'create' or 'upsert' + $currentAction = $state[$collectionId][$documentId]['action']; + if ($currentAction !== 'create' && $currentAction !== 'upsert') { + $state[$collectionId][$documentId]['action'] = 'update'; + } } else { // Document doesn't exist in transaction state, will be merged with committed version $state[$collectionId][$documentId] = [ @@ -141,7 +195,7 @@ class TransactionState if ($docState['action'] === 'create') { // Document was created in transaction, return the created version - return $docState['document']; + return $this->applyProjection($docState['document'], $queries); } if ($docState['action'] === 'update' || $docState['action'] === 'upsert') { @@ -154,10 +208,12 @@ class TransactionState $committedDoc->setAttribute($key, $value); } } - return $committedDoc; + // committedDoc already has projection applied by dbForProject->getDocument() + // But we need to reapply in case transaction added new fields + return $this->applyProjection($committedDoc, $queries); } elseif ($docState['action'] === 'upsert') { // Upsert created a new document since committed doc doesn't exist - return $docState['document']; + return $this->applyProjection($docState['document'], $queries); } } } @@ -195,8 +251,8 @@ class TransactionState // Document was deleted, remove from results unset($documentMap[$docId]); } elseif ($docState['action'] === 'create') { - // Document was created, add to results - $documentMap[$docId] = $docState['document']; + // Document was created, add to results with projection + $documentMap[$docId] = $this->applyProjection($docState['document'], $queries); } elseif ($docState['action'] === 'update' || $docState['action'] === 'upsert') { if (isset($documentMap[$docId])) { // Update existing document @@ -205,9 +261,11 @@ class TransactionState $documentMap[$docId]->setAttribute($key, $value); } } + // Reapply projection in case transaction added new fields + $documentMap[$docId] = $this->applyProjection($documentMap[$docId], $queries); } elseif ($docState['action'] === 'upsert') { - // Upsert created a new document - $documentMap[$docId] = $docState['document']; + // Upsert created a new document, apply projection + $documentMap[$docId] = $this->applyProjection($docState['document'], $queries); } } } @@ -216,6 +274,197 @@ class TransactionState return array_values($documentMap); } + /** + * Count documents with transaction-aware logic + */ + public function countDocuments( + string $collectionId, + ?string $transactionId = null, + array $queries = [] + ): int { + // If no transaction, use normal database count + if ($transactionId === null) { + return $this->dbForProject->count($collectionId, $queries, APP_LIMIT_COUNT); + } + + $state = $this->getTransactionState($transactionId); + + // Get base count from database + $baseCount = $this->dbForProject->count($collectionId, $queries, APP_LIMIT_COUNT); + + // If no transaction state for this collection, return base count + if (!isset($state[$collectionId])) { + return $baseCount; + } + + // Build a set of committed document IDs that match the query + // We need to find which documents match the filters + $committedDocs = $this->dbForProject->find($collectionId, $queries); + $committedDocIds = []; + foreach ($committedDocs as $doc) { + $committedDocIds[$doc->getId()] = true; + } + + $adjustedCount = $baseCount; + + // Apply transaction state changes to the count + foreach ($state[$collectionId] as $docId => $docState) { + if (!$docState['exists']) { + // Document was deleted in transaction + if (isset($committedDocIds[$docId])) { + $adjustedCount--; // Was in results, now deleted + } + } elseif ($docState['action'] === 'create') { + // Document was created in transaction + // We need to check if it would match the query filters + // For now, we'll conservatively add it if no filters are present + // or apply basic filter matching + if ($this->documentMatchesFilters($docState['document'], $queries)) { + $adjustedCount++; // New document that matches + } + } elseif ($docState['action'] === 'update' || $docState['action'] === 'upsert') { + // Document was updated/upserted + $wasInResults = isset($committedDocIds[$docId]); + $nowMatches = $this->documentMatchesFilters($docState['document'], $queries); + + if (!$wasInResults && $nowMatches && $docState['action'] === 'upsert') { + $adjustedCount++; // Upsert created new document that matches + } elseif ($wasInResults && !$nowMatches) { + $adjustedCount--; // Update/upsert made document no longer match + } elseif (!$wasInResults && $nowMatches) { + // Update shouldn't add a new doc, but upsert might have + if ($docState['action'] === 'upsert') { + $adjustedCount++; + } + } + } + } + + return max(0, $adjustedCount); + } + + /** + * Check if a document matches filter queries (simplified implementation) + */ + private function documentMatchesFilters(Document $doc, array $queries): bool + { + // Extract filter queries + $filters = []; + foreach ($queries as $query) { + $method = $query->getMethod(); + // Only process filter queries, not limit/offset/cursor/select + if (!\in_array($method, [ + Query::TYPE_LIMIT, + Query::TYPE_OFFSET, + Query::TYPE_CURSOR_AFTER, + Query::TYPE_CURSOR_BEFORE, + Query::TYPE_SELECT, + Query::TYPE_ORDER_ASC, + Query::TYPE_ORDER_DESC + ])) { + $filters[] = $query; + } + } + + // If no filters, document matches + if (empty($filters)) { + return true; + } + + // Check each filter + foreach ($filters as $filter) { + $attribute = $filter->getAttribute(); + $values = $filter->getValues(); + $docValue = $doc->getAttribute($attribute); + + switch ($filter->getMethod()) { + case Query::TYPE_EQUAL: + if (!\in_array($docValue, $values)) { + return false; + } + break; + case Query::TYPE_NOT_EQUAL: + if (\in_array($docValue, $values)) { + return false; + } + break; + case Query::TYPE_CONTAINS: + $matches = false; + foreach ($values as $value) { + if (\is_array($docValue) && \in_array($value, $docValue)) { + $matches = true; + break; + } + } + if (!$matches) { + return false; + } + break; + case Query::TYPE_STARTS_WITH: + $matches = false; + foreach ($values as $value) { + if (\is_string($docValue) && \str_starts_with($docValue, $value)) { + $matches = true; + break; + } + } + if (!$matches) { + return false; + } + break; + case Query::TYPE_ENDS_WITH: + $matches = false; + foreach ($values as $value) { + if (\is_string($docValue) && \str_ends_with($docValue, $value)) { + $matches = true; + break; + } + } + if (!$matches) { + return false; + } + break; + case Query::TYPE_GREATER_THAN: + if (!($docValue > $values[0])) { + return false; + } + break; + case Query::TYPE_GREATER_THAN_EQUAL: + if (!($docValue >= $values[0])) { + return false; + } + break; + case Query::TYPE_LESSER_THAN: + if (!($docValue < $values[0])) { + return false; + } + break; + case Query::TYPE_LESSER_THAN_EQUAL: + if (!($docValue <= $values[0])) { + return false; + } + break; + case Query::TYPE_IS_NULL: + if (!\is_null($docValue)) { + return false; + } + break; + case Query::TYPE_IS_NOT_NULL: + if (\is_null($docValue)) { + return false; + } + break; + case Query::TYPE_BETWEEN: + if (!($docValue >= $values[0] && $docValue <= $values[1])) { + return false; + } + break; + } + } + + return true; + } + /** * Check if a document exists with transaction-aware logic */ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 26d315e52e..a30fed47ed 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -129,7 +129,7 @@ class XList extends Action // Use transaction-aware document retrieval if transactionId is provided if ($transactionId !== null) { $documents = $transactionState->listDocuments($collectionTableId, $transactionId, $queries); - $total = count($documents); // For transaction-aware queries, we count the actual results + $total = $transactionState->countDocuments($collectionTableId, $transactionId, $queries); } elseif (! empty($selectQueries)) { // has selects, allow relationship on documents $documents = $dbForProject->find($collectionTableId, $queries); From 7fd2502dd5cc28364be986c4bf2e8c89f954579c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:03:34 +1300 Subject: [PATCH 203/274] Block client bulk upsert txn --- .../Databases/Http/Databases/Transactions/Operations/Create.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 0aca3e3f7c..20cf79551c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -87,6 +87,7 @@ class Create extends Action if (!$isAPIKey && !$isPrivilegedUser && \in_array($operation['action'], [ 'bulkCreate', 'bulkUpdate', + 'bulkUpsert', 'bulkDelete' ])) { throw new Exception(Exception::USER_UNAUTHORIZED); From f4830b1672f624b7e5ad3738c2e3859b2b4140be Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:03:46 +1300 Subject: [PATCH 204/274] Catch query exception --- .../Databases/Http/Databases/Transactions/Update.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index afa466c366..2513c09c64 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -18,6 +18,7 @@ use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Query; @@ -227,6 +228,11 @@ class Update extends Action 'status' => 'failed', ])); throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); + } catch (QueryException $e) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } }); From 5da2ca292af649ac6034bd8e9c26a16ce84e3847 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:03:54 +1300 Subject: [PATCH 205/274] Add missing injection --- .../Modules/Databases/Http/TablesDB/Transactions/Delete.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php index 28f273f566..46cd6b6c51 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Delete.php @@ -49,6 +49,7 @@ class Delete extends TransactionsDelete ->param('transactionId', '', new UID(), 'Transaction ID.') ->inject('response') ->inject('dbForProject') + ->inject('queueForDeletes') ->callback($this->action(...)); } } From d54058b59ade2721520beda1d8b809d2d36a7a29 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:12:19 +1300 Subject: [PATCH 206/274] Fix multi API event --- .../Databases/Http/Databases/Transactions/Update.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 2513c09c64..37642a0cca 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -266,12 +266,18 @@ class Update extends Action Query::equal('$sequence', [$collectionInternalId]) ]); + $groupId = $this->getGroupId(); + $resourceId = $this->getResourceId(); + $contextKey = $this->getContext(); + $resource = $this->getResource(); + $resourcePlural = $resource . 's'; + $queueForEvents ->setParam('databaseId', $database->getId()) ->setContext('database', $database) ->setParam('collectionId', $collection->getId()) ->setParam('tableId', $collection->getId()) - ->setContext('collection', $collection); + ->setContext($contextKey, $collection); $eventAction = ''; $documents = []; @@ -308,7 +314,7 @@ class Update extends Action $queueForEvents ->setParam('documentId', $docId) ->setParam('rowId', $docId) - ->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].' . $eventAction); + ->setEvent("databases.[databaseId].{$contextKey}s.[{$groupId}].{$resourcePlural}.[{$resourceId}]." . $eventAction); $queueForRealtime->from($queueForEvents)->trigger(); $queueForFunctions->from($queueForEvents)->trigger(); From 44e7068a9a0fc49f15a666b00b4975eb193a4941 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:26:39 +1300 Subject: [PATCH 207/274] Fix syntax --- src/Appwrite/Utopia/Database/Validator/Operation.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 5cc3ca2135..911250c2bc 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -127,12 +127,6 @@ class Operation extends Validator } // If action requires documentId, it must be present -- if ( -- isset($this->requiresDocumentId[$value['action']]) && -- !\array_key_exists($this->documentIdName, $value) -- ) { -- $this->description = "Key '$this->documentIdName' is required for action '{$value['action']}'"; -- return false; $actionRequiresDocumentId = ($this->requiresDocumentId[$value['action']] ?? false) === true; if ($actionRequiresDocumentId && !\array_key_exists($this->documentIdName, $value)) { $this->description = "Key '$this->documentIdName' is required for action '{$value['action']}'"; From 522a4d2a62ab781526a8df9ba442519fcb4914e3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 00:39:14 +1300 Subject: [PATCH 208/274] Format --- src/Appwrite/Databases/TransactionState.php | 4 +++- .../Http/Databases/Collections/Documents/Bulk/Upsert.php | 2 +- src/Appwrite/Utopia/Database/Validator/Operation.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index f37366f72c..1f8e7f65c8 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -134,7 +134,9 @@ class TransactionState case 'upsert': $docId = $documentId ?? ($data['$id'] ?? null); - if (!$docId) { break; } + if (!$docId) { + break; + } $state[$collectionId][$docId] = [ 'action' => 'upsert', 'document' => new Document($data), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 2c8d26020d..a2156484a8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -144,7 +144,7 @@ class Upsert extends Action 'transactions', $transactionId, 'operations', - 1 + 1 ); }); diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 911250c2bc..2e3dc8e9e0 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -151,7 +151,7 @@ class Operation extends Validator $this->description = "Key 'data' must be an array"; return false; } - } else if (\array_key_exists('data', $value)) { + } elseif (\array_key_exists('data', $value)) { // Data is optional but if provided, must be an array if (!\is_array($value['data'])) { $this->description = "Key 'data' must be an array"; From 76d3dc3d5a12e8d4223ba82ad27d98de15980e3c Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Thu, 2 Oct 2025 18:27:20 +0530 Subject: [PATCH 209/274] * added senstive var copying in `from` method * added tests --- src/Appwrite/Event/Event.php | 1 + .../Realtime/RealtimeCustomClientTest.php | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index e9f3ccc2a2..16fe76bf8a 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -592,6 +592,7 @@ class Event $this->project = $event->getProject(); $this->user = $event->getUser(); $this->payload = $event->getPayload(); + $this->sensitive = $event->sensitive; $this->event = $event->getEvent(); $this->params = $event->getParams(); $this->context = $event->context; diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 3e57c5e9bc..ef39908658 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -2507,4 +2507,155 @@ class RealtimeCustomClientTest extends Scope $client->close(); } + + public function testRelationshipPayloadHidesRelatedDoc() + { + $user = $this->getUser(); + $session = $user['session'] ?? ''; + $projectId = $this->getProject()['$id']; + + $client = $this->getWebsocket(['documents'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $projectId . '=' . $session + ]); + + $response = json_decode($client->receive(), true); + $this->assertArrayHasKey('type', $response); + $this->assertEquals('connected', $response['type']); + + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'db-rel' + ]); + $databaseId = $database['body']['$id']; + + $level1 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'level1', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + $level1Id = $level1['body']['$id']; + + $level2 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'level2', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + ]); + $level2Id = $level2['body']['$id']; + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => false, + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level2Id}/attributes/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => false, + ]); + + sleep(2); + + // two-way one-to-one relationship from level1 to level2 + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/attributes/relationship", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'relatedCollectionId' => $level2Id, + 'type' => 'oneToOne', + 'twoWay' => true, + 'key' => 'level2Ref', + 'onDelete' => 'cascade', + ]); + + sleep(2); + + $doc2 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level2Id}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ 'name' => 'L2' ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $doc2Id = $doc2['body']['$id']; + + $doc1 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ 'name' => 'L1' ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $doc1Id = $doc1['body']['$id']; + + json_decode($client->receive(), true); + + $this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$level1Id}/documents/{$doc1Id}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'level2Ref' => $doc2Id, + ], + ]); + + // payload should not contain the relationship attribute 'level2Ref' + $event = json_decode($client->receive(), true); + $this->assertArrayHasKey('type', $event); + $this->assertEquals('event', $event['type']); + $this->assertArrayHasKey('data', $event); + $this->assertNotEmpty($event['data']); + $this->assertArrayHasKey('payload', $event['data']); + $this->assertArrayHasKey('$id', $event['data']['payload']); + $this->assertEquals($doc1Id, $event['data']['payload']['$id']); + $this->assertArrayNotHasKey('level2Ref', $event['data']['payload']); + + $client->close(); + } } From 770c58d33c501e4d3d870749bdc074113fd386fe Mon Sep 17 00:00:00 2001 From: Priyanshu Thapliyal <114170980+Priyanshuthapliyal2005@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:28:35 +0000 Subject: [PATCH 210/274] fix(docs): replace broken table creation links with correct references --- app/config/specs/open-api3-1.8.x-client.json | 16 +++--- app/config/specs/open-api3-1.8.x-console.json | 54 +++++++++---------- app/config/specs/open-api3-1.8.x-server.json | 54 +++++++++---------- app/config/specs/open-api3-latest-client.json | 16 +++--- .../specs/open-api3-latest-console.json | 54 +++++++++---------- app/config/specs/open-api3-latest-server.json | 54 +++++++++---------- app/config/specs/swagger2-1.8.x-client.json | 16 +++--- app/config/specs/swagger2-1.8.x-console.json | 54 +++++++++---------- app/config/specs/swagger2-1.8.x-server.json | 54 +++++++++---------- app/config/specs/swagger2-latest-client.json | 16 +++--- app/config/specs/swagger2-latest-console.json | 54 +++++++++---------- app/config/specs/swagger2-latest-server.json | 54 +++++++++---------- docs/references/tablesdb/create-row.md | 2 +- docs/references/tablesdb/create-rows.md | 2 +- docs/references/tablesdb/create-table.md | 2 +- docs/references/tablesdb/upsert-row.md | 2 +- docs/references/tablesdb/upsert-rows.md | 2 +- .../Tables/Columns/Boolean/Create.php | 2 +- .../Tables/Columns/Boolean/Update.php | 2 +- .../TablesDB/Tables/Columns/Line/Create.php | 2 +- .../TablesDB/Tables/Columns/Line/Update.php | 2 +- .../TablesDB/Tables/Columns/Point/Create.php | 2 +- .../TablesDB/Tables/Columns/Point/Update.php | 2 +- .../Tables/Columns/Polygon/Create.php | 2 +- .../Tables/Columns/Polygon/Update.php | 2 +- .../TablesDB/Tables/Columns/String/Create.php | 2 +- .../TablesDB/Tables/Columns/String/Update.php | 2 +- .../Http/TablesDB/Tables/Indexes/Create.php | 2 +- .../Http/TablesDB/Tables/Indexes/Delete.php | 2 +- .../Http/TablesDB/Tables/Indexes/Get.php | 2 +- .../Http/TablesDB/Tables/Indexes/XList.php | 2 +- .../Http/TablesDB/Tables/Rows/Bulk/Delete.php | 2 +- .../Http/TablesDB/Tables/Rows/Create.php | 2 +- .../Http/TablesDB/Tables/Rows/Delete.php | 2 +- .../Http/TablesDB/Tables/Rows/Get.php | 2 +- .../Http/TablesDB/Tables/Rows/XList.php | 2 +- 36 files changed, 272 insertions(+), 272 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index d57b9f83b2..2038c1940a 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -7533,7 +7533,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", @@ -7562,7 +7562,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7624,7 +7624,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" } ], @@ -7652,7 +7652,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "schema": { "type": "string", @@ -7766,7 +7766,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -7805,7 +7805,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7866,7 +7866,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -8105,7 +8105,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index bdff664cbc..e0eccd3f69 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -33219,7 +33219,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -33717,7 +33717,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -33826,7 +33826,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35336,7 +35336,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35448,7 +35448,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35568,7 +35568,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35680,7 +35680,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35800,7 +35800,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35912,7 +35912,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -36166,7 +36166,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -36286,7 +36286,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -36927,7 +36927,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37012,7 +37012,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37144,7 +37144,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37218,7 +37218,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37390,7 +37390,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", @@ -37419,7 +37419,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -37481,7 +37481,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -37507,7 +37507,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -37535,7 +37535,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "schema": { "type": "string", @@ -37588,7 +37588,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -37646,7 +37646,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -37865,7 +37865,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37961,7 +37961,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -38000,7 +38000,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -38061,7 +38061,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -38300,7 +38300,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 6b766dbdee..74be46d8d3 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -23633,7 +23633,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -24137,7 +24137,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -24247,7 +24247,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -25770,7 +25770,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -25883,7 +25883,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26004,7 +26004,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26117,7 +26117,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26238,7 +26238,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26351,7 +26351,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26607,7 +26607,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26728,7 +26728,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27375,7 +27375,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27461,7 +27461,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27594,7 +27594,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27669,7 +27669,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27757,7 +27757,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", @@ -27786,7 +27786,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -27849,7 +27849,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -27876,7 +27876,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -27906,7 +27906,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "schema": { "type": "string", @@ -27959,7 +27959,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -28018,7 +28018,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -28240,7 +28240,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -28338,7 +28338,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -28377,7 +28377,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -28439,7 +28439,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -28684,7 +28684,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index d57b9f83b2..2038c1940a 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -7533,7 +7533,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", @@ -7562,7 +7562,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7624,7 +7624,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" } ], @@ -7652,7 +7652,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "schema": { "type": "string", @@ -7766,7 +7766,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -7805,7 +7805,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7866,7 +7866,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -8105,7 +8105,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index bdff664cbc..e0eccd3f69 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -33219,7 +33219,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -33717,7 +33717,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -33826,7 +33826,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35336,7 +35336,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35448,7 +35448,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35568,7 +35568,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35680,7 +35680,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35800,7 +35800,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -35912,7 +35912,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -36166,7 +36166,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -36286,7 +36286,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -36927,7 +36927,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37012,7 +37012,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37144,7 +37144,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37218,7 +37218,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37390,7 +37390,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", @@ -37419,7 +37419,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -37481,7 +37481,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -37507,7 +37507,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -37535,7 +37535,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "schema": { "type": "string", @@ -37588,7 +37588,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -37646,7 +37646,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -37865,7 +37865,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37961,7 +37961,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -38000,7 +38000,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -38061,7 +38061,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -38300,7 +38300,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 6b766dbdee..74be46d8d3 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -23633,7 +23633,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -24137,7 +24137,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -24247,7 +24247,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -25770,7 +25770,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -25883,7 +25883,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26004,7 +26004,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26117,7 +26117,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26238,7 +26238,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26351,7 +26351,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26607,7 +26607,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -26728,7 +26728,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27375,7 +27375,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27461,7 +27461,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27594,7 +27594,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27669,7 +27669,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27757,7 +27757,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", @@ -27786,7 +27786,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -27849,7 +27849,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -27876,7 +27876,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -27906,7 +27906,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "schema": { "type": "string", @@ -27959,7 +27959,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -28018,7 +28018,7 @@ "model": "#\/components\/schemas\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -28240,7 +28240,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -28338,7 +28338,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -28377,7 +28377,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -28439,7 +28439,7 @@ "model": "#\/components\/schemas\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -28684,7 +28684,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index c2628533d0..3003de22af 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -7612,7 +7612,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", @@ -7644,7 +7644,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7701,7 +7701,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" } ], @@ -7727,7 +7727,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "type": "string", "x-example": "", @@ -7838,7 +7838,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -7878,7 +7878,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7934,7 +7934,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -8163,7 +8163,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index ee3702d27d..f222d3dff6 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -33348,7 +33348,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -33834,7 +33834,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -33943,7 +33943,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35442,7 +35442,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35546,7 +35546,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35656,7 +35656,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35760,7 +35760,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35870,7 +35870,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35974,7 +35974,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -36220,7 +36220,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -36342,7 +36342,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -36964,7 +36964,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37046,7 +37046,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37177,7 +37177,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37249,7 +37249,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37409,7 +37409,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", @@ -37441,7 +37441,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -37498,7 +37498,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -37524,7 +37524,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -37550,7 +37550,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "type": "string", "x-example": "", @@ -37609,7 +37609,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -37663,7 +37663,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -37875,7 +37875,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37965,7 +37965,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -38005,7 +38005,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -38061,7 +38061,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -38290,7 +38290,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index ff5056b35a..db04d55cd0 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -23817,7 +23817,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -24309,7 +24309,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -24419,7 +24419,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -25931,7 +25931,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26036,7 +26036,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26147,7 +26147,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26252,7 +26252,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26363,7 +26363,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26468,7 +26468,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26716,7 +26716,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26839,7 +26839,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27467,7 +27467,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27550,7 +27550,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27682,7 +27682,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27755,7 +27755,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27836,7 +27836,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", @@ -27868,7 +27868,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -27926,7 +27926,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -27953,7 +27953,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -27981,7 +27981,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "type": "string", "x-example": "", @@ -28040,7 +28040,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -28095,7 +28095,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -28310,7 +28310,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -28402,7 +28402,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -28442,7 +28442,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -28499,7 +28499,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -28734,7 +28734,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index c2628533d0..3003de22af 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -7612,7 +7612,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", @@ -7644,7 +7644,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7701,7 +7701,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" } ], @@ -7727,7 +7727,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "type": "string", "x-example": "", @@ -7838,7 +7838,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -7878,7 +7878,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -7934,7 +7934,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -8163,7 +8163,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index ee3702d27d..f222d3dff6 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -33348,7 +33348,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -33834,7 +33834,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -33943,7 +33943,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35442,7 +35442,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35546,7 +35546,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35656,7 +35656,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35760,7 +35760,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35870,7 +35870,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -35974,7 +35974,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -36220,7 +36220,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -36342,7 +36342,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -36964,7 +36964,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37046,7 +37046,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37177,7 +37177,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37249,7 +37249,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37409,7 +37409,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", @@ -37441,7 +37441,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -37498,7 +37498,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -37524,7 +37524,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -37550,7 +37550,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "type": "string", "x-example": "", @@ -37609,7 +37609,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -37663,7 +37663,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -37875,7 +37875,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37965,7 +37965,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -38005,7 +38005,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -38061,7 +38061,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -38290,7 +38290,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index ff5056b35a..db04d55cd0 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -23817,7 +23817,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Table", @@ -24309,7 +24309,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -24419,7 +24419,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -25931,7 +25931,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26036,7 +26036,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26147,7 +26147,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26252,7 +26252,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26363,7 +26363,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26468,7 +26468,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26716,7 +26716,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -26839,7 +26839,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27467,7 +27467,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27550,7 +27550,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27682,7 +27682,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27755,7 +27755,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27836,7 +27836,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdbdb#tablesdbCreate).", + "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", @@ -27868,7 +27868,7 @@ "tags": [ "tablesDB" ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -27926,7 +27926,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-row.md" }, { @@ -27953,7 +27953,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/create-rows.md" } ], @@ -27981,7 +27981,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable). Make sure to define columns before creating rows.", "required": true, "type": "string", "x-example": "", @@ -28040,7 +28040,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "responses": { "201": { "description": "Rows List", @@ -28095,7 +28095,7 @@ "model": "#\/definitions\/rowList" } ], - "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.\n", + "description": "Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.\n", "demo": "tablesdb\/upsert-rows.md" } ], @@ -28310,7 +28310,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -28402,7 +28402,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -28442,7 +28442,7 @@ "tags": [ "tablesDB" ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "responses": { "201": { "description": "Row", @@ -28499,7 +28499,7 @@ "model": "#\/definitions\/row" } ], - "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreateTable) API or directly from your database console.", + "description": "Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable) API or directly from your database console.", "demo": "tablesdb\/upsert-row.md" } ], @@ -28734,7 +28734,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/server\/tablesdb#tablesDBCreate).", + "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", diff --git a/docs/references/tablesdb/create-row.md b/docs/references/tablesdb/create-row.md index 9b25555eeb..2ddae7e5a7 100644 --- a/docs/references/tablesdb/create-row.md +++ b/docs/references/tablesdb/create-row.md @@ -1 +1 @@ -Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreateTable) API or directly from your database console. \ No newline at end of file +Create a new Row. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. \ No newline at end of file diff --git a/docs/references/tablesdb/create-rows.md b/docs/references/tablesdb/create-rows.md index 6a19d7bc9f..b8b5e93582 100644 --- a/docs/references/tablesdb/create-rows.md +++ b/docs/references/tablesdb/create-rows.md @@ -1 +1 @@ -Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreateTable) API or directly from your database console. \ No newline at end of file +Create new Rows. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. \ No newline at end of file diff --git a/docs/references/tablesdb/create-table.md b/docs/references/tablesdb/create-table.md index 006e8794df..c240440bf2 100644 --- a/docs/references/tablesdb/create-table.md +++ b/docs/references/tablesdb/create-table.md @@ -1 +1 @@ -Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreateTable) API or directly from your database console. \ No newline at end of file +Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. \ No newline at end of file diff --git a/docs/references/tablesdb/upsert-row.md b/docs/references/tablesdb/upsert-row.md index 26edd7ad04..2ae62fedc9 100644 --- a/docs/references/tablesdb/upsert-row.md +++ b/docs/references/tablesdb/upsert-row.md @@ -1 +1 @@ -Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreateTable) API or directly from your database console. \ No newline at end of file +Create or update a Row. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. \ No newline at end of file diff --git a/docs/references/tablesdb/upsert-rows.md b/docs/references/tablesdb/upsert-rows.md index f980d8c30d..52f9d29398 100644 --- a/docs/references/tablesdb/upsert-rows.md +++ b/docs/references/tablesdb/upsert-rows.md @@ -1 +1 @@ -Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreateTable) API or directly from your database console. +Create or update Rows. Before using this route, you should create a new table resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php index 5222d2e133..bb4bef35e8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php @@ -50,7 +50,7 @@ class Create extends BooleanCreate ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Boolean(), 'Default value for column when not provided. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php index 3c6ef50813..2d8789e394 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php @@ -53,7 +53,7 @@ class Update extends BooleanUpdate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php index f60d4dd5b8..9903ad3a7e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Create.php @@ -53,7 +53,7 @@ class Create extends LineCreate ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php index 19c6df202d..04b514afb8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php @@ -55,7 +55,7 @@ class Update extends LineUpdate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php index 47d97e8077..ed95e4629c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Create.php @@ -53,7 +53,7 @@ class Create extends PointCreate ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php index 2e98bf2cf9..f29e5bb27a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php @@ -55,7 +55,7 @@ class Update extends PointUpdate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php index 371d5f8fd5..c50c5acd5c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Create.php @@ -53,7 +53,7 @@ class Create extends PolygonCreate ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php index c5654b77d4..9f689717c3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php @@ -55,7 +55,7 @@ class Update extends PolygonUpdate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required.', true) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php index 14f0c8321e..efe7dd513f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php @@ -53,7 +53,7 @@ class Create extends StringCreate ] )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Column size for text columns, in number of characters.') ->param('required', null, new Boolean(), 'Is column required?') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php index fc45557f3b..7ba813aedd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php @@ -56,7 +56,7 @@ class Update extends StringUpdate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php index a2a5c8b453..fdbe91f5f7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php @@ -56,7 +56,7 @@ class Create extends IndexCreate contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', null, new Key(), 'Index Key.') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL]), 'Index type.') ->param('columns', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of columns to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' columns are allowed, each 32 characters long.') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php index 586bad78f4..dd97c4bdc7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php @@ -55,7 +55,7 @@ class Delete extends IndexDelete contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php index 3f2978b547..907b396072 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Get.php @@ -48,7 +48,7 @@ class Get extends IndexGet contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php index c275fd2771..835e543d07 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/XList.php @@ -48,7 +48,7 @@ class XList extends IndexXList contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following columns: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php index c9729d714d..a99316394a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php @@ -54,7 +54,7 @@ class Delete extends DocumentsDelete contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php index cf38bac63b..a39d9adbda 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php @@ -94,7 +94,7 @@ class Create extends DocumentCreate ]) ->param('databaseId', '', new UID(), 'Database ID.') ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate). Make sure to define columns before creating rows.') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable). Make sure to define columns before creating rows.') ->param('data', [], new JSON(), 'Row data as JSON object.', true, example: '{"username":"walter.obrien","email":"walter.obrien@example.com","fullName":"Walter O\'Brien","age":30,"isAdmin":false}') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of rows data as JSON objects.', true, ['plan']) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php index 7ac954c5dd..72f4ef3d94 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php @@ -59,7 +59,7 @@ class Delete extends DocumentDelete contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('rowId', '', new UID(), 'Row ID.') ->inject('requestTimestamp') ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php index 5704f75d82..faf1584d0d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php @@ -49,7 +49,7 @@ class Get extends DocumentGet contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/tablesdb#tablesDBCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('rowId', '', new UID(), 'Row ID.') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php index 5d503f1c59..cbbbf9520d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php @@ -49,7 +49,7 @@ class XList extends DocumentXList contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TableDB service [server integration](https://appwrite.io/docs/server/tablesdbdb#tablesdbCreate).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TableDB service [server integration](https://appwrite.io/docs/products/databases/tables#create-table).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('response') ->inject('dbForProject') From a61385e5340f24cf2a12f1b97dbb21141ca65ab0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 3 Oct 2025 08:43:54 +0530 Subject: [PATCH 211/274] update versions --- app/config/platforms.php | 18 +++++++++--------- src/Appwrite/Platform/Tasks/SDKs.php | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 5b42381da3..fff382df3d 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '20.2.0', + 'version' => '21.0.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '19.2.0', + 'version' => '20s.0.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '12.2.0', + 'version' => '13.0.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '10.2.0', + 'version' => '11.0.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '19.2.0', + 'version' => '20.0.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '18.2.0', + 'version' => '19.0.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '18.2.0', + 'version' => '19.0.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '11.2.0', + 'version' => '12.0.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '12.2.0', + 'version' => '13.0.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 90aa07f726..37ed2c9f3b 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -318,7 +318,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND '); Console::success("Pushed {$language['name']} SDK to {$gitUrl}"); - if ($createPr) { $prTitle = "feat: {$language['name']} SDK update for version {$language['version']}"; $prBody = "This PR contains updates to the {$language['name']} SDK for version {$language['version']}."; @@ -353,7 +352,25 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND } else { $errorMessage = implode("\n", $prOutput); if (strpos($errorMessage, 'already exists') !== false) { - Console::warning("Pull request already exists for {$language['name']} SDK"); + Console::warning("Pull request already exists for {$language['name']} SDK, updating title and body..."); + + $updateCommand = 'cd ' . $target . ' && \ + gh pr edit "' . $gitBranch . '" \ + --repo "' . $repoName . '" \ + --title "' . $prTitle . '" \ + --body "' . $prBody . '" \ + 2>&1'; + + $updateOutput = []; + $updateReturnCode = 0; + \exec($updateCommand, $updateOutput, $updateReturnCode); + + if ($updateReturnCode === 0) { + Console::success("Successfully updated pull request for {$language['name']} SDK"); + } else { + $updateErrorMessage = implode("\n", $updateOutput); + Console::error("Failed to update pull request for {$language['name']} SDK: " . $updateErrorMessage); + } } else { Console::error("Failed to create pull request for {$language['name']} SDK: " . $errorMessage); } From 848b932ea1c94d89663a4893b0f365e8c80385da Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 3 Oct 2025 08:46:25 +0530 Subject: [PATCH 212/274] fix flutter version --- app/config/platforms.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index fff382df3d..aeed725bf7 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '20s.0.0', + 'version' => '21.0.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, From 71b2164154ae844a6c4bbe024da2c03f8689ae0e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 16:58:23 +1300 Subject: [PATCH 213/274] Clean up state helper --- src/Appwrite/Databases/TransactionState.php | 484 +++++++++++------- .../Http/Databases/Transactions/Update.php | 117 ++++- 2 files changed, 423 insertions(+), 178 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 1f8e7f65c8..85ecb41ae2 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -8,6 +8,11 @@ use Utopia\Database\Query; /** * Service for managing transaction state and providing transaction-aware document operations + * + * This class provides methods to: + * - Query documents with transaction awareness (getDocument, listDocuments, countDocuments) + * - Apply bulk operations to transaction state for cross-operation visibility + * - Replay transaction operations to build current state */ class TransactionState { @@ -18,161 +23,15 @@ class TransactionState $this->dbForProject = $dbForProject; } - /** - * Apply projection (select) semantics from queries to a document - */ - private function applyProjection(Document $doc, array $queries): Document - { - if (empty($queries)) { - return $doc; - } - - // Extract selections from queries - $selections = []; - foreach ($queries as $query) { - if ($query->getMethod() === Query::TYPE_SELECT) { - $values = $query->getValues(); - foreach ($values as $value) { - // Skip relationship selections (containing '.') - if (!\str_contains($value, '.')) { - $selections[] = $value; - } - } - } - } - - // If no selections or wildcard present, return document as-is - if (empty($selections) || \in_array('*', $selections)) { - return $doc; - } - - // Create a new document with only selected attributes - $projected = new Document(); - - // Always preserve internal attributes - $projected->setAttribute('$id', $doc->getId()); - $projected->setAttribute('$collection', $doc->getCollection()); - $projected->setAttribute('$createdAt', $doc->getCreatedAt()); - $projected->setAttribute('$updatedAt', $doc->getUpdatedAt()); - if ($doc->offsetExists('$permissions')) { - $projected->setAttribute('$permissions', $doc->getPermissions()); - } - - // Add selected attributes - foreach ($selections as $attribute) { - if ($doc->offsetExists($attribute)) { - $projected->setAttribute($attribute, $doc->getAttribute($attribute)); - } - } - - return $projected; - } - - /** - * Get the current state of a transaction by replaying its operations - */ - private function getTransactionState(string $transactionId): array - { - $transaction = $this->dbForProject->getDocument('transactions', $transactionId); - if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') { - return []; - } - - // Fetch operations ordered by sequence to replay in exact order - $operations = $this->dbForProject->find('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - ]); - - $state = []; - - foreach ($operations as $operation) { - $databaseInternalId = $operation['databaseInternalId']; - $collectionInternalId = $operation['collectionInternalId']; - $collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; - $documentId = $operation['documentId']; - $action = $operation['action']; - $data = $operation['data']; - - if ($data instanceof Document) { - $data = $data->getArrayCopy(); - } - - switch ($action) { - case 'create': - $docId = $documentId ?? ($data['$id'] ?? null); - if ($docId) { - $state[$collectionId][$docId] = [ - 'action' => 'create', - 'document' => new Document($data), - 'exists' => true - ]; - } - break; - case 'update': - if (isset($state[$collectionId][$documentId])) { - // Update existing document in transaction state - $existingDocument = $state[$collectionId][$documentId]['document']; - foreach ($data as $key => $value) { - if ($key !== '$id') { - $existingDocument->setAttribute($key, $value); - } - } - // Only set action to 'update' if it's not already 'create' or 'upsert' - $currentAction = $state[$collectionId][$documentId]['action']; - if ($currentAction !== 'create' && $currentAction !== 'upsert') { - $state[$collectionId][$documentId]['action'] = 'update'; - } - } else { - // Document doesn't exist in transaction state, will be merged with committed version - $state[$collectionId][$documentId] = [ - 'action' => 'update', - 'document' => new Document($data), - 'exists' => true - ]; - } - break; - - case 'upsert': - $docId = $documentId ?? ($data['$id'] ?? null); - if (!$docId) { - break; - } - $state[$collectionId][$docId] = [ - 'action' => 'upsert', - 'document' => new Document($data), - 'exists' => true - ]; - break; - - case 'delete': - $state[$collectionId][$documentId] = [ - 'action' => 'delete', - 'exists' => false - ]; - break; - - case 'bulkCreate': - if (is_array($data)) { - foreach ($data as $doc) { - if ($doc instanceof Document) { - $doc = $doc->getArrayCopy(); - } - $state[$collectionId][$doc['$id']] = [ - 'action' => 'create', - 'document' => new Document($doc), - 'exists' => true - ]; - } - } - break; - } - } - - return $state; - } /** * Get a document with transaction-aware logic + * + * @param string $collectionId Collection ID + * @param string $documentId Document ID + * @param string|null $transactionId Optional transaction ID + * @param array $queries Optional query filters + * @return Document */ public function getDocument( string $collectionId, @@ -187,7 +46,6 @@ class TransactionState $state = $this->getTransactionState($transactionId); - // Check if document exists in transaction state if (isset($state[$collectionId][$documentId])) { $docState = $state[$collectionId][$documentId]; @@ -212,8 +70,7 @@ class TransactionState $committedDoc->setAttribute($key, $value); } } - // committedDoc already has projection applied by dbForProject->getDocument() - // But we need to reapply in case transaction added new fields + // Reapply projection in case transaction added new fields return $this->applyProjection($committedDoc, $queries); } elseif ($docState['action'] === 'upsert') { // Upsert created a new document since committed doc doesn't exist @@ -228,6 +85,11 @@ class TransactionState /** * List documents with transaction-aware logic + * + * @param string $collectionId Collection ID + * @param string|null $transactionId Optional transaction ID + * @param array $queries Optional query filters + * @return array Array of Document objects */ public function listDocuments( string $collectionId, @@ -280,6 +142,11 @@ class TransactionState /** * Count documents with transaction-aware logic + * + * @param string $collectionId Collection ID + * @param string|null $transactionId Optional transaction ID + * @param array $queries Optional query filters + * @return int Document count */ public function countDocuments( string $collectionId, @@ -302,7 +169,6 @@ class TransactionState } // Build a set of committed document IDs that match the query - // We need to find which documents match the filters $committedDocs = $this->dbForProject->find($collectionId, $queries); $committedDocIds = []; foreach ($committedDocs as $doc) { @@ -320,9 +186,6 @@ class TransactionState } } elseif ($docState['action'] === 'create') { // Document was created in transaction - // We need to check if it would match the query filters - // For now, we'll conservatively add it if no filters are present - // or apply basic filter matching if ($this->documentMatchesFilters($docState['document'], $queries)) { $adjustedCount++; // New document that matches } @@ -348,7 +211,285 @@ class TransactionState } /** - * Check if a document matches filter queries (simplified implementation) + * Check if a document exists with transaction-aware logic + * + * @param string $collectionId Collection ID + * @param string $documentId Document ID + * @param string|null $transactionId Optional transaction ID + * @return bool True if document exists + */ + public function documentExists( + string $collectionId, + string $documentId, + ?string $transactionId = null + ): bool { + $doc = $this->getDocument($collectionId, $documentId, $transactionId); + return !$doc->isEmpty(); + } + + /** + * Apply bulk update to documents in transaction state that match queries + * + * This allows bulk operations within a transaction to see each other's changes. + * + * @param string $collectionId Collection ID + * @param Document $updateData Document with update values + * @param array $queries Query filters to match documents + * @param array &$state Transaction state (passed by reference) + * @return void + */ + public function applyBulkUpdateToState( + string $collectionId, + Document $updateData, + array $queries, + array &$state + ): void { + if (!isset($state[$collectionId])) { + return; + } + + foreach ($state[$collectionId] as $docId => $doc) { + if ($this->documentMatchesFilters($doc, $queries)) { + // Apply the update to the state document + foreach ($updateData->getArrayCopy() as $key => $value) { + if ($key !== '$id') { + $doc->setAttribute($key, $value); + } + } + } + } + } + + /** + * Apply bulk delete to documents in transaction state that match queries + * + * This allows bulk operations within a transaction to see each other's changes. + * + * @param string $collectionId Collection ID + * @param array $queries Query filters to match documents + * @param array &$state Transaction state (passed by reference) + * @return void + */ + public function applyBulkDeleteToState( + string $collectionId, + array $queries, + array &$state + ): void { + if (!isset($state[$collectionId])) { + return; + } + + foreach ($state[$collectionId] as $docId => $doc) { + if ($this->documentMatchesFilters($doc, $queries)) { + unset($state[$collectionId][$docId]); + } + } + } + + /** + * Apply bulk upsert to documents in transaction state + * + * This allows bulk operations within a transaction to see each other's changes. + * + * @param string $collectionId Collection ID + * @param array $documents Array of Document objects to upsert + * @param array &$state Transaction state (passed by reference) + * @return void + */ + public function applyBulkUpsertToState( + string $collectionId, + array $documents, + array &$state + ): void { + foreach ($documents as $doc) { + if (!($doc instanceof Document)) { + continue; + } + + $docId = $doc->getId(); + if (!$docId) { + continue; + } + + // If document exists in state, update it; otherwise it will be handled by DB upsert + if (isset($state[$collectionId][$docId])) { + // Apply updates to existing state document + foreach ($doc->getArrayCopy() as $key => $value) { + if ($key !== '$id') { + $state[$collectionId][$docId]->setAttribute($key, $value); + } + } + } + } + } + + /** + * Get the current state of a transaction by replaying its operations + * + * @param string $transactionId Transaction ID + * @return array State array with structure: [collectionId => [docId => ['action' => ..., 'document' => ..., 'exists' => ...]]] + */ + private function getTransactionState(string $transactionId): array + { + $transaction = $this->dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') { + return []; + } + + // Fetch operations ordered by sequence to replay in exact order + $operations = $this->dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + ]); + + $state = []; + + foreach ($operations as $operation) { + $databaseInternalId = $operation['databaseInternalId']; + $collectionInternalId = $operation['collectionInternalId']; + $collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; + $documentId = $operation['documentId']; + $action = $operation['action']; + $data = $operation['data']; + + if ($data instanceof Document) { + $data = $data->getArrayCopy(); + } + + switch ($action) { + case 'create': + $docId = $documentId ?? ($data['$id'] ?? null); + if ($docId) { + $state[$collectionId][$docId] = [ + 'action' => 'create', + 'document' => new Document($data), + 'exists' => true + ]; + } + break; + + case 'update': + if (isset($state[$collectionId][$documentId])) { + // Update existing document in transaction state + $existingDocument = $state[$collectionId][$documentId]['document']; + foreach ($data as $key => $value) { + if ($key !== '$id') { + $existingDocument->setAttribute($key, $value); + } + } + // Only set action to 'update' if it's not already 'create' or 'upsert' + $currentAction = $state[$collectionId][$documentId]['action']; + if ($currentAction !== 'create' && $currentAction !== 'upsert') { + $state[$collectionId][$documentId]['action'] = 'update'; + } + } else { + // Document doesn't exist in transaction state, will be merged with committed version + $state[$collectionId][$documentId] = [ + 'action' => 'update', + 'document' => new Document($data), + 'exists' => true + ]; + } + break; + + case 'upsert': + $docId = $documentId ?? ($data['$id'] ?? null); + if (!$docId) { + break; + } + $state[$collectionId][$docId] = [ + 'action' => 'upsert', + 'document' => new Document($data), + 'exists' => true + ]; + break; + + case 'delete': + $state[$collectionId][$documentId] = [ + 'action' => 'delete', + 'exists' => false + ]; + break; + + case 'bulkCreate': + if (\is_array($data)) { + foreach ($data as $doc) { + if ($doc instanceof Document) { + $doc = $doc->getArrayCopy(); + } + $state[$collectionId][$doc['$id']] = [ + 'action' => 'create', + 'document' => new Document($doc), + 'exists' => true + ]; + } + } + break; + } + } + + return $state; + } + + /** + * Apply projection (select) semantics from queries to a document + * + * @param Document $doc Document to apply projection to + * @param array $queries Query array that may contain select queries + * @return Document Projected document + */ + private function applyProjection(Document $doc, array $queries): Document + { + if (empty($queries)) { + return $doc; + } + + // Extract selections from queries + $selections = []; + foreach ($queries as $query) { + if ($query->getMethod() === Query::TYPE_SELECT) { + $values = $query->getValues(); + foreach ($values as $value) { + // Skip relationship selections (containing '.') + if (!\str_contains($value, '.')) { + $selections[] = $value; + } + } + } + } + + // If no selections or wildcard present, return document as-is + if (empty($selections) || \in_array('*', $selections)) { + return $doc; + } + + // Create a new document with only selected attributes + $projected = new Document(); + + // Always preserve internal attributes + $projected->setAttribute('$id', $doc->getId()); + $projected->setAttribute('$collection', $doc->getCollection()); + $projected->setAttribute('$createdAt', $doc->getCreatedAt()); + $projected->setAttribute('$updatedAt', $doc->getUpdatedAt()); + if ($doc->offsetExists('$permissions')) { + $projected->setAttribute('$permissions', $doc->getPermissions()); + } + + // Add selected attributes + foreach ($selections as $attribute) { + if ($doc->offsetExists($attribute)) { + $projected->setAttribute($attribute, $doc->getAttribute($attribute)); + } + } + + return $projected; + } + + /** + * Check if a document matches filter queries + * + * @param Document $doc Document to check + * @param array $queries Query filters + * @return bool True if document matches all filters */ private function documentMatchesFilters(Document $doc, array $queries): bool { @@ -387,11 +528,13 @@ class TransactionState return false; } break; + case Query::TYPE_NOT_EQUAL: if (\in_array($docValue, $values)) { return false; } break; + case Query::TYPE_CONTAINS: $matches = false; foreach ($values as $value) { @@ -404,6 +547,7 @@ class TransactionState return false; } break; + case Query::TYPE_STARTS_WITH: $matches = false; foreach ($values as $value) { @@ -416,6 +560,7 @@ class TransactionState return false; } break; + case Query::TYPE_ENDS_WITH: $matches = false; foreach ($values as $value) { @@ -428,36 +573,43 @@ class TransactionState return false; } break; + case Query::TYPE_GREATER_THAN: if (!($docValue > $values[0])) { return false; } break; + case Query::TYPE_GREATER_THAN_EQUAL: if (!($docValue >= $values[0])) { return false; } break; + case Query::TYPE_LESSER_THAN: if (!($docValue < $values[0])) { return false; } break; + case Query::TYPE_LESSER_THAN_EQUAL: if (!($docValue <= $values[0])) { return false; } break; + case Query::TYPE_IS_NULL: if (!\is_null($docValue)) { return false; } break; + case Query::TYPE_IS_NOT_NULL: if (\is_null($docValue)) { return false; } break; + case Query::TYPE_BETWEEN: if (!($docValue >= $values[0] && $docValue <= $values[1])) { return false; @@ -468,16 +620,4 @@ class TransactionState return true; } - - /** - * Check if a document exists with transaction-aware logic - */ - public function documentExists( - string $collectionId, - string $documentId, - ?string $transactionId = null - ): bool { - $doc = $this->getDocument($collectionId, $documentId, $transactionId); - return !$doc->isEmpty(); - } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 37642a0cca..19ba8f10d8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; +use Appwrite\Databases\TransactionState; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; @@ -66,6 +67,7 @@ class Update extends Action ->param('rollback', false, new Boolean(), 'Rollback transaction?', true) ->inject('response') ->inject('dbForProject') + ->inject('transactionState') ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('queueForStatsUsage') @@ -81,6 +83,7 @@ class Update extends Action * @param bool $rollback * @param UtopiaResponse $response * @param Database $dbForProject + * @param TransactionState $transactionState * @param Delete $queueForDeletes * @param Event $queueForEvents * @param StatsUsage $queueForStatsUsage @@ -96,7 +99,7 @@ class Update extends Action * @throws Structure * @throws \Utopia\Exception */ - public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void + public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void { if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); @@ -182,13 +185,13 @@ class Update extends Action $this->handleBulkCreateOperation($dbForProject, $collectionId, $data, $createdAt, $state); break; case 'bulkUpdate': - $this->handleBulkUpdateOperation($dbForProject, $collectionId, $data, $createdAt, $state); + $this->handleBulkUpdateOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); break; case 'bulkUpsert': - $this->handleBulkUpsertOperation($dbForProject, $collectionId, $data, $createdAt, $state); + $this->handleBulkUpsertOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); break; case 'bulkDelete': - $this->handleBulkDeleteOperation($dbForProject, $collectionId, $data, $createdAt, $state); + $this->handleBulkDeleteOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); break; } } @@ -348,6 +351,14 @@ class Update extends Action /** * Handle create operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param string|null $documentId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void * @throws \Utopia\Database\Exception */ private function handleCreateOperation( @@ -371,6 +382,14 @@ class Update extends Action /** * Handle update operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param string $documentId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void * @throws ConflictException * @throws \Utopia\Database\Exception */ @@ -410,6 +429,14 @@ class Update extends Action /** * Handle upsert operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param string|null $documentId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void * @throws \Utopia\Database\Exception */ private function handleUpsertOperation( @@ -442,6 +469,15 @@ class Update extends Action /** * Handle delete operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param string $documentId + * @param \DateTime $createdAt + * @param array &$state + * @return void + * @throws \Utopia\Database\Exception + * @throws NotFoundException */ private function handleDeleteOperation( Database $dbForProject, @@ -473,6 +509,16 @@ class Update extends Action /** * Handle increment operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param string $documentId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void + * @throws ConflictException + * @throws \Utopia\Database\Exception */ private function handleIncrementOperation( Database $dbForProject, @@ -510,6 +556,16 @@ class Update extends Action /** * Handle decrement operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param string $documentId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void + * @throws ConflictException + * @throws \Utopia\Database\Exception */ private function handleDecrementOperation( Database $dbForProject, @@ -547,6 +603,14 @@ class Update extends Action /** * Handle bulk create operation + * + * @param Database $dbForProject + * @param string $collectionId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void + * @throws \Utopia\Database\Exception */ private function handleBulkCreateOperation( Database $dbForProject, @@ -573,22 +637,33 @@ class Update extends Action /** * Handle bulk update operation with manual timestamp checking + * + * @param Database $dbForProject + * @param TransactionState $transactionState + * @param string $collectionId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void * @throws \Utopia\Database\Exception * @throws \Utopia\Database\Exception\Query * @throws ConflictException */ private function handleBulkUpdateOperation( Database $dbForProject, + TransactionState $transactionState, string $collectionId, array $data, \DateTime $createdAt, array &$state ): void { $queries = Query::parseQueries($data['queries'] ?? []); + $updateData = new Document($data['data']); + // First, update documents in the committed database $dbForProject->updateDocuments( $collectionId, - new Document($data['data']), + $updateData, $queries, onNext: function (Document $updated, Document $old) use (&$state, $collectionId, $createdAt) { // Check if this document was created/modified in this transaction @@ -605,14 +680,27 @@ class Update extends Action $state[$collectionId][$updated->getId()] = $updated; } ); + + // Also update documents in the transaction state that match the query + $transactionState->applyBulkUpdateToState($collectionId, $updateData, $queries, $state); } /** * Handle bulk upsert operation with manual timestamp checking + * + * @param Database $dbForProject + * @param TransactionState $transactionState + * @param string $collectionId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void * @throws ConflictException + * @throws \Utopia\Database\Exception */ private function handleBulkUpsertOperation( Database $dbForProject, + TransactionState $transactionState, string $collectionId, array $data, \DateTime $createdAt, @@ -623,7 +711,11 @@ class Update extends Action return $doc instanceof Document ? $doc : new Document($doc); }, $data); - // Run bulk upsert without timestamp wrapper, checking manually in callback + // First, apply upserts to documents in the transaction state + // This ensures documents created in this transaction are updated properly + $transactionState->applyBulkUpsertToState($collectionId, $documents, $state); + + // Then run bulk upsert on committed database, checking manually in callback $dbForProject->upsertDocuments( $collectionId, $documents, @@ -649,11 +741,21 @@ class Update extends Action /** * Handle bulk delete operation with manual timestamp checking + * + * @param Database $dbForProject + * @param TransactionState $transactionState + * @param string $collectionId + * @param array $data + * @param \DateTime $createdAt + * @param array &$state + * @return void * @throws \Utopia\Database\Exception\Query * @throws ConflictException + * @throws \Utopia\Database\Exception */ private function handleBulkDeleteOperation( Database $dbForProject, + TransactionState $transactionState, string $collectionId, array $data, \DateTime $createdAt, @@ -681,5 +783,8 @@ class Update extends Action } } ); + + // Also delete documents in the transaction state that match the query + $transactionState->applyBulkDeleteToState($collectionId, $queries, $state); } } From 59ae403391892b33a4d67e1eeafcc18061ea1a8f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 17:34:34 +1300 Subject: [PATCH 214/274] Add more validation tests --- .../Http/TablesDB/Transactions/Update.php | 1 + .../Utopia/Database/Validator/Operation.php | 45 ++ .../Legacy/Transactions/TransactionsBase.php | 351 ++++++++++++++ .../Transactions/TransactionsBase.php | 449 ++++++++++++++++++ 4 files changed, 846 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php index bcfb2db406..6bdf6df7ef 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php @@ -52,6 +52,7 @@ class Update extends TransactionsUpdate ->param('rollback', false, new Boolean(), 'Rollback transaction?', true) ->inject('response') ->inject('dbForProject') + ->inject('transactionState') ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 2e3dc8e9e0..4e421689e3 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -159,6 +159,51 @@ class Operation extends Validator } } + // Bulk operation specific validations + $action = $value['action']; + + // BulkUpdate and BulkDelete require queries + if (\in_array($action, ['bulkUpdate', 'bulkDelete'])) { + if (!\array_key_exists('data', $value) || !\is_array($value['data'])) { + $this->description = "Key 'data' must be an array for {$action}"; + return false; + } + if (!\array_key_exists('queries', $value['data'])) { + $this->description = "Key 'queries' is required in data for {$action}"; + return false; + } + if (!\is_array($value['data']['queries'])) { + $this->description = "Key 'queries' must be an array for {$action}"; + return false; + } + } + + // BulkUpdate requires both queries and data + if ($action === 'bulkUpdate') { + if (!\array_key_exists('data', $value['data'])) { + $this->description = "Key 'data' is required in data for {$action}"; + return false; + } + if (!\is_array($value['data']['data'])) { + $this->description = "Key 'data.data' must be an array for {$action}"; + return false; + } + } + + // Increment and Decrement require specific keys + if (\in_array($action, ['increment', 'decrement'])) { + if (!\array_key_exists('data', $value) || !\is_array($value['data'])) { + $this->description = "Key 'data' must be an array for {$action}"; + return false; + } + // Get the attribute key name based on type + $attributeKey = $this->type === 'tablesdb' ? 'column' : 'attribute'; + if (!\array_key_exists($attributeKey, $value['data'])) { + $this->description = "Key '{$attributeKey}' is required in data for {$action}"; + return false; + } + } + return true; } diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php index f05563123c..a0485b5ef3 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php @@ -4225,4 +4225,355 @@ trait TransactionsBase sort($remainingIds); $this->assertEquals(['doc_6', 'doc_7', 'doc_8'], $remainingIds); } + + /** + * Test validation for invalid operation inputs + */ + public function testCreateOperationsValidation(): void + { + // Create database and collection for testing + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'ValidationTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'ValidationTest', + 'documentSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $collectionId = $collection['body']['$id']; + + // Add required attribute + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + + // Wait for attribute to be ready + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; + + // Test 1: Invalid action type + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'invalidAction', + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 2: Missing required action field + $response = $this->client->call(Client::METHOD_POST, "/databases/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, + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 3: Missing required databaseId field + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'create', + 'collectionId' => $collectionId, + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 4: Missing documentId for create operation + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 5: Missing data for create operation + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => ID::unique() + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 6: BulkCreate with non-array data + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'bulkCreate', + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'data' => 'not an array' + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 7: BulkUpdate with missing queries + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'bulkUpdate', + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'data' => [ + 'data' => ['name' => 'Updated'] + ] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 8: Empty operations array + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 9: Operations not an array + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => 'not an array' + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * Test validation for committing/rolling back transactions + */ + public function testCommitRollbackValidation(): void + { + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; + + // Test 1: Missing both commit and rollback + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 2: Both commit and rollback set to true + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true, + 'rollback' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 3: Invalid transaction ID + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/invalid_id", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Commit the transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/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']); + + // Test 4: Attempt to commit already committed transaction + $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * Test validation for non-existent resources + */ + public function testNonExistentResources(): void + { + // Create database and transaction + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'ResourceTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + $transaction = $this->client->call(Client::METHOD_POST, '/databases/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']); + $transactionId = $transaction['body']['$id']; + + // Test 1: Non-existent database + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'create', + 'databaseId' => 'nonExistentDatabase', + 'collectionId' => 'someCollection', + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Test 2: Non-existent collection + $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'operations' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'collectionId' => 'nonExistentCollection', + 'documentId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + } } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index 4e264e8641..2752e2d036 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -4225,4 +4225,453 @@ trait TransactionsBase sort($remainingIds); $this->assertEquals(['row_6', 'row_7', 'row_8'], $remainingIds); } + + /** + * Test validation for invalid operation inputs + */ + public function testCreateOperationsValidation(): void + { + // Create database and table for testing + $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' => 'ValidationTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $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' => 'ValidationTest', + 'rowSecurity' => false, + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $tableId = $table['body']['$id']; + + // Add required column + $column = $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->assertEquals(202, $column['headers']['status-code']); + + // Wait for column to be ready + sleep(2); + + // 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']); + $transactionId = $transaction['body']['$id']; + + // Test 1: Invalid action type + $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' => [ + [ + 'action' => 'invalidAction', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 2: Missing required action field + $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, + 'tableId' => $tableId, + 'rowId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 3: Missing required databaseId field + $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' => [ + [ + 'action' => 'create', + 'tableId' => $tableId, + 'rowId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 4: Missing required tableId field + $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' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'rowId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 5: Missing rowId for create operation + $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' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 6: Missing data for create operation + $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' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => ID::unique() + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 7: BulkCreate with non-array 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' => [ + [ + 'action' => 'bulkCreate', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'data' => 'not an array' + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 8: BulkUpdate with missing queries + $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' => [ + [ + 'action' => 'bulkUpdate', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'data' => [ + 'data' => ['name' => 'Updated'] + ] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 9: BulkUpdate with invalid query format + $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' => [ + [ + 'action' => 'bulkUpdate', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'data' => [ + 'queries' => 'not an array', + 'data' => ['name' => 'Updated'] + ] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 10: BulkDelete with missing queries + $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' => [ + [ + 'action' => 'bulkDelete', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'data' => [] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 11: Increment with missing attribute + $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' => [ + [ + 'action' => 'increment', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => ID::unique(), + 'data' => ['value' => 1] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 12: Decrement with invalid value type + $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' => [ + [ + 'action' => 'decrement', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => ID::unique(), + 'data' => [ + 'attribute' => 'counter', + 'value' => 'not a number' + ] + ] + ] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 13: Empty operations array + $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' => [] + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 14: Operations not an array + $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' => 'not an array' + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } + + /** + * Test validation for committing/rolling back transactions + */ + public function testCommitRollbackValidation(): void + { + // 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']); + $transactionId = $transaction['body']['$id']; + + // Test 1: Missing both commit and rollback + $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'] + ]), []); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 2: Both commit and rollback set to true + $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, + 'rollback' => true + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // Test 3: Invalid transaction ID + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/invalid_id", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'commit' => true + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Commit the 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']); + + // Test 4: Attempt to commit already committed 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(400, $response['headers']['status-code']); + } + + /** + * Test validation for non-existent resources + */ + public function testNonExistentResources(): void + { + // Create database and transaction + $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' => 'ResourceTestDatabase' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + $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']); + $transactionId = $transaction['body']['$id']; + + // Test 1: Non-existent database + $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' => [ + [ + 'action' => 'create', + 'databaseId' => 'nonExistentDatabase', + 'tableId' => 'someTable', + 'rowId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + // Test 2: Non-existent table + $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' => [ + [ + 'action' => 'create', + 'databaseId' => $databaseId, + 'tableId' => 'nonExistentTable', + 'rowId' => ID::unique(), + 'data' => ['name' => 'Test'] + ] + ] + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + } } From 2bc90db1f6f1593b48ab06bc9beaef4a71a5fd9a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 18:56:05 +1300 Subject: [PATCH 215/274] Fix tests --- src/Appwrite/Databases/TransactionState.php | 8 ++++---- .../Http/Databases/Transactions/Operations/Create.php | 4 ++++ .../Databases/Http/Databases/Transactions/Update.php | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 85ecb41ae2..e9cb6b82e4 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -574,25 +574,25 @@ class TransactionState } break; - case Query::TYPE_GREATER_THAN: + case Query::TYPE_GREATER: if (!($docValue > $values[0])) { return false; } break; - case Query::TYPE_GREATER_THAN_EQUAL: + case Query::TYPE_GREATER_EQUAL: if (!($docValue >= $values[0])) { return false; } break; - case Query::TYPE_LESSER_THAN: + case Query::TYPE_LESSER: if (!($docValue < $values[0])) { return false; } break; - case Query::TYPE_LESSER_THAN_EQUAL: + case Query::TYPE_LESSER_EQUAL: if (!($docValue <= $values[0])) { return false; } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 20cf79551c..80122a6028 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -64,6 +64,10 @@ class Create extends Action public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, array $plan): void { + if (empty($operations)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); + } + $transaction = $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 19ba8f10d8..bd0c7f5582 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -129,7 +129,7 @@ class Update extends Action $totalOperations = 0; $databaseOperations = []; - $dbForProject->withTransaction(function () use ($dbForProject, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { + $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committing', ])); From deb3d7537539d958fe40c8ba0cbdd8e1f9b6044c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 3 Oct 2025 20:00:32 +1300 Subject: [PATCH 216/274] Explicit order asc --- src/Appwrite/Databases/TransactionState.php | 16 ++++++++++++++++ .../Http/Databases/Transactions/Update.php | 1 + 2 files changed, 17 insertions(+) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index e9cb6b82e4..31e09ccf75 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -4,6 +4,8 @@ namespace Appwrite\Databases; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception; +use Utopia\Database\Exception\Timeout; use Utopia\Database\Query; /** @@ -32,6 +34,9 @@ class TransactionState * @param string|null $transactionId Optional transaction ID * @param array $queries Optional query filters * @return Document + * @throws Exception + * @throws Exception\Query + * @throws Timeout */ public function getDocument( string $collectionId, @@ -90,6 +95,9 @@ class TransactionState * @param string|null $transactionId Optional transaction ID * @param array $queries Optional query filters * @return array Array of Document objects + * @throws Exception + * @throws Exception\Query + * @throws Timeout */ public function listDocuments( string $collectionId, @@ -147,6 +155,9 @@ class TransactionState * @param string|null $transactionId Optional transaction ID * @param array $queries Optional query filters * @return int Document count + * @throws Exception + * @throws Exception\Query + * @throws Timeout */ public function countDocuments( string $collectionId, @@ -328,6 +339,9 @@ class TransactionState * * @param string $transactionId Transaction ID * @return array State array with structure: [collectionId => [docId => ['action' => ..., 'document' => ..., 'exists' => ...]]] + * @throws Exception + * @throws Exception\Query + * @throws Timeout */ private function getTransactionState(string $transactionId): array { @@ -339,6 +353,8 @@ class TransactionState // Fetch operations ordered by sequence to replay in exact order $operations = $this->dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc(), + Query::limit(PHP_INT_MAX) ]); $state = []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index bd0c7f5582..525c74abc7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -137,6 +137,7 @@ class Update extends Action // Fetch operations ordered by sequence by default to replay operations in exact order they were created $operations = $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc(), Query::limit(PHP_INT_MAX), ]); From ce86758cdccb9e7669233e182cdee4314243147d Mon Sep 17 00:00:00 2001 From: Priyanshu Thapliyal <114170980+Priyanshuthapliyal2005@users.noreply.github.com> Date: Fri, 3 Oct 2025 08:30:06 +0000 Subject: [PATCH 217/274] fix typo issues raise by bot --- app/config/specs/open-api3-1.8.x-client.json | 2 +- app/config/specs/open-api3-1.8.x-console.json | 4 ++-- app/config/specs/open-api3-1.8.x-server.json | 4 ++-- app/config/specs/open-api3-latest-client.json | 2 +- app/config/specs/open-api3-latest-console.json | 4 ++-- app/config/specs/open-api3-latest-server.json | 4 ++-- app/config/specs/swagger2-1.8.x-client.json | 2 +- app/config/specs/swagger2-1.8.x-console.json | 4 ++-- app/config/specs/swagger2-1.8.x-server.json | 4 ++-- app/config/specs/swagger2-latest-client.json | 2 +- app/config/specs/swagger2-latest-console.json | 4 ++-- app/config/specs/swagger2-latest-server.json | 4 ++-- .../Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php | 2 +- .../Modules/Databases/Http/TablesDB/Tables/Rows/XList.php | 2 +- 14 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index 2038c1940a..8dc8a050f0 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -7533,7 +7533,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index e0eccd3f69..d0cc1d3161 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -37218,7 +37218,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37390,7 +37390,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 74be46d8d3..55c0d1f53e 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -27669,7 +27669,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27757,7 +27757,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 2038c1940a..8dc8a050f0 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -7533,7 +7533,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index e0eccd3f69..d0cc1d3161 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -37218,7 +37218,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -37390,7 +37390,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 74be46d8d3..55c0d1f53e 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -27669,7 +27669,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "schema": { "type": "string", @@ -27757,7 +27757,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "schema": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 3003de22af..717d6ddbb2 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -7612,7 +7612,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index f222d3dff6..a276134b54 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -37249,7 +37249,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37409,7 +37409,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index db04d55cd0..e79ef98a04 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -27755,7 +27755,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27836,7 +27836,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 3003de22af..717d6ddbb2 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -7612,7 +7612,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index f222d3dff6..a276134b54 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -37249,7 +37249,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -37409,7 +37409,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index db04d55cd0..e79ef98a04 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -27755,7 +27755,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the Database service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/references\/cloud\/server-dart\/tablesDB#createTable).", "required": true, "type": "string", "x-example": "", @@ -27836,7 +27836,7 @@ }, { "name": "tableId", - "description": "Table ID. You can create a new table using the TableDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", + "description": "Table ID. You can create a new table using the TablesDB service [server integration](https:\/\/appwrite.io\/docs\/products\/databases\/tables#create-table).", "required": true, "type": "string", "x-example": "", diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php index dd97c4bdc7..ec05c92a64 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Delete.php @@ -55,7 +55,7 @@ class Delete extends IndexDelete contentType: ContentType::NONE )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php index cbbbf9520d..00953a1d04 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php @@ -49,7 +49,7 @@ class XList extends DocumentXList contentType: ContentType::JSON )) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TableDB service [server integration](https://appwrite.io/docs/products/databases/tables#create-table).') + ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/products/databases/tables#create-table).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->inject('response') ->inject('dbForProject') From b145c609bd81457282d9d29a693cf1ee86d11f53 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 3 Oct 2025 16:29:39 +0530 Subject: [PATCH 218/274] change error codes --- app/config/errors.php | 15 +++++++++++++++ .../Services/Account/AccountCustomClientTest.php | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/config/errors.php b/app/config/errors.php index 156af5db8f..4345884ff5 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -211,6 +211,11 @@ return [ 'description' => 'User with the requested ID could not be found.', 'code' => 404, ], + Exception::USER_EMAIL_NOT_FOUND => [ + 'name' => Exception::USER_EMAIL_NOT_FOUND, + 'description' => 'User email could not be found.', + 'code' => 400, + ], Exception::USER_EMAIL_ALREADY_EXISTS => [ 'name' => Exception::USER_EMAIL_ALREADY_EXISTS, 'description' => 'A user with the same email already exists in the current project.', @@ -312,11 +317,21 @@ return [ 'description' => 'OAuth2 provider returned some error.', 'code' => 424, ], + Exception::USER_EMAIL_NOT_VERIFIED => [ + 'name' => Exception::USER_EMAIL_NOT_VERIFIED, + 'description' => 'User email is not verified', + 'code' => 400, + ], Exception::USER_EMAIL_ALREADY_VERIFIED => [ 'name' => Exception::USER_EMAIL_ALREADY_VERIFIED, 'description' => 'User email is already verified', 'code' => 409, ], + Exception::USER_PHONE_NOT_VERIFIED => [ + 'name' => Exception::USER_PHONE_NOT_VERIFIED, + 'description' => 'User phone is not verified', + 'code' => 400, + ], Exception::USER_PHONE_ALREADY_VERIFIED => [ 'name' => Exception::USER_PHONE_ALREADY_VERIFIED, 'description' => 'User phone is already verified', diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 5cec3770f7..8ebd89c983 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -1864,7 +1864,7 @@ class AccountCustomClientTest extends Scope 'url' => 'http://localhost/verification', ]); - $this->assertEquals(500, $response['body']['code']); + $this->assertEquals(400, $response['body']['code']); $this->assertEquals('user_email_not_found', $response['body']['type']); return []; From 89a47953d807643a8f66c233ba493688606f547e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 00:46:13 +1300 Subject: [PATCH 219/274] Fix tests --- src/Appwrite/Databases/TransactionState.php | 37 ++++--- .../Http/Databases/Transactions/Update.php | 96 ++++++++++--------- 2 files changed, 77 insertions(+), 56 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 31e09ccf75..645c25bf76 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -188,6 +188,8 @@ class TransactionState $adjustedCount = $baseCount; + $filters = $this->extractFilters($queries); + // Apply transaction state changes to the count foreach ($state[$collectionId] as $docId => $docState) { if (!$docState['exists']) { @@ -197,13 +199,13 @@ class TransactionState } } elseif ($docState['action'] === 'create') { // Document was created in transaction - if ($this->documentMatchesFilters($docState['document'], $queries)) { + if ($this->documentMatchesFilters($docState['document'], $filters)) { $adjustedCount++; // New document that matches } } elseif ($docState['action'] === 'update' || $docState['action'] === 'upsert') { // Document was updated/upserted $wasInResults = isset($committedDocIds[$docId]); - $nowMatches = $this->documentMatchesFilters($docState['document'], $queries); + $nowMatches = $this->documentMatchesFilters($docState['document'], $filters); if (!$wasInResults && $nowMatches && $docState['action'] === 'upsert') { $adjustedCount++; // Upsert created new document that matches @@ -259,8 +261,10 @@ class TransactionState return; } + $filters = $this->extractFilters($queries); + foreach ($state[$collectionId] as $docId => $doc) { - if ($this->documentMatchesFilters($doc, $queries)) { + if ($this->documentMatchesFilters($doc, $filters)) { // Apply the update to the state document foreach ($updateData->getArrayCopy() as $key => $value) { if ($key !== '$id') { @@ -290,8 +294,10 @@ class TransactionState return; } + $filters = $this->extractFilters($queries); + foreach ($state[$collectionId] as $docId => $doc) { - if ($this->documentMatchesFilters($doc, $queries)) { + if ($this->documentMatchesFilters($doc, $filters)) { unset($state[$collectionId][$docId]); } } @@ -501,19 +507,17 @@ class TransactionState } /** - * Check if a document matches filter queries + * Extract only filter queries from a query array * - * @param Document $doc Document to check - * @param array $queries Query filters - * @return bool True if document matches all filters + * @param array $queries Query array + * @return array Filtered queries */ - private function documentMatchesFilters(Document $doc, array $queries): bool + private function extractFilters(array $queries): array { - // Extract filter queries $filters = []; foreach ($queries as $query) { $method = $query->getMethod(); - // Only process filter queries, not limit/offset/cursor/select + // Only process filter queries, not limit/offset/cursor/select/order if (!\in_array($method, [ Query::TYPE_LIMIT, Query::TYPE_OFFSET, @@ -526,7 +530,18 @@ class TransactionState $filters[] = $query; } } + return $filters; + } + /** + * Check if a document matches filter queries + * + * @param Document $doc Document to check + * @param array $filters Pre-filtered Query filters (use extractFilters first) + * @return bool True if document matches all filters + */ + private function documentMatchesFilters(Document $doc, array $filters): bool + { // If no filters, document matches if (empty($filters)) { return true; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 525c74abc7..c17fb7e75f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -129,22 +129,22 @@ class Update extends Action $totalOperations = 0; $databaseOperations = []; - $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'committing', - ])); + try { + $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'committing', + ])); - // Fetch operations ordered by sequence by default to replay operations in exact order they were created - $operations = $dbForProject->find('transactionLogs', [ - Query::equal('transactionInternalId', [$transaction->getSequence()]), - Query::orderAsc(), - Query::limit(PHP_INT_MAX), - ]); + // Fetch operations ordered by sequence by default to replay operations in exact order they were created + $operations = $dbForProject->find('transactionLogs', [ + Query::equal('transactionInternalId', [$transaction->getSequence()]), + Query::orderAsc(), + Query::limit(PHP_INT_MAX), + ]); - // Track transaction state for cross-operation visibility - $state = []; + // Track transaction state for cross-operation visibility + $state = []; - try { foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; $collectionInternalId = $operation['collectionInternalId']; @@ -207,38 +207,44 @@ class Update extends Action $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); - } catch (NotFoundException $e) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); - } catch (DuplicateException|ConflictException $e) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); - } catch (StructureException $e) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); - } catch (LimitException $e) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage()); - } catch (TransactionException $e) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); - } catch (QueryException $e) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ - 'status' => 'failed', - ])); - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - }); + }); + } catch (NotFoundException $e) { + // Transaction has been rolled back, now mark it as failed + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); + } catch (DuplicateException|ConflictException $e) { + // Transaction has been rolled back, now mark it as failed + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); + } catch (StructureException $e) { + // Transaction has been rolled back, now mark it as failed + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } catch (LimitException $e) { + // Transaction has been rolled back, now mark it as failed + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage()); + } catch (TransactionException $e) { + // Transaction has been rolled back, now mark it as failed + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); + } catch (QueryException $e) { + // Transaction has been rolled back, now mark it as failed + $dbForProject->updateDocument('transactions', $transactionId, new Document([ + 'status' => 'failed', + ])); + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $totalOperations); From e93e03c248619e2153bd3761f1746f6d4f56b0c7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 01:15:53 +1300 Subject: [PATCH 220/274] Update src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Modules/Databases/Http/Databases/Transactions/Update.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 525c74abc7..a1f6bd2a65 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -591,8 +591,9 @@ class Update extends Action } // Use timestamp wrapper for independent operations - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data) { - $dbForProject->decreaseDocumentAttribute( + // Use timestamp wrapper for independent operations + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { + $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, attribute: $data[$this->getAttributeKey()], From 742ddc517bdcd8b37f213014a17e4a1cb6113ef2 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 01:38:06 +1300 Subject: [PATCH 221/274] Update database --- composer.json | 2 +- composer.lock | 65 +++++++++++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/composer.json b/composer.json index 1dc7288441..d51ff828a3 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "1.*", + "utopia-php/database": "dev-feat-relationship-updates as 1.5.1", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", "utopia-php/dns": "0.3.*", diff --git a/composer.lock b/composer.lock index 95288fc01d..475d66c44b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7553e976312b0423cc31544abb91caec", + "content-hash": "ae57f581b8351bbabd8cf007d514e2b4", "packages": [ { "name": "adhocore/jwt", @@ -1159,16 +1159,16 @@ }, { "name": "open-telemetry/api", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2" + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/ee17d937652eca06c2341b6fadc0f74c1c1a5af2", - "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522", + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522", "shasum": "" }, "require": { @@ -1188,7 +1188,7 @@ ] }, "branch-alias": { - "dev-main": "1.4.x-dev" + "dev-main": "1.7.x-dev" } }, "autoload": { @@ -1225,7 +1225,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-19T00:05:49+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/context", @@ -1415,22 +1415,22 @@ }, { "name": "open-telemetry/sdk", - "version": "1.8.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89" + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/105c6e81e3d86150bd5704b00c7e4e165e957b89", - "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.6", + "open-telemetry/api": "^1.7", "open-telemetry/context": "^1.4", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", @@ -1465,7 +1465,7 @@ ] }, "branch-alias": { - "dev-main": "1.0.x-dev" + "dev-main": "1.9.x-dev" } }, "autoload": { @@ -1508,7 +1508,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-19T00:05:49+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/sem-conv", @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "1.5.1", + "version": "dev-feat-relationship-updates", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9" + "reference": "d97d181d7c2ed267b0b57ba421d3364faff64f8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/56efe4daaf23abb753553acffccdcc04cd6178c9", - "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9", + "url": "https://api.github.com/repos/utopia-php/database/zipball/d97d181d7c2ed267b0b57ba421d3364faff64f8e", + "reference": "d97d181d7c2ed267b0b57ba421d3364faff64f8e", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.5.1" + "source": "https://github.com/utopia-php/database/tree/feat-relationship-updates" }, - "time": "2025-10-01T04:44:14+00:00" + "time": "2025-10-03T12:05:20+00:00" }, { "name": "utopia-php/detector", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd" + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07a7d6276bd684b49469ad7b9e8c3c962121c6fd", - "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.4.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.3" }, - "time": "2025-10-01T03:23:04+00:00" + "time": "2025-10-01T06:25:19+00:00" }, { "name": "doctrine/annotations", @@ -8515,9 +8515,18 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/database", + "version": "dev-feat-relationship-updates", + "alias": "1.5.1", + "alias_normalized": "1.5.1.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/database": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 2c7cf7826b59205910525df1febae6fcb09c5606 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 16:43:39 +1300 Subject: [PATCH 222/274] Fix operation perm checks --- .../Http/Databases/Transactions/Create.php | 5 +- .../Transactions/Operations/Create.php | 103 ++- .../Http/Databases/Transactions/Update.php | 148 ++-- .../Http/TablesDB/Transactions/Update.php | 1 + .../Legacy/Transactions/TransactionsBase.php | 117 +-- .../TablesDB/Transactions/PermissionsBase.php | 670 ++++++++++++++++++ .../PermissionsCustomClientTest.php | 14 + .../Transactions/TransactionsBase.php | 117 +-- 8 files changed, 962 insertions(+), 213 deletions(-) create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php create mode 100644 tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsCustomClientTest.php diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index a5866ba7d1..e67c1d08d0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\DateTime; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Swoole\Response as SwooleResponse; @@ -57,12 +58,12 @@ class Create extends Action public function action(int $ttl, UtopiaResponse $response, Database $dbForProject): void { - $transaction = $dbForProject->createDocument('transactions', new Document([ + $transaction = Authorization::skip(fn () => $dbForProject->createDocument('transactions', new Document([ '$id' => ID::unique(), 'status' => 'pending', 'operations' => 0, 'expiresAt' => DateTime::addSeconds(new \DateTime(), $ttl), - ])); + ]))); $response ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 80122a6028..f7314fb87c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -14,6 +14,8 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -68,7 +70,7 @@ class Create extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); } - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)); if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } @@ -97,18 +99,103 @@ class Create extends Action throw new Exception(Exception::USER_UNAUTHORIZED); } - $database = $databases[$operation['databaseId']] ??= $dbForProject->getDocument('databases', $operation['databaseId']); - if ($database->isEmpty()) { + $database = $databases[$operation['databaseId']] ??= Authorization::skip(fn () => $dbForProject->getDocument('databases', $operation['databaseId'])); + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $collections[$operation[$this->getGroupId()]] ??= - $dbForProject->getDocument('database_' . $database->getSequence(), $operation[$this->getGroupId()]); + Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $operation[$this->getGroupId()])); - if ($collection->isEmpty()) { + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } + // Check if collection has relationships for bulk operations + if (\in_array($operation['action'], ['bulkCreate', 'bulkUpdate', 'bulkUpsert', 'bulkDelete'])) { + $hasRelationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + if ($hasRelationships) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk operations are not supported for ' . $this->getGroupId() . ' with relationship attributes'); + } + } + + // For update, upsert, delete, check document existence first + $document = null; + if (\in_array($operation['action'], ['update', 'delete', 'upsert'])) { + $documentId = $operation[$this->getResourceId()] ?? null; + if (empty($documentId)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Document ID is required for ' . $operation['action'] . ' operations'); + } + + $document = Authorization::skip(fn () => $dbForProject->getDocument( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + $documentId + )); + + if ($document->isEmpty() && \in_array($operation['action'], ['update', 'delete'])) { + throw new Exception(Exception::DOCUMENT_NOT_FOUND); + } + } + + $permissionType = match ($operation['action']) { + 'create', 'bulkCreate' => Database::PERMISSION_CREATE, + 'update', 'bulkUpdate' => Database::PERMISSION_UPDATE, + 'delete', 'bulkDelete' => Database::PERMISSION_DELETE, + 'upsert', 'bulkUpsert' => ($document && !$document->isEmpty()) ? Database::PERMISSION_UPDATE : Database::PERMISSION_CREATE, + default => throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid action: ' . $operation['action']) + }; + + if (!$isAPIKey && !$isPrivilegedUser) { + $documentSecurity = $collection->getAttribute('documentSecurity', false); + $validator = new Authorization($permissionType); + $collectionValid = $validator->isValid($collection->getPermissionsByType($permissionType)); + $documentValid = false; + if ($document !== null && !$document->isEmpty() && $documentSecurity) { + if ($permissionType === Database::PERMISSION_UPDATE) { + $documentValid = $validator->isValid($document->getUpdate()); + } elseif ($permissionType === Database::PERMISSION_DELETE) { + $documentValid = $validator->isValid($document->getDelete()); + } + } + + if ($permissionType === Database::PERMISSION_CREATE || !$documentSecurity) { + if (!$collectionValid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + } else { + if (!$collectionValid && !$documentValid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + } + + // Users can only set permissions for roles they have + if (isset($operation['data']['$permissions'])) { + $permissions = $operation['data']['$permissions'] ?? null; + if (!\is_null($permissions)) { + $roles = Authorization::getRoles(); + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + } + } + } + } + } + } + $staged[] = new Document([ '$id' => ID::unique(), 'databaseInternalId' => $database->getSequence(), @@ -121,13 +208,13 @@ class Create extends Action } $transaction = $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { - $dbForProject->createDocuments('transactionLogs', $staged); - return $dbForProject->increaseDocumentAttribute( + Authorization::skip(fn () => $dbForProject->createDocuments('transactionLogs', $staged)); + return Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute( 'transactions', $transactionId, 'operations', \count($operations) - ); + )); }); $response diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index c17fb7e75f..5c949c9227 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -14,9 +14,10 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Query as QueryException; @@ -67,6 +68,7 @@ class Update extends Action ->param('rollback', false, new Boolean(), 'Rollback transaction?', true) ->inject('response') ->inject('dbForProject') + ->inject('user') ->inject('transactionState') ->inject('queueForDeletes') ->inject('queueForEvents') @@ -83,6 +85,7 @@ class Update extends Action * @param bool $rollback * @param UtopiaResponse $response * @param Database $dbForProject + * @param Document $user * @param TransactionState $transactionState * @param Delete $queueForDeletes * @param Event $queueForEvents @@ -99,8 +102,9 @@ class Update extends Action * @throws Structure * @throws \Utopia\Exception */ - public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void + public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Document $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void { + if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); } @@ -108,7 +112,7 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); } - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } @@ -123,6 +127,7 @@ class Update extends Action } if ($commit) { + $operations = []; // Track metrics for usage stats @@ -131,16 +136,17 @@ class Update extends Action try { $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, $queueForEvents, $queueForStatsUsage, $queueForRealtime, $queueForFunctions, $queueForWebhooks) { - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'committing', - ])); + ]))); // Fetch operations ordered by sequence by default to replay operations in exact order they were created - $operations = $dbForProject->find('transactionLogs', [ + $operations = Authorization::skip(fn () => $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), Query::orderAsc(), Query::limit(PHP_INT_MAX), - ]); + ])); + // Track transaction state for cross-operation visibility $state = []; @@ -154,6 +160,15 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; + // For delete operations, fetch the document before deleting for realtime events + if ($action === 'delete' && $documentId && empty($data)) { + $doc = $dbForProject->getDocument($collectionId, $documentId); + if (!$doc->isEmpty()) { + $operation['data'] = $doc->getArrayCopy(); + $data = $operation['data']; + } + } + // Track operations for stats $totalOperations++; $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + 1; @@ -197,52 +212,53 @@ class Update extends Action } } - $transaction = $dbForProject->updateDocument( + $transaction = Authorization::skip(fn () => $dbForProject->updateDocument( 'transactions', $transactionId, new Document(['status' => 'committed']) - ); + )); // Clear the transaction logs $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); }); + } catch (NotFoundException $e) { // Transaction has been rolled back, now mark it as failed - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', - ])); + ]))); throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); } catch (DuplicateException|ConflictException $e) { // Transaction has been rolled back, now mark it as failed - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', - ])); + ]))); throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); } catch (StructureException $e) { // Transaction has been rolled back, now mark it as failed - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', - ])); + ]))); throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); } catch (LimitException $e) { // Transaction has been rolled back, now mark it as failed - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', - ])); + ]))); throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage()); } catch (TransactionException $e) { // Transaction has been rolled back, now mark it as failed - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', - ])); + ]))); throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); } catch (QueryException $e) { // Transaction has been rolled back, now mark it as failed - $dbForProject->updateDocument('transactions', $transactionId, new Document([ + Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', - ])); + ]))); throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } @@ -258,23 +274,27 @@ class Update extends Action } // Trigger realtime events for each operation + foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; $collectionInternalId = $operation['collectionInternalId']; + $collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}"; $action = $operation['action']; $documentId = $operation['documentId']; $data = $operation['data']; + if ($data instanceof Document) { $data = $data->getArrayCopy(); } - $database = $dbForProject->findOne('databases', [ + $database = Authorization::skip(fn () => $dbForProject->findOne('databases', [ Query::equal('$sequence', [$databaseInternalId]) - ]); - $collection = $dbForProject->findOne('database_' . $databaseInternalId, [ + ])); + + $collection = Authorization::skip(fn () => $dbForProject->findOne('database_' . $databaseInternalId, [ Query::equal('$sequence', [$collectionInternalId]) - ]); + ])); $groupId = $this->getGroupId(); $resourceId = $this->getResourceId(); @@ -290,26 +310,49 @@ class Update extends Action ->setContext($contextKey, $collection); $eventAction = ''; - $documents = []; + $documentsToTrigger = []; switch ($action) { case 'create': $eventAction = 'create'; - $documents[] = $documentId ?? $data['$id'] ?? null; + $docId = $documentId ?? $data['$id'] ?? null; + if ($docId) { + // Fetch the created document from the database + $doc = $dbForProject->getDocument($collectionId, $docId); + if (!$doc->isEmpty()) { + $documentsToTrigger[] = $doc; + } + } break; case 'update': case 'increment': case 'decrement': $eventAction = 'update'; - $documents[] = $documentId; + if ($documentId) { + // Fetch the updated document from the database + $doc = $dbForProject->getDocument($collectionId, $documentId); + if (!$doc->isEmpty()) { + $documentsToTrigger[] = $doc; + } + } break; case 'delete': $eventAction = 'delete'; - $documents[] = $documentId; + if ($documentId && !empty($data)) { + // For delete, use the fetched document data (fetched before deletion) + $documentsToTrigger[] = new Document(array_merge($data, ['$id' => $documentId])); + } break; case 'upsert': - $eventAction = 'upsert'; - $documents[] = $documentId ?? $data['$id'] ?? null; + $eventAction = 'update'; // Upsert is treated as update for events + $docId = $documentId ?? $data['$id'] ?? null; + if ($docId) { + // Fetch the upserted document from the database + $doc = $dbForProject->getDocument($collectionId, $docId); + if (!$doc->isEmpty()) { + $documentsToTrigger[] = $doc; + } + } break; case 'bulkCreate': case 'bulkUpdate': @@ -319,32 +362,43 @@ class Update extends Action } // Trigger events for each document - foreach ($documents as $docId) { - if ($docId) { - $queueForEvents - ->setParam('documentId', $docId) - ->setParam('rowId', $docId) - ->setEvent("databases.[databaseId].{$contextKey}s.[{$groupId}].{$resourcePlural}.[{$resourceId}]." . $eventAction); - $queueForRealtime->from($queueForEvents)->trigger(); - $queueForFunctions->from($queueForEvents)->trigger(); - $queueForWebhooks->from($queueForEvents)->trigger(); + $eventString = "databases.[databaseId].{$contextKey}s.[{$groupId}].{$resourcePlural}.[{$resourceId}]." . $eventAction; - $queueForEvents->reset(); - $queueForRealtime->reset(); - $queueForFunctions->reset(); - $queueForWebhooks->reset(); - } + $queueForEvents->setEvent($eventString); + + foreach ($documentsToTrigger as $doc) { + + // Add table/collection IDs to the payload for realtime channels + $payload = $doc->getArrayCopy(); + $payload['$tableId'] = $collection->getId(); + $payload['$collectionId'] = $collection->getId(); + + $queueForEvents + ->setParam('documentId', $doc->getId()) + ->setParam('rowId', $doc->getId()) + ->setPayload($payload); + + $project = $queueForEvents->getProject(); + $result = $queueForRealtime->from($queueForEvents)->trigger(); + + $queueForFunctions->from($queueForEvents)->trigger(); + $queueForWebhooks->from($queueForEvents)->trigger(); } + + $queueForEvents->reset(); + $queueForRealtime->reset(); + $queueForFunctions->reset(); + $queueForWebhooks->reset(); } } if ($rollback) { - $transaction = $dbForProject->updateDocument( + $transaction = Authorization::skip(fn () => $dbForProject->updateDocument( 'transactions', $transactionId, new Document(['status' => 'failed']) - ); + )); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php index 6bdf6df7ef..72a6a9da6f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php @@ -52,6 +52,7 @@ class Update extends TransactionsUpdate ->param('rollback', false, new Boolean(), 'Rollback transaction?', true) ->inject('response') ->inject('dbForProject') + ->inject('user') ->inject('transactionState') ->inject('queueForDeletes') ->inject('queueForEvents') diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php index a0485b5ef3..7c84295a4f 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php @@ -32,8 +32,7 @@ trait TransactionsBase $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $response['headers']['status-code']); $this->assertArrayHasKey('$id', $response['body']); @@ -49,8 +48,7 @@ trait TransactionsBase $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ + ], $this->getHeaders()), [ 'ttl' => 900 ]); @@ -69,8 +67,7 @@ trait TransactionsBase $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ + ], $this->getHeaders()), [ 'ttl' => 30 // Below minimum ]); @@ -79,8 +76,7 @@ trait TransactionsBase $response = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ + ], $this->getHeaders()), [ 'ttl' => 4000 // Above maximum ]); @@ -109,8 +105,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -299,8 +294,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -410,8 +404,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -542,8 +535,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ + ], $this->getHeaders()), [ 'ttl' => 60 ]); @@ -633,8 +625,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -769,14 +760,12 @@ trait TransactionsBase $txn1 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $txn2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId1 = $txn1['body']['$id']; $transactionId2 = $txn2['body']['$id']; @@ -908,8 +897,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1027,8 +1015,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1190,8 +1177,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1302,8 +1288,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1350,8 +1335,7 @@ trait TransactionsBase $transaction2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId2 = $transaction2['body']['$id']; @@ -1428,8 +1412,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1467,8 +1450,7 @@ trait TransactionsBase $transaction2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId2 = $transaction2['body']['$id']; @@ -1564,8 +1546,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -1703,8 +1684,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1815,8 +1795,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1944,8 +1923,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2047,8 +2025,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2207,8 +2184,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2331,8 +2307,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2479,8 +2454,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2616,8 +2590,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; $this->assertEquals(201, $transaction['headers']['status-code']); @@ -2860,8 +2833,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3037,8 +3009,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3219,8 +3190,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3384,8 +3354,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3547,8 +3516,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3704,8 +3672,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3861,8 +3828,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -4012,8 +3978,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -4163,8 +4128,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -4283,8 +4247,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -4450,8 +4413,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -4532,8 +4494,7 @@ trait TransactionsBase $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ], $this->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php new file mode 100644 index 0000000000..41487b2bb8 --- /dev/null +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php @@ -0,0 +1,670 @@ +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' => 'PermissionsTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $this->permissionsDatabase = $database['body']['$id']; + } + + /** + * Test collection-level create permission check on staging + */ + public function testCollectionCreatePermissionDenied(): void + { + // Create a collection with no create permission for current user + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest1', + 'name' => 'Permission Test 1', + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Try to stage a create operation without permission, should fail + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'create', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc1', + 'data' => ['title' => 'Test Document'], + ]] + ]); + + // This should fail with 401 Unauthorized + if ($staged['headers']['status-code'] !== 401) { + echo "\nDEBUG - Actual response code: " . $staged['headers']['status-code'] . "\n"; + echo "DEBUG - Response body: " . json_encode($staged['body'], JSON_PRETTY_PRINT) . "\n"; + } + $this->assertEquals(401, $staged['headers']['status-code']); + } + + /** + * Test collection-level update permission check on staging + */ + public function testCollectionUpdatePermissionDenied(): void + { + // Create a collection with create but no update permission + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest2', + 'name' => 'Permission Test 2', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create a document first with API key + $doc = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'testDoc2', + 'data' => ['title' => 'Original Title'], + ]); + + $this->assertEquals(201, $doc['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'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Try to stage an update operation without permission, should fail + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'update', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc2', + 'data' => ['title' => 'Updated Title'], + ]] + ]); + + // This should fail with 401 Unauthorized + $this->assertEquals(401, $staged['headers']['status-code']); + } + + /** + * Test collection-level delete permission check on staging + */ + public function testCollectionDeletePermissionDenied(): void + { + // Create a collection with create, read but no delete permission + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest3', + 'name' => 'Permission Test 3', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + $doc = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'testDoc3', + 'data' => ['title' => 'To Be Deleted'], + ]); + + $this->assertEquals(201, $doc['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'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Try to stage a delete operation without permission, should fail + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'delete', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc3', + 'data' => [], + ]] + ]); + + // This should fail with 401 Unauthorized + $this->assertEquals(401, $staged['headers']['status-code']); + } + + /** + * Test document-level update permission grants access when rowSecurity is enabled + * Collection has no update permission, but document does, should succeed + */ + public function testDocumentLevelUpdatePermissionGranted(): void + { + // Create collection with rowSecurity enabled but no update permission at collection level + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest4', + 'name' => 'Permission Test 4', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + ], + 'rowSecurity' => true, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create a document with update permission at document level + $doc = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'testDoc4', + 'data' => ['title' => 'Protected Document'], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $this->assertEquals(201, $doc['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'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Stage an update, should succeed because document has update permission + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'update', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc4', + 'data' => ['title' => 'Trying to Update'], + ]] + ]); + + // This should succeed with 201 because document has update permission + $this->assertEquals(201, $staged['headers']['status-code']); + } + + /** + * Test document-level delete permission grants access when rowSecurity is enabled + * Collection has no delete permission, but document does, should succeed + */ + public function testDocumentLevelDeletePermissionGranted(): void + { + // Create collection with rowSecurity enabled but no delete permission at collection level + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest5', + 'name' => 'Permission Test 5', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'rowSecurity' => true, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create a document with delete permission at document level + $doc = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'rowId' => 'testDoc5', + 'data' => ['title' => 'Can Delete Me'], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $doc['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'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Stage a delete should succeed because document has delete permission + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'delete', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc5', + 'data' => [], + ]] + ]); + + // This should succeed with 201 because document has DELETE permission + $this->assertEquals(201, $staged['headers']['status-code']); + } + + /** + * Test that users cannot set permissions for roles they don't have + */ + public function testCannotSetUnauthorizedRolePermissions(): void + { + // Create a collection + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest6', + 'name' => 'Permission Test 6', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => true, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + // Add attribute + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Try to stage a create with team permissions, current user is not in team + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'create', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc6', + 'data' => [ + 'title' => 'Admin Only Doc', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::team('adminTeam')), + ], + ], + ]] + ]); + + // This should fail with 401 Unauthorized, cannot set permissions for roles you don't have + $this->assertEquals(401, $staged['headers']['status-code']); + $this->assertStringContainsString('Permissions must be one of', $staged['body']['message']); + } + + /** + * Test successful staging when user has the required permissions + */ + public function testSuccessfulStagingWithProperPermissions(): void + { + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest7', + 'name' => 'Permission Test 7', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => true, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Stage a create with permissions for current user's roles, should succeed + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'create', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'testDoc7', + 'data' => [ + 'title' => 'Valid Document', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($this->getUser()['$id'])), + ], + ], + ]] + ]); + + // This should succeed + $this->assertEquals(201, $staged['headers']['status-code']); + $this->assertEquals(1, $staged['body']['operations']); + } + + /** + * Test that non-existent documents cannot be updated in transactions + */ + public function testCannotUpdateNonExistentDocument(): void + { + // Create a collection + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest8', + 'name' => 'Permission Test 8', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Try to update a document that doesn't exist - should fail + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'update', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'nonExistentDoc', + 'data' => ['title' => 'Trying to Update'], + ]] + ]); + + // This should fail with 404 Not Found + $this->assertEquals(404, $staged['headers']['status-code']); + } + + /** + * Test that non-existent documents cannot be deleted in transactions + */ + public function testCannotDeleteNonExistentDocument(): void + { + // Create a collection + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest9', + 'name' => 'Permission Test 9', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Try to delete a document that doesn't exist, should fail + $staged = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'delete', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'nonExistentDoc', + 'data' => [], + ]] + ]); + + // This should fail with 404 Not Found + $this->assertEquals(404, $staged['headers']['status-code']); + } +} diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsCustomClientTest.php b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsCustomClientTest.php new file mode 100644 index 0000000000..fa33aea7b6 --- /dev/null +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsCustomClientTest.php @@ -0,0 +1,14 @@ +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->getHeaders())); $this->assertEquals(201, $response['headers']['status-code']); $this->assertArrayHasKey('$id', $response['body']); @@ -49,8 +48,7 @@ trait TransactionsBase $response = $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->getHeaders()), [ 'ttl' => 900 ]); @@ -69,8 +67,7 @@ trait TransactionsBase $response = $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->getHeaders()), [ 'ttl' => 30 // Below minimum ]); @@ -79,8 +76,7 @@ trait TransactionsBase $response = $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->getHeaders()), [ 'ttl' => 4000 // Above maximum ]); @@ -109,8 +105,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -299,8 +294,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -410,8 +404,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -542,8 +535,7 @@ trait TransactionsBase $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->getHeaders()), [ 'ttl' => 60 ]); @@ -633,8 +625,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -769,14 +760,12 @@ trait TransactionsBase $txn1 = $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->getHeaders())); $txn2 = $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->getHeaders())); $transactionId1 = $txn1['body']['$id']; $transactionId2 = $txn2['body']['$id']; @@ -908,8 +897,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1027,8 +1015,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1190,8 +1177,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1302,8 +1288,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1350,8 +1335,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId2 = $transaction2['body']['$id']; @@ -1428,8 +1412,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1467,8 +1450,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId2 = $transaction2['body']['$id']; @@ -1564,8 +1546,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -1703,8 +1684,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1815,8 +1795,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -1944,8 +1923,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2047,8 +2025,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2207,8 +2184,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2331,8 +2307,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2479,8 +2454,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -2616,8 +2590,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; $this->assertEquals(201, $transaction['headers']['status-code']); @@ -2860,8 +2833,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3037,8 +3009,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3219,8 +3190,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3384,8 +3354,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3547,8 +3516,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3704,8 +3672,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -3861,8 +3828,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -4012,8 +3978,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -4163,8 +4128,7 @@ trait TransactionsBase $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->getHeaders())); $transactionId = $transaction['body']['$id']; @@ -4283,8 +4247,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -4548,8 +4511,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; @@ -4630,8 +4592,7 @@ trait TransactionsBase $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->getHeaders())); $this->assertEquals(201, $transaction['headers']['status-code']); $transactionId = $transaction['body']['$id']; From 9edc0c16d8cb5854679e0b92a9ee7f9f4034c33b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 16:48:44 +1300 Subject: [PATCH 223/274] Realtime txn tests --- .../Realtime/RealtimeCustomClientTest.php | 458 ++++++++++++++++++ 1 file changed, 458 insertions(+) diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 3e57c5e9bc..4e7a2a398a 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -2507,4 +2507,462 @@ class RealtimeCustomClientTest extends Scope $client->close(); } + + public function testChannelDatabaseTransaction() + { + $user = $this->getUser(); + $session = $user['session'] ?? ''; + $projectId = $this->getProject()['$id']; + + $client = $this->getWebsocket(['documents'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $projectId . '=' . $session + ]); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertEquals('connected', $response['type']); + + /** + * Setup Database and Collection + */ + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Transactions DB', + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Test Collection', + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + 'documentSecurity' => true, + ]); + + $collectionId = $collection['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + /** + * Test Transaction Create with Single Document + */ + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 3600 // 1 hour + ]); + + $this->assertEquals(201, $transaction['headers']['status-code'], 'Failed to create transaction: ' . json_encode($transaction['body'])); + $this->assertNotEmpty($transaction['body']['$id']); + + $transactionId = $transaction['body']['$id']; + $documentId = ID::unique(); + + $operationsResponse = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId, + 'action' => 'create', + 'data' => [ + 'name' => 'Transaction Document', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ], + ] + ] + ]); + + $this->assertEquals(201, $operationsResponse['headers']['status-code'], 'Failed to add operations: ' . json_encode($operationsResponse['body'])); + + // Commit transaction + $commitResponse = $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $commitResponse['headers']['status-code'], 'Failed to commit transaction: ' . json_encode($commitResponse['body'])); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertArrayHasKey('data', $response); + $this->assertEquals('event', $response['type']); + $this->assertNotEmpty($response['data']); + $this->assertArrayHasKey('timestamp', $response['data']); + $this->assertContains('documents', $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$collectionId}.rows.{$documentId}.create", $response['data']['events']); + $this->assertNotEmpty($response['data']['payload']); + $this->assertEquals('Transaction Document', $response['data']['payload']['name']); + + /** + * Test Transaction Update + */ + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 3600 + ]); + + $transactionId = $transaction['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId, + 'action' => 'update', + 'data' => [ + 'name' => 'Updated Transaction Document', + ], + ] + ] + ]); + + $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertEquals('event', $response['type']); + $this->assertContains("databases.{$databaseId}.tables.{$collectionId}.rows.{$documentId}.update", $response['data']['events']); + $this->assertEquals('Updated Transaction Document', $response['data']['payload']['name']); + + /** + * Test Transaction Delete + */ + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 3600 + ]); + + $transactionId = $transaction['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId, + 'action' => 'delete', + ] + ] + ]); + + $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $response = json_decode($client->receive(), true); + + $this->assertArrayHasKey('type', $response); + $this->assertEquals('event', $response['type']); + $this->assertContains("databases.{$databaseId}.tables.{$collectionId}.rows.{$documentId}.delete", $response['data']['events']); + + $client->close(); + } + + public function testChannelDatabaseTransactionMultipleOperations() + { + $user = $this->getUser(); + $session = $user['session'] ?? ''; + $projectId = $this->getProject()['$id']; + + $client = $this->getWebsocket(['documents'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $projectId . '=' . $session + ]); + + $response = json_decode($client->receive(), true); + $this->assertEquals('connected', $response['type']); + + /** + * Setup Database and Collection + */ + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Multi-Op DB', + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Test Collection', + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + 'documentSecurity' => true, + ]); + + $collectionId = $collection['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + /** + * Test Multiple Operations in Single Transaction + */ + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 3600 + ]); + + $transactionId = $transaction['body']['$id']; + $documentId1 = ID::unique(); + $documentId2 = ID::unique(); + $documentId3 = ID::unique(); + + $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId1, + 'action' => 'create', + 'data' => [ + 'name' => 'Doc 1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ], + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId2, + 'action' => 'create', + 'data' => [ + 'name' => 'Doc 2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ], + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId3, + 'action' => 'create', + 'data' => [ + 'name' => 'Doc 3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ], + ] + ] + ]); + + $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + // Should receive 3 events, one for each document + $response1 = json_decode($client->receive(), true); + $response2 = json_decode($client->receive(), true); + $response3 = json_decode($client->receive(), true); + + $this->assertEquals('event', $response1['type']); + $this->assertEquals('event', $response2['type']); + $this->assertEquals('event', $response3['type']); + + $receivedDocIds = [ + $response1['data']['payload']['$id'], + $response2['data']['payload']['$id'], + $response3['data']['payload']['$id'], + ]; + + $this->assertContains($documentId1, $receivedDocIds); + $this->assertContains($documentId2, $receivedDocIds); + $this->assertContains($documentId3, $receivedDocIds); + + $client->close(); + } + + public function testChannelDatabaseTransactionRollback() + { + $user = $this->getUser(); + $session = $user['session'] ?? ''; + $projectId = $this->getProject()['$id']; + + $client = $this->getWebsocket(['documents'], [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $projectId . '=' . $session + ]); + + $response = json_decode($client->receive(), true); + $this->assertEquals('connected', $response['type']); + + /** + * Setup Database and Collection + */ + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Rollback DB', + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Test Collection', + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + 'documentSecurity' => true, + ]); + + $collectionId = $collection['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + /** + * Test Transaction Rollback - Should NOT trigger realtime events + */ + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'ttl' => 3600 + ]); + + $transactionId = $transaction['body']['$id']; + $documentId = ID::unique(); + + $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'databaseId' => $databaseId, + 'tableId' => $collectionId, + 'rowId' => $documentId, + 'action' => 'create', + 'data' => ['name' => 'Rollback Document'], + ] + ] + ]); + + // Rollback transaction + $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rollback' => true + ]); + + // Wait a bit to ensure no event is received + sleep(1); + + try { + $client->receive(1); // 1 second timeout + $this->fail('Should not receive any event after rollback'); + } catch (TimeoutException $e) { + // Expected - no event should be triggered + $this->assertTrue(true); + } + + $client->close(); + } } From 3e77810e13d2ec90b78627860b39cf0af292e6dd Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 19:26:22 +1300 Subject: [PATCH 224/274] Fix existence check for tracked + staged ops --- src/Appwrite/Databases/TransactionState.php | 11 +- .../Http/Databases/Transactions/Create.php | 2 +- .../Transactions/Operations/Create.php | 72 ++++++----- .../Http/Databases/Transactions/Update.php | 3 +- .../Transactions/Operations/Create.php | 1 + .../TablesDB/Transactions/PermissionsBase.php | 113 ++++++++++++++++++ .../Realtime/RealtimeCustomClientTest.php | 7 +- 7 files changed, 167 insertions(+), 42 deletions(-) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 645c25bf76..2d35ce92ae 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -7,6 +7,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Exception\Timeout; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; /** * Service for managing transaction state and providing transaction-aware document operations @@ -351,17 +352,17 @@ class TransactionState */ private function getTransactionState(string $transactionId): array { - $transaction = $this->dbForProject->getDocument('transactions', $transactionId); + $transaction = Authorization::skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId)); if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') { return []; } // Fetch operations ordered by sequence to replay in exact order - $operations = $this->dbForProject->find('transactionLogs', [ + $operations = Authorization::skip(fn () => $this->dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), Query::orderAsc(), Query::limit(PHP_INT_MAX) - ]); + ])); $state = []; @@ -381,6 +382,10 @@ class TransactionState case 'create': $docId = $documentId ?? ($data['$id'] ?? null); if ($docId) { + // Ensure document has the correct $id + if (!isset($data['$id'])) { + $data['$id'] = $docId; + } $state[$collectionId][$docId] = [ 'action' => 'create', 'document' => new Document($data), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index e67c1d08d0..744ad33540 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -9,9 +9,9 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\DateTime; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\Authorization; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Range; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index f7314fb87c..5f6fa4f1a6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations; use Appwrite\Auth\Auth; +use Appwrite\Databases\TransactionState; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Action; use Appwrite\SDK\AuthType; @@ -60,11 +61,12 @@ class Create extends Action ->param('operations', [], new ArrayList(new Operation(type: 'legacy')), 'Array of staged operations.', true) ->inject('response') ->inject('dbForProject') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } - public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, array $plan): void + public function action(string $transactionId, array $operations, UtopiaResponse $response, Database $dbForProject, TransactionState $transactionState, array $plan): void { if (empty($operations)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); @@ -88,7 +90,7 @@ class Create extends Action $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $databases = $collections = $staged = []; + $databases = $collections = $staged = $dependants = []; foreach ($operations as $operation) { if (!$isAPIKey && !$isPrivilegedUser && \in_array($operation['action'], [ 'bulkCreate', @@ -122,27 +124,26 @@ class Create extends Action } } - // For update, upsert, delete, check document existence first + // For update, upsert, delete, increment, decrement, check document existence first $document = null; - if (\in_array($operation['action'], ['update', 'delete', 'upsert'])) { + if (\in_array($operation['action'], ['update', 'delete', 'upsert', 'increment', 'decrement'])) { $documentId = $operation[$this->getResourceId()] ?? null; if (empty($documentId)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Document ID is required for ' . $operation['action'] . ' operations'); } - $document = Authorization::skip(fn () => $dbForProject->getDocument( - 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), - $documentId - )); + $collectionKey = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); + $isDependant = isset($dependants[$collectionKey][$documentId]); - if ($document->isEmpty() && \in_array($operation['action'], ['update', 'delete'])) { + $document = $transactionState->getDocument($collectionKey, $documentId, $transactionId); + if ($document->isEmpty() && !$isDependant && $operation['action'] !== 'upsert') { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } } $permissionType = match ($operation['action']) { 'create', 'bulkCreate' => Database::PERMISSION_CREATE, - 'update', 'bulkUpdate' => Database::PERMISSION_UPDATE, + 'update', 'bulkUpdate', 'increment', 'decrement' => Database::PERMISSION_UPDATE, 'delete', 'bulkDelete' => Database::PERMISSION_DELETE, 'upsert', 'bulkUpsert' => ($document && !$document->isEmpty()) ? Database::PERMISSION_UPDATE : Database::PERMISSION_CREATE, default => throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid action: ' . $operation['action']) @@ -173,23 +174,21 @@ class Create extends Action // Users can only set permissions for roles they have if (isset($operation['data']['$permissions'])) { - $permissions = $operation['data']['$permissions'] ?? null; - if (!\is_null($permissions)) { - $roles = Authorization::getRoles(); - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); - } + $permissions = $operation['data']['$permissions']; + $roles = Authorization::getRoles(); + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -205,17 +204,26 @@ class Create extends Action 'action' => $operation['action'], 'data' => $operation['data'] ?? [], ]); + + // Track create operations for dependent update/increment/decrement/delete operations in same batch + if ($operation['action'] === 'create') { + $collectionKey = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); + $documentId = $operation[$this->getResourceId()] ?? null; + if ($documentId) { + $dependants[$collectionKey][$documentId] = true; + } + } } - $transaction = $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { - Authorization::skip(fn () => $dbForProject->createDocuments('transactionLogs', $staged)); - return Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute( + $transaction = Authorization::skip(fn () => $dbForProject->withTransaction(function () use ($dbForProject, $transactionId, $staged, $existing, $operations) { + $dbForProject->createDocuments('transactionLogs', $staged); + return $dbForProject->increaseDocumentAttribute( 'transactions', $transactionId, 'operations', \count($operations) - )); - }); + ); + })); $response ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 0d7ca5c252..d522c699a8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -14,16 +14,15 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Transaction as TransactionException; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php index eab0fed161..d85020b2bc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -52,6 +52,7 @@ class Create extends OperationsCreate ->param('operations', [], new ArrayList(new Operation(type: 'tablesdb')), 'Array of staged operations.', true) ->inject('response') ->inject('dbForProject') + ->inject('transactionState') ->inject('plan') ->callback($this->action(...)); } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php index 41487b2bb8..9dac59e2ec 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php @@ -667,4 +667,117 @@ trait PermissionsBase // This should fail with 404 Not Found $this->assertEquals(404, $staged['headers']['status-code']); } + + /** + * Test that a document created in one batch can be updated in a subsequent batch within the same transaction + * This validates the transactionState->getDocument() fix for cross-batch dependencies + */ + public function testCanUpdateDocumentCreatedInPreviousBatch(): void + { + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest10', + 'name' => 'Permission Test 10', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + + // Batch 1: Create a document + $batch1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'create', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'crossBatchDoc', + 'data' => [ + 'title' => 'Initial Title', + ], + ]] + ]); + + $this->assertEquals(201, $batch1['headers']['status-code']); + $this->assertEquals(1, $batch1['body']['operations']); + + // Batch 2: Update the document created in batch 1 + $batch2 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'update', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'crossBatchDoc', + 'data' => [ + 'title' => 'Updated Title', + ], + ]] + ]); + + // This should succeed with 201 because transactionState finds the staged document from batch 1 + $this->assertEquals(201, $batch2['headers']['status-code']); + $this->assertEquals(2, $batch2['body']['operations']); + + // Batch 3: Delete the same document + $batch3 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transaction['body']['$id'] . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [[ + 'action' => 'delete', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'crossBatchDoc', + 'data' => [], + ]] + ]); + + // This should also succeed with 201 + $this->assertEquals(201, $batch3['headers']['status-code']); + $this->assertEquals(3, $batch3['body']['operations']); + + // Rollback to clean up + $rollback = $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transaction['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rollback' => true, + ]); + + $this->assertEquals(200, $rollback['headers']['status-code']); + } } diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 7152925e3a..5a61ae7dc5 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -28,7 +28,7 @@ class RealtimeCustomClientTest extends Scope $userId = $user['$id'] ?? ''; $session = $user['session'] ?? ''; - $headers = [ + $headers = [ 'origin' => 'http://localhost', 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session ]; @@ -3068,7 +3068,7 @@ class RealtimeCustomClientTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), [ 'documentId' => ID::unique(), - 'data' => [ 'name' => 'L2' ], + 'data' => ['name' => 'L2'], 'permissions' => [ Permission::read(Role::any()), Permission::update(Role::any()), @@ -3082,7 +3082,7 @@ class RealtimeCustomClientTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), [ 'documentId' => ID::unique(), - 'data' => [ 'name' => 'L1' ], + 'data' => ['name' => 'L1'], 'permissions' => [ Permission::read(Role::any()), Permission::update(Role::any()), @@ -3116,5 +3116,4 @@ class RealtimeCustomClientTest extends Scope $client->close(); } - } From 57e3e8f5f4104508dfa368083ec45efa54748194 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 21:10:50 +1300 Subject: [PATCH 225/274] Fix test scope --- .../Legacy/Transactions/TransactionsBase.php | 30 +++---------------- .../TransactionsConsoleClientTest.php | 3 +- .../TransactionsCustomClientTest.php | 3 +- .../TransactionsCustomServerTest.php | 3 +- .../Transactions/TransactionsBase.php | 30 +++---------------- 5 files changed, 14 insertions(+), 55 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php index 7c84295a4f..836f1ca966 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php @@ -1416,7 +1416,7 @@ trait TransactionsBase $transactionId = $transaction['body']['$id']; - // Try to update non-existent document + // Try to update non-existent document - should fail at staging time with early validation $response = $this->client->call(Client::METHOD_POST, "/databases/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1433,20 +1433,9 @@ trait TransactionsBase ] ]); - $this->assertEquals(201, $response['headers']['status-code']); // Operation added + $this->assertEquals(404, $response['headers']['status-code']); // Document not found at staging time - // Commit should fail - $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'commit' => true - ]); - - $this->assertEquals(404, $response['headers']['status-code']); // Document not found - - // Test delete non-existent document + // Test delete non-existent document - should also fail at staging time with early validation $transaction2 = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1470,18 +1459,7 @@ trait TransactionsBase ] ]); - $this->assertEquals(201, $response['headers']['status-code']); - - // Commit should fail - $response = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId2}", array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'commit' => true - ]); - - $this->assertEquals(404, $response['headers']['status-code']); // Document not found + $this->assertEquals(404, $response['headers']['status-code']); // Document not found at staging time } /** diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php index 427e79fb3a..ef6e9d15d0 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsConsoleClientTest.php @@ -3,9 +3,10 @@ namespace Tests\E2E\Services\Databases\Legacy\Transactions; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideConsole; -class TransactionsConsoleClientTest +class TransactionsConsoleClientTest extends Scope { use TransactionsBase; use ProjectCustom; diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomClientTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomClientTest.php index 3ee685d778..cdc9a325dc 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomClientTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomClientTest.php @@ -3,9 +3,10 @@ namespace Tests\E2E\Services\Databases\Legacy\Transactions; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; -class TransactionsCustomClientTest +class TransactionsCustomClientTest extends Scope { use TransactionsBase; use ProjectCustom; diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomServerTest.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomServerTest.php index 425f731ef3..7b3d092128 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomServerTest.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsCustomServerTest.php @@ -3,9 +3,10 @@ namespace Tests\E2E\Services\Databases\Legacy\Transactions; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -class TransactionsCustomServerTest +class TransactionsCustomServerTest extends Scope { use TransactionsBase; use ProjectCustom; diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index c972137cee..1ea8d0cc9a 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -1416,7 +1416,7 @@ trait TransactionsBase $transactionId = $transaction['body']['$id']; - // Try to update non-existent row + // Try to update non-existent row - should fail at staging time with early validation $response = $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1433,20 +1433,9 @@ trait TransactionsBase ] ]); - $this->assertEquals(201, $response['headers']['status-code']); // Operation added + $this->assertEquals(404, $response['headers']['status-code']); // Document not found at staging time - // Commit should fail - $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(404, $response['headers']['status-code']); // Document not found - - // Test delete non-existent row + // Test delete non-existent row - should also fail at staging time with early validation $transaction2 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1470,18 +1459,7 @@ trait TransactionsBase ] ]); - $this->assertEquals(201, $response['headers']['status-code']); - - // Commit should fail - $response = $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(404, $response['headers']['status-code']); // Document not found + $this->assertEquals(404, $response['headers']['status-code']); // Document not found at staging time } /** From 52892ee46e1d54d43e80c70016b0c0f7cb04f023 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 22:32:01 +1300 Subject: [PATCH 226/274] Upgrade for breaking changes --- .../Http/Databases/Collections/Documents/Bulk/Upsert.php | 2 +- .../Databases/Http/Databases/Collections/Documents/Upsert.php | 2 +- src/Appwrite/Platform/Workers/StatsResources.php | 2 +- src/Appwrite/Platform/Workers/StatsUsage.php | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 4ce3990a38..03e4450f68 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -113,7 +113,7 @@ class Upsert extends Action try { $modified = $dbForProject->withPreserveDates(function () use ($dbForProject, $database, $collection, $documents, $plan, &$upserted) { - return $dbForProject->createOrUpdateDocuments( + return $dbForProject->upsertDocuments( 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documents, onNext: function (Document $document) use ($plan, &$upserted) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 3ac5e704e3..23fbf205dc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -243,7 +243,7 @@ class Upsert extends Action $upserted = []; try { $dbForProject->withPreserveDates(function () use (&$upserted, $dbForProject, $database, $collection, $newDocument) { - return $dbForProject->createOrUpdateDocuments( + return $dbForProject->upsertDocuments( 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), [$newDocument], onNext: function (Document $document) use (&$upserted) { diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 0c8f11c07b..5ec306c5bb 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -462,7 +462,7 @@ class StatsResources extends Action }); try { - $dbForLogs->createOrUpdateDocuments( + $dbForLogs->upsertDocuments( 'stats', $this->documents, ); diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index bd34d53b00..4da63b6ae3 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -452,7 +452,7 @@ class StatsUsage extends Action return strcmp($a['time'], $b['time']); }); - $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); + $dbForProject->upsertDocumentsWithIncrease('stats', 'value', $projectStats['stats']); Console::success('Batch successfully written to DB'); } catch (Throwable $e) { Console::error('Error processing stats: ' . $e->getMessage()); @@ -532,7 +532,7 @@ class StatsUsage extends Action return strcmp($a['time'], $b['time']); }); - $dbForLogs->createOrUpdateDocumentsWithIncrease( + $dbForLogs->upsertDocumentsWithIncrease( 'stats', 'value', $this->statDocuments From 6881df6ae53e3ce529e41e0c581db8b2ca4e5929 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 22:55:07 +1300 Subject: [PATCH 227/274] Fix expectations --- tests/e2e/Services/Databases/Legacy/DatabasesBase.php | 3 +-- tests/e2e/Services/Databases/TablesDB/DatabasesBase.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index a432bc0acd..a8bd53ae3f 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -4715,8 +4715,7 @@ trait DatabasesBase ], ]); - $this->assertEquals(400, $documents['headers']['status-code']); - $this->assertEquals('Invalid query: Cannot query nested attribute on: library', $documents['body']['message']); + $this->assertEquals(200, $documents['headers']['status-code']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/attributes/library', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 336193f5d9..0aa4f4ba20 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -4642,8 +4642,7 @@ trait DatabasesBase ], ]); - $this->assertEquals(400, $rows['headers']['status-code']); - $this->assertEquals('Invalid query: Cannot query nested attribute on: library', $rows['body']['message']); + $this->assertEquals(200, $rows['headers']['status-code']); $response = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/library', array_merge([ 'content-type' => 'application/json', From 12f6b07fbf9a361c59cf90a65359a23621b34555 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 4 Oct 2025 23:20:08 +1300 Subject: [PATCH 228/274] Update database --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 475d66c44b..7446cf31a6 100644 --- a/composer.lock +++ b/composer.lock @@ -3639,12 +3639,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "d97d181d7c2ed267b0b57ba421d3364faff64f8e" + "reference": "7e39c677a20a9f4ac4d84007882a460fbee206f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/d97d181d7c2ed267b0b57ba421d3364faff64f8e", - "reference": "d97d181d7c2ed267b0b57ba421d3364faff64f8e", + "url": "https://api.github.com/repos/utopia-php/database/zipball/7e39c677a20a9f4ac4d84007882a460fbee206f6", + "reference": "7e39c677a20a9f4ac4d84007882a460fbee206f6", "shasum": "" }, "require": { @@ -3687,7 +3687,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/feat-relationship-updates" }, - "time": "2025-10-03T12:05:20+00:00" + "time": "2025-10-04T10:17:19+00:00" }, { "name": "utopia-php/detector", From 5be2489e5573cbf017738e847be922cbd77a39c7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 5 Oct 2025 00:06:56 +1300 Subject: [PATCH 229/274] Check query result --- tests/e2e/Services/Databases/Legacy/DatabasesBase.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index a8bd53ae3f..bfc56567ef 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -4711,11 +4711,16 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ + Query::select(['library.*'])->toString(), Query::equal('library.libraryName', ['Library 1'])->toString(), ], ]); $this->assertEquals(200, $documents['headers']['status-code']); + $this->assertEquals(1, $documents['body']['total']); + $this->assertCount(1, $documents['body']['documents']); + $this->assertEquals('Library 1', $documents['body']['documents'][0]['library']['libraryName']); + $this->assertEquals($person1['body']['$id'], $documents['body']['documents'][0]['$id']); $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/attributes/library', array_merge([ 'content-type' => 'application/json', From ce52dba4197512d736cf0b5da78f255fc0e6f73a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 5 Oct 2025 00:09:26 +1300 Subject: [PATCH 230/274] Update to release --- composer.json | 2 +- composer.lock | 69 ++++++++++++++++++++++----------------------------- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/composer.json b/composer.json index d51ff828a3..9042cb088f 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-relationship-updates as 1.5.1", + "utopia-php/database": "2.*", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", "utopia-php/dns": "0.3.*", diff --git a/composer.lock b/composer.lock index 7446cf31a6..4166face48 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ae57f581b8351bbabd8cf007d514e2b4", + "content-hash": "773efb29b9b584b1249790e0016c823a", "packages": [ { "name": "adhocore/jwt", @@ -3293,16 +3293,16 @@ }, { "name": "utopia-php/abuse", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be" + "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/c5e2232033b507a07f72180dc56d37e1872ee7be", - "reference": "c5e2232033b507a07f72180dc56d37e1872ee7be", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd591568791556d246d901d6aaf9935ab02c3f9a", + "reference": "cd591568791556d246d901d6aaf9935ab02c3f9a", "shasum": "" }, "require": { @@ -3310,7 +3310,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "1.*" + "utopia-php/database": "2.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3338,9 +3338,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/1.0.0" + "source": "https://github.com/utopia-php/abuse/tree/1.0.1" }, - "time": "2025-08-13T09:12:54+00:00" + "time": "2025-09-04T12:46:54+00:00" }, { "name": "utopia-php/analytics", @@ -3390,21 +3390,21 @@ }, { "name": "utopia-php/audit", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb" + "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/c0ed75f4d068f1f6c2e7149a909490d4214e72bb", - "reference": "c0ed75f4d068f1f6c2e7149a909490d4214e72bb", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", + "reference": "5ef26d6a2ab2db7bb86288a1a6ef970307b46f22", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "1.*" + "utopia-php/database": "2.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3431,9 +3431,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/1.0.0" + "source": "https://github.com/utopia-php/audit/tree/1.0.1" }, - "time": "2025-08-13T09:09:00+00:00" + "time": "2025-09-04T12:46:43+00:00" }, { "name": "utopia-php/cache", @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "dev-feat-relationship-updates", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "7e39c677a20a9f4ac4d84007882a460fbee206f6" + "reference": "1e3f40b806fbf2f146fa12f2d5e7ea09cda3dbb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/7e39c677a20a9f4ac4d84007882a460fbee206f6", - "reference": "7e39c677a20a9f4ac4d84007882a460fbee206f6", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1e3f40b806fbf2f146fa12f2d5e7ea09cda3dbb3", + "reference": "1e3f40b806fbf2f146fa12f2d5e7ea09cda3dbb3", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/feat-relationship-updates" + "source": "https://github.com/utopia-php/database/tree/2.3.0" }, - "time": "2025-10-04T10:17:19+00:00" + "time": "2025-10-04T11:07:33+00:00" }, { "name": "utopia-php/detector", @@ -4187,16 +4187,16 @@ }, { "name": "utopia-php/migration", - "version": "1.2.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093" + "reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/42ff497c5231f5a727d1e229419ff1d2195d8093", - "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/6fb6f8f032cd34c3c65728a55d494adeac2ff038", + "reference": "6fb6f8f032cd34c3c65728a55d494adeac2ff038", "shasum": "" }, "require": { @@ -4204,7 +4204,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "1.*", + "utopia-php/database": "2.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4237,9 +4237,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.2.0" + "source": "https://github.com/utopia-php/migration/tree/1.1.0" }, - "time": "2025-09-24T10:32:24+00:00" + "time": "2025-09-10T05:45:30+00:00" }, { "name": "utopia-php/orchestration", @@ -8515,18 +8515,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/database", - "version": "dev-feat-relationship-updates", - "alias": "1.5.1", - "alias_normalized": "1.5.1.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/database": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From 236585c20b6bf9435955dce5c896bf39fb995a0e Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 4 Oct 2025 18:38:35 +0530 Subject: [PATCH 231/274] fix: sanitize 5xx errors on realtime when running in production. --- app/realtime.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index bb0d4da78c..e0a776b85b 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -604,11 +604,18 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $code = 500; } + $message = $th->getMessage(); + + // sanitize 5xx errors + if ($code >= 500 && !App::isDevelopment()) { + $message = 'Error: Server Error'; + } + $response = [ 'type' => 'error', 'data' => [ 'code' => $code, - 'message' => $th->getMessage() + 'message' => $message ] ]; @@ -705,11 +712,19 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Message type is not valid.'); } } catch (Throwable $th) { + $code = $th->getCode(); + $message = $th->getMessage(); + + // sanitize 5xx errors + if ($code >= 500 && !App::isDevelopment()) { + $message = 'Error: Server Error'; + } + $response = [ 'type' => 'error', 'data' => [ - 'code' => $th->getCode(), - 'message' => $th->getMessage() + 'code' => $code, + 'message' => $message ] ]; From f5af1ee7bb0302fe0feeffb470fcfdacd4c478d2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 4 Oct 2025 21:09:19 +0530 Subject: [PATCH 232/274] update more --- app/config/platforms.php | 2 +- composer.lock | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index aeed725bf7..e5a297fb0c 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '21.0.0', + 'version' => '20.0.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, diff --git a/composer.lock b/composer.lock index 95288fc01d..fc95ef3e12 100644 --- a/composer.lock +++ b/composer.lock @@ -1159,16 +1159,16 @@ }, { "name": "open-telemetry/api", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2" + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/ee17d937652eca06c2341b6fadc0f74c1c1a5af2", - "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522", + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522", "shasum": "" }, "require": { @@ -1188,7 +1188,7 @@ ] }, "branch-alias": { - "dev-main": "1.4.x-dev" + "dev-main": "1.7.x-dev" } }, "autoload": { @@ -1225,7 +1225,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-19T00:05:49+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/context", @@ -1415,22 +1415,22 @@ }, { "name": "open-telemetry/sdk", - "version": "1.8.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89" + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/105c6e81e3d86150bd5704b00c7e4e165e957b89", - "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.6", + "open-telemetry/api": "^1.7", "open-telemetry/context": "^1.4", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", @@ -1465,7 +1465,7 @@ ] }, "branch-alias": { - "dev-main": "1.0.x-dev" + "dev-main": "1.9.x-dev" } }, "autoload": { @@ -1508,7 +1508,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-19T00:05:49+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/sem-conv", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd" + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07a7d6276bd684b49469ad7b9e8c3c962121c6fd", - "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.4.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.3" }, - "time": "2025-10-01T03:23:04+00:00" + "time": "2025-10-01T06:25:19+00:00" }, { "name": "doctrine/annotations", From 81bd4cbe504c445073ee5b1c740aed11b8f32085 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sun, 5 Oct 2025 00:26:37 +0530 Subject: [PATCH 233/274] chore: update composer dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated open-telemetry/api from 1.6.0 to 1.7.0 - Updated open-telemetry/sdk from 1.8.0 to 1.9.0 - Updated utopia-php/domains from 0.8.0 to 0.8.1 - Updated appwrite/sdk-generator from 1.4.2 to 1.4.3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- composer.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 95288fc01d..988a25c214 100644 --- a/composer.lock +++ b/composer.lock @@ -1159,16 +1159,16 @@ }, { "name": "open-telemetry/api", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2" + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/ee17d937652eca06c2341b6fadc0f74c1c1a5af2", - "reference": "ee17d937652eca06c2341b6fadc0f74c1c1a5af2", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522", + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522", "shasum": "" }, "require": { @@ -1188,7 +1188,7 @@ ] }, "branch-alias": { - "dev-main": "1.4.x-dev" + "dev-main": "1.7.x-dev" } }, "autoload": { @@ -1225,7 +1225,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-19T00:05:49+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/context", @@ -1415,22 +1415,22 @@ }, { "name": "open-telemetry/sdk", - "version": "1.8.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89" + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/105c6e81e3d86150bd5704b00c7e4e165e957b89", - "reference": "105c6e81e3d86150bd5704b00c7e4e165e957b89", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.6", + "open-telemetry/api": "^1.7", "open-telemetry/context": "^1.4", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", @@ -1465,7 +1465,7 @@ ] }, "branch-alias": { - "dev-main": "1.0.x-dev" + "dev-main": "1.9.x-dev" } }, "autoload": { @@ -1508,7 +1508,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-19T00:05:49+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/sem-conv", @@ -3792,16 +3792,16 @@ }, { "name": "utopia-php/domains", - "version": "0.8.0", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "650463d2a1525273eb03223c48da9fb1a768bbf7" + "reference": "d5f903e93c105407da6374e411c4805b7decd8a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/650463d2a1525273eb03223c48da9fb1a768bbf7", - "reference": "650463d2a1525273eb03223c48da9fb1a768bbf7", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/d5f903e93c105407da6374e411c4805b7decd8a8", + "reference": "d5f903e93c105407da6374e411c4805b7decd8a8", "shasum": "" }, "require": { @@ -3847,9 +3847,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.8.0" + "source": "https://github.com/utopia-php/domains/tree/0.8.1" }, - "time": "2025-05-16T10:03:59+00:00" + "time": "2025-10-03T11:58:53+00:00" }, { "name": "utopia-php/dsn", @@ -5004,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd" + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07a7d6276bd684b49469ad7b9e8c3c962121c6fd", - "reference": "07a7d6276bd684b49469ad7b9e8c3c962121c6fd", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", "shasum": "" }, "require": { @@ -5049,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.4.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.3" }, - "time": "2025-10-01T03:23:04+00:00" + "time": "2025-10-01T06:25:19+00:00" }, { "name": "doctrine/annotations", From 0314547c95bd0ba9828367baaaacd1db4015a2f4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sun, 5 Oct 2025 00:39:54 +0530 Subject: [PATCH 234/274] fix: permission issues --- src/Appwrite/Platform/Tasks/SDKs.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 37ed2c9f3b..63ffddf950 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -281,7 +281,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // Make sure we have a clean slate. // Otherwise, all files in this dir will be pushed, // regardless of whether they were just generated or not. - \exec('rm -rf ' . $result); + \exec('chmod -R u+w ' . $result . ' 2>/dev/null; rm -rf ' . $result); try { $sdk->generate($result); @@ -298,7 +298,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $repoBranch = $language['repoBranch'] ?? 'main'; if ($git && !empty($gitUrl)) { - // TODO: fix the temporary 2>/dev/null || true - added due to permission issues when removing files \exec('rm -rf ' . $target . ' && \ mkdir -p ' . $target . ' && \ cd ' . $target . ' && \ @@ -310,7 +309,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND git checkout ' . $gitBranch . ' || git checkout -b ' . $gitBranch . ' && \ git fetch origin ' . $gitBranch . ' || git push -u origin ' . $gitBranch . ' && \ git pull origin ' . $gitBranch . ' && \ - rm -rf ' . $target . '/* 2>/dev/null || true && \ + find . -mindepth 1 ! -path "./.git*" -delete && \ cp -r ' . $result . '/. ' . $target . '/ && \ git add . && \ git commit -m "' . $message . '" && \ @@ -377,7 +376,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND } } - \exec('rm -rf ' . $target); + \exec('chmod -R u+w ' . $target . ' && rm -rf ' . $target); Console::success("Remove temp directory '{$target}' for {$language['name']} SDK"); } From 24f6da56100c29a7712cd114faafbfb7559d4dde Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 6 Oct 2025 13:47:21 +1300 Subject: [PATCH 235/274] Add assertions on tables DB --- tests/e2e/Services/Databases/TablesDB/DatabasesBase.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 0aa4f4ba20..6c1c09f9d8 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -4638,11 +4638,16 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ + Query::select(['library.*']), Query::equal('library.libraryName', ['Library 1'])->toString(), ], ]); $this->assertEquals(200, $rows['headers']['status-code']); + $this->assertEquals(1, $rows['body']['total']); + $this->assertCount(1, $rows['body']['rows']); + $this->assertEquals('Library 1', $rows['body']['rows'][0]['library']['libraryName']); + $this->assertEquals($person1['body']['$id'], $rows['body']['rows'][0]['$id']); $response = $this->client->call(Client::METHOD_DELETE, '/tablesdb/' . $databaseId . '/tables/' . $person['body']['$id'] . '/columns/library', array_merge([ 'content-type' => 'application/json', From 2566f4d98eb30bb4d2e0ba6e02ad1af1ccda4a4f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 6 Oct 2025 14:16:01 +1300 Subject: [PATCH 236/274] Add tostring --- tests/e2e/Services/Databases/TablesDB/DatabasesBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 6c1c09f9d8..d3aa50a99a 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -4638,7 +4638,7 @@ trait DatabasesBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - Query::select(['library.*']), + Query::select(['library.*'])->toString(), Query::equal('library.libraryName', ['Library 1'])->toString(), ], ]); From 1da034d00cf254cf4300d6631c3e58b00ae6b949 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 6 Oct 2025 17:08:19 +1300 Subject: [PATCH 237/274] Update database --- composer.lock | 258 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 239 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 4166face48..f6cbc491f6 100644 --- a/composer.lock +++ b/composer.lock @@ -959,6 +959,83 @@ }, "time": "2025-08-20T17:20:16+00:00" }, + { + "name": "mongodb/mongodb", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "f399d24905dd42f97dfe0af9706129743ef247ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/f399d24905dd42f97dfe0af9706129743ef247ac", + "reference": "f399d24905dd42f97dfe0af9706129743ef247ac", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "ext-mongodb": "^2.1", + "php": "^8.1", + "psr/log": "^1.1.4|^2|^3", + "symfony/polyfill-php85": "^1.32" + }, + "replace": { + "mongodb/builder": "*" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0", + "phpunit/phpunit": "^10.5.35", + "rector/rector": "^1.2", + "squizlabs/php_codesniffer": "^3.7", + "vimeo/psalm": "6.5.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + }, + { + "name": "Jérôme Tamarelle", + "email": "jerome.tamarelle@mongodb.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/2.1.1" + }, + "time": "2025-08-13T20:50:05+00:00" + }, { "name": "mustangostang/spyc", "version": "0.6.3", @@ -1927,16 +2004,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.47", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", "shasum": "" }, "require": { @@ -2017,7 +2094,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" }, "funding": [ { @@ -2033,7 +2110,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2025-10-06T01:07:24+00:00" }, { "name": "psr/container", @@ -3017,6 +3094,86 @@ ], "time": "2025-07-08T02:45:35+00:00" }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" + }, { "name": "symfony/service-contracts", "version": "v3.6.0", @@ -3635,29 +3792,31 @@ }, { "name": "utopia-php/database", - "version": "2.3.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "1e3f40b806fbf2f146fa12f2d5e7ea09cda3dbb3" + "reference": "33ca4266c7f79a037604fc889db7d79ed37d73fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/1e3f40b806fbf2f146fa12f2d5e7ea09cda3dbb3", - "reference": "1e3f40b806fbf2f146fa12f2d5e7ea09cda3dbb3", + "url": "https://api.github.com/repos/utopia-php/database/zipball/33ca4266c7f79a037604fc889db7d79ed37d73fd", + "reference": "33ca4266c7f79a037604fc889db7d79ed37d73fd", "shasum": "" }, "require": { "ext-mbstring": "*", + "ext-mongodb": "*", "ext-pdo": "*", "php": ">=8.1", "utopia-php/cache": "0.13.*", "utopia-php/framework": "0.33.*", + "utopia-php/mongo": "0.10.*", "utopia-php/pools": "0.8.*" }, "require-dev": { "fakerphp/faker": "1.23.*", - "laravel/pint": "1.*", + "laravel/pint": "*", "pcov/clobber": "2.*", "phpstan/phpstan": "1.*", "phpunit/phpunit": "9.*", @@ -3685,9 +3844,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.3.0" + "source": "https://github.com/utopia-php/database/tree/2.3.1" }, - "time": "2025-10-04T11:07:33+00:00" + "time": "2025-10-06T04:04:42+00:00" }, { "name": "utopia-php/detector", @@ -3792,16 +3951,16 @@ }, { "name": "utopia-php/domains", - "version": "0.8.0", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "650463d2a1525273eb03223c48da9fb1a768bbf7" + "reference": "d5f903e93c105407da6374e411c4805b7decd8a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/650463d2a1525273eb03223c48da9fb1a768bbf7", - "reference": "650463d2a1525273eb03223c48da9fb1a768bbf7", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/d5f903e93c105407da6374e411c4805b7decd8a8", + "reference": "d5f903e93c105407da6374e411c4805b7decd8a8", "shasum": "" }, "require": { @@ -3847,9 +4006,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.8.0" + "source": "https://github.com/utopia-php/domains/tree/0.8.1" }, - "time": "2025-05-16T10:03:59+00:00" + "time": "2025-10-03T11:58:53+00:00" }, { "name": "utopia-php/dsn", @@ -4241,6 +4400,67 @@ }, "time": "2025-09-10T05:45:30+00:00" }, + { + "name": "utopia-php/mongo", + "version": "0.10.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/mongo.git", + "reference": "ecfad6aad2e2e3fe5899ac2ebf1009a21b4d6b18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/mongo/zipball/ecfad6aad2e2e3fe5899ac2ebf1009a21b4d6b18", + "reference": "ecfad6aad2e2e3fe5899ac2ebf1009a21b4d6b18", + "shasum": "" + }, + "require": { + "ext-mongodb": "2.1.*", + "mongodb/mongodb": "2.1.*", + "php": ">=8.0", + "ramsey/uuid": "4.9.*" + }, + "require-dev": { + "fakerphp/faker": "1.*", + "laravel/pint": "*", + "phpstan/phpstan": "*", + "phpunit/phpunit": "9.*", + "swoole/ide-helper": "5.1.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Mongo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Wess", + "email": "wess@appwrite.io" + } + ], + "description": "A simple library to manage Mongo database", + "keywords": [ + "database", + "mongo", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/mongo/issues", + "source": "https://github.com/utopia-php/mongo/tree/0.10.0" + }, + "time": "2025-10-02T04:50:07+00:00" + }, { "name": "utopia-php/orchestration", "version": "0.9.1", From 02b982f3a1f434c8ab1e35e125fae69b7713ef46 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 6 Oct 2025 09:57:54 +0530 Subject: [PATCH 238/274] update: `code` fallback! --- app/realtime.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/realtime.php b/app/realtime.php index e0a776b85b..fccf5c9a20 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -713,6 +713,10 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re } } catch (Throwable $th) { $code = $th->getCode(); + if (!is_int($code)) { + $code = 500; + } + $message = $th->getMessage(); // sanitize 5xx errors From b7f7fb00afb9592d1c79b5941b371d006106ded1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 6 Oct 2025 17:32:35 +1300 Subject: [PATCH 239/274] Update database --- composer.lock | 230 ++------------------------------------------------ 1 file changed, 5 insertions(+), 225 deletions(-) diff --git a/composer.lock b/composer.lock index f6cbc491f6..bad4ed32af 100644 --- a/composer.lock +++ b/composer.lock @@ -959,83 +959,6 @@ }, "time": "2025-08-20T17:20:16+00:00" }, - { - "name": "mongodb/mongodb", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/mongodb/mongo-php-library.git", - "reference": "f399d24905dd42f97dfe0af9706129743ef247ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/f399d24905dd42f97dfe0af9706129743ef247ac", - "reference": "f399d24905dd42f97dfe0af9706129743ef247ac", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.0", - "ext-mongodb": "^2.1", - "php": "^8.1", - "psr/log": "^1.1.4|^2|^3", - "symfony/polyfill-php85": "^1.32" - }, - "replace": { - "mongodb/builder": "*" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0", - "phpunit/phpunit": "^10.5.35", - "rector/rector": "^1.2", - "squizlabs/php_codesniffer": "^3.7", - "vimeo/psalm": "6.5.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "MongoDB\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Andreas Braun", - "email": "andreas.braun@mongodb.com" - }, - { - "name": "Jeremy Mikola", - "email": "jmikola@gmail.com" - }, - { - "name": "Jérôme Tamarelle", - "email": "jerome.tamarelle@mongodb.com" - } - ], - "description": "MongoDB driver library", - "homepage": "https://jira.mongodb.org/browse/PHPLIB", - "keywords": [ - "database", - "driver", - "mongodb", - "persistence" - ], - "support": { - "issues": "https://github.com/mongodb/mongo-php-library/issues", - "source": "https://github.com/mongodb/mongo-php-library/tree/2.1.1" - }, - "time": "2025-08-13T20:50:05+00:00" - }, { "name": "mustangostang/spyc", "version": "0.6.3", @@ -3094,86 +3017,6 @@ ], "time": "2025-07-08T02:45:35+00:00" }, - { - "name": "symfony/polyfill-php85", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-23T16:12:55+00:00" - }, { "name": "symfony/service-contracts", "version": "v3.6.0", @@ -3796,27 +3639,25 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "33ca4266c7f79a037604fc889db7d79ed37d73fd" + "reference": "a91e04080d7f13c35c4885dea0ffebc33cd33e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/33ca4266c7f79a037604fc889db7d79ed37d73fd", - "reference": "33ca4266c7f79a037604fc889db7d79ed37d73fd", + "url": "https://api.github.com/repos/utopia-php/database/zipball/a91e04080d7f13c35c4885dea0ffebc33cd33e1f", + "reference": "a91e04080d7f13c35c4885dea0ffebc33cd33e1f", "shasum": "" }, "require": { "ext-mbstring": "*", - "ext-mongodb": "*", "ext-pdo": "*", "php": ">=8.1", "utopia-php/cache": "0.13.*", "utopia-php/framework": "0.33.*", - "utopia-php/mongo": "0.10.*", "utopia-php/pools": "0.8.*" }, "require-dev": { "fakerphp/faker": "1.23.*", - "laravel/pint": "*", + "laravel/pint": "1.*", "pcov/clobber": "2.*", "phpstan/phpstan": "1.*", "phpunit/phpunit": "9.*", @@ -3846,7 +3687,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/2.3.1" }, - "time": "2025-10-06T04:04:42+00:00" + "time": "2025-10-06T04:29:14+00:00" }, { "name": "utopia-php/detector", @@ -4400,67 +4241,6 @@ }, "time": "2025-09-10T05:45:30+00:00" }, - { - "name": "utopia-php/mongo", - "version": "0.10.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/mongo.git", - "reference": "ecfad6aad2e2e3fe5899ac2ebf1009a21b4d6b18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/mongo/zipball/ecfad6aad2e2e3fe5899ac2ebf1009a21b4d6b18", - "reference": "ecfad6aad2e2e3fe5899ac2ebf1009a21b4d6b18", - "shasum": "" - }, - "require": { - "ext-mongodb": "2.1.*", - "mongodb/mongodb": "2.1.*", - "php": ">=8.0", - "ramsey/uuid": "4.9.*" - }, - "require-dev": { - "fakerphp/faker": "1.*", - "laravel/pint": "*", - "phpstan/phpstan": "*", - "phpunit/phpunit": "9.*", - "swoole/ide-helper": "5.1.*" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Mongo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Wess", - "email": "wess@appwrite.io" - } - ], - "description": "A simple library to manage Mongo database", - "keywords": [ - "database", - "mongo", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/mongo/issues", - "source": "https://github.com/utopia-php/mongo/tree/0.10.0" - }, - "time": "2025-10-02T04:50:07+00:00" - }, { "name": "utopia-php/orchestration", "version": "0.9.1", From 4348a176d500638b26f21ab17c26d8ba20de2b64 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 6 Oct 2025 16:44:31 +0530 Subject: [PATCH 240/274] Rename verification SDK methods to be more specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit renames the verification SDK methods to better reflect their purpose: - `createVerification` → `createEmailVerification` - `updateVerification` → `updateEmailVerification` The old method names are maintained for backwards compatibility and marked as deprecated (since 1.8.0) with references to the new method names. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/config/specs/open-api3-1.8.x-client.json | 116 ++++++++++++++++- app/config/specs/open-api3-1.8.x-console.json | 116 ++++++++++++++++- app/config/specs/open-api3-1.8.x-server.json | 120 +++++++++++++++++- app/config/specs/open-api3-latest-client.json | 116 ++++++++++++++++- .../specs/open-api3-latest-console.json | 116 ++++++++++++++++- app/config/specs/open-api3-latest-server.json | 120 +++++++++++++++++- app/config/specs/swagger2-1.8.x-client.json | 116 ++++++++++++++++- app/config/specs/swagger2-1.8.x-console.json | 116 ++++++++++++++++- app/config/specs/swagger2-1.8.x-server.json | 120 +++++++++++++++++- app/config/specs/swagger2-latest-client.json | 116 ++++++++++++++++- app/config/specs/swagger2-latest-console.json | 116 ++++++++++++++++- app/config/specs/swagger2-latest-server.json | 120 +++++++++++++++++- app/controllers/api/account.php | 96 ++++++++++---- 13 files changed, 1404 insertions(+), 100 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index d57b9f83b2..1ebb051018 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -3467,7 +3467,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "tags": [ "account" ], @@ -3486,12 +3486,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3502,6 +3502,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3535,7 +3585,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "tags": [ "account" ], @@ -3554,12 +3604,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3570,6 +3620,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index bdff664cbc..2ecd94f1f9 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -3476,7 +3476,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "tags": [ "account" ], @@ -3495,12 +3495,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3511,6 +3511,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3543,7 +3593,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "tags": [ "account" ], @@ -3562,12 +3612,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3578,6 +3628,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 6b766dbdee..7a2fe08274 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -3164,7 +3164,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "tags": [ "account" ], @@ -3183,12 +3183,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3199,6 +3199,58 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] @@ -3233,7 +3285,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "tags": [ "account" ], @@ -3252,12 +3304,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3268,6 +3320,62 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index d57b9f83b2..1ebb051018 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3467,7 +3467,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "tags": [ "account" ], @@ -3486,12 +3486,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3502,6 +3502,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3535,7 +3585,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "tags": [ "account" ], @@ -3554,12 +3604,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3570,6 +3620,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index bdff664cbc..2ecd94f1f9 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3476,7 +3476,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "tags": [ "account" ], @@ -3495,12 +3495,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3511,6 +3511,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3543,7 +3593,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "tags": [ "account" ], @@ -3562,12 +3612,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3578,6 +3628,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 6b766dbdee..7a2fe08274 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3164,7 +3164,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "tags": [ "account" ], @@ -3183,12 +3183,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3199,6 +3199,58 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] @@ -3233,7 +3285,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "tags": [ "account" ], @@ -3252,12 +3304,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3268,6 +3320,62 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/components\/schemas\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index c2628533d0..60374274c1 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -3602,7 +3602,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "consumes": [ "application\/json" ], @@ -3623,12 +3623,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3639,6 +3639,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3673,7 +3723,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "consumes": [ "application\/json" ], @@ -3694,12 +3744,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3710,6 +3760,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index ee3702d27d..4254bf7924 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -3621,7 +3621,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "consumes": [ "application\/json" ], @@ -3642,12 +3642,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3658,6 +3658,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3691,7 +3741,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "consumes": [ "application\/json" ], @@ -3712,12 +3762,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3728,6 +3778,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index ff5056b35a..7b1604ebb2 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -3305,7 +3305,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "consumes": [ "application\/json" ], @@ -3326,12 +3326,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3342,6 +3342,58 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] @@ -3377,7 +3429,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "consumes": [ "application\/json" ], @@ -3398,12 +3450,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3414,6 +3466,62 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index c2628533d0..60374274c1 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3602,7 +3602,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "consumes": [ "application\/json" ], @@ -3623,12 +3623,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3639,6 +3639,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3673,7 +3723,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "consumes": [ "application\/json" ], @@ -3694,12 +3744,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3710,6 +3760,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index ee3702d27d..4254bf7924 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3621,7 +3621,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "consumes": [ "application\/json" ], @@ -3642,12 +3642,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3658,6 +3658,56 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [] } @@ -3691,7 +3741,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "consumes": [ "application\/json" ], @@ -3712,12 +3762,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3728,6 +3778,60 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [] } diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index ff5056b35a..7b1604ebb2 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3305,7 +3305,7 @@ "\/account\/verification": { "post": { "summary": "Create email verification", - "operationId": "accountCreateVerification", + "operationId": "accountCreateEmailVerification", "consumes": [ "application\/json" ], @@ -3326,12 +3326,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "createVerification", + "method": "createEmailVerification", "group": "verification", "weight": 41, "cookies": false, "type": "", - "demo": "account\/create-verification.md", + "demo": "account\/create-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3342,6 +3342,58 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "createEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-email-verification.md" + }, + { + "name": "createVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "url" + ], + "required": [ + "url" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to your app and allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n", + "demo": "account\/create-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.createEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] @@ -3377,7 +3429,7 @@ }, "put": { "summary": "Update email verification (confirmation)", - "operationId": "accountUpdateVerification", + "operationId": "accountUpdateEmailVerification", "consumes": [ "application\/json" ], @@ -3398,12 +3450,12 @@ }, "deprecated": false, "x-appwrite": { - "method": "updateVerification", + "method": "updateEmailVerification", "group": "verification", "weight": 42, "cookies": false, "type": "", - "demo": "account\/update-verification.md", + "demo": "account\/update-email-verification.md", "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/update-email-verification.md", "rate-limit": 10, "rate-time": 3600, @@ -3414,6 +3466,62 @@ "server" ], "packaging": false, + "methods": [ + { + "name": "updateEmailVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-email-verification.md" + }, + { + "name": "updateVerification", + "namespace": "account", + "desc": "", + "auth": { + "Project": [], + "Session": [] + }, + "parameters": [ + "userId", + "secret" + ], + "required": [ + "userId", + "secret" + ], + "responses": [ + { + "code": 200, + "model": "#\/definitions\/token" + } + ], + "description": "Use this endpoint to complete the user email verification process. Use both the **userId** and **secret** parameters that were attached to your app URL to verify the user email ownership. If confirmed this route will return a 200 status code.", + "demo": "account\/update-verification.md", + "deprecated": { + "since": "1.8.0", + "replaceWith": "account.updateEmailVerification" + } + } + ], "auth": { "Project": [], "Session": [] diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 8aaa5283c4..57d9e31876 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -3513,20 +3513,40 @@ App::post('/v1/account/verification') ->label('event', 'users.[userId].verification.[tokenId].create') ->label('audits.event', 'verification.create') ->label('audits.resource', 'user/{response.userId}') - ->label('sdk', new Method( - namespace: 'account', - group: 'verification', - name: 'createVerification', - description: '/docs/references/account/create-email-verification.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_TOKEN, - ) - ], - contentType: ContentType::JSON, - )) + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'verification', + name: 'createEmailVerification', + description: '/docs/references/account/create-email-verification.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_TOKEN, + ) + ], + contentType: ContentType::JSON, + ), + new Method( + namespace: 'account', + group: 'verification', + name: 'createVerification', + description: '/docs/references/account/create-email-verification.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_TOKEN, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.createEmailVerification' + ), + ) + ]) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},userId:{userId}') ->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey']) // TODO add built-in confirm page @@ -3679,20 +3699,40 @@ App::put('/v1/account/verification') ->label('event', 'users.[userId].verification.[tokenId].update') ->label('audits.event', 'verification.update') ->label('audits.resource', 'user/{response.userId}') - ->label('sdk', new Method( - namespace: 'account', - group: 'verification', - name: 'updateVerification', - description: '/docs/references/account/update-email-verification.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_TOKEN, - ) - ], - contentType: ContentType::JSON - )) + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'verification', + name: 'updateEmailVerification', + description: '/docs/references/account/update-email-verification.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_TOKEN, + ) + ], + contentType: ContentType::JSON + ), + new Method( + namespace: 'account', + group: 'verification', + name: 'updateVerification', + description: '/docs/references/account/update-email-verification.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_TOKEN, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.updateEmailVerification' + ), + ) + ]) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},userId:{param-userId}') ->param('userId', '', new UID(), 'User ID.') From c618144d783689fb2dcb8ca868e5924aed258432 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 6 Oct 2025 16:46:56 +0530 Subject: [PATCH 241/274] update examples --- .../java/account/create-email-verification.md | 22 +++++++++++++++++ .../java/account/update-email-verification.md | 23 ++++++++++++++++++ .../account/create-email-verification.md | 13 ++++++++++ .../account/update-email-verification.md | 14 +++++++++++ .../account/create-email-verification.md | 12 ++++++++++ .../account/update-email-verification.md | 13 ++++++++++ .../account/create-email-verification.md | 11 +++++++++ .../account/update-email-verification.md | 12 ++++++++++ .../account/create-email-verification.md | 12 ++++++++++ .../account/update-email-verification.md | 13 ++++++++++ .../account/create-email-verification.md | 13 ++++++++++ .../account/update-email-verification.md | 14 +++++++++++ .../account/create-email-verification.md | 11 +++++++++ .../account/update-email-verification.md | 12 ++++++++++ .../account/create-email-verification.md | 13 ++++++++++ .../account/update-email-verification.md | 14 +++++++++++ .../account/create-email-verification.md | 2 ++ .../account/update-email-verification.md | 3 +++ .../account/create-email-verification.md | 13 ++++++++++ .../account/update-email-verification.md | 14 +++++++++++ .../account/create-email-verification.md | 12 ++++++++++ .../account/update-email-verification.md | 13 ++++++++++ .../account/create-email-verification.md | 14 +++++++++++ .../account/update-email-verification.md | 15 ++++++++++++ .../account/create-email-verification.md | 19 +++++++++++++++ .../account/update-email-verification.md | 20 ++++++++++++++++ .../account/create-email-verification.md | 12 ++++++++++ .../account/update-email-verification.md | 13 ++++++++++ .../java/account/create-email-verification.md | 23 ++++++++++++++++++ .../java/account/update-email-verification.md | 24 +++++++++++++++++++ .../account/create-email-verification.md | 14 +++++++++++ .../account/update-email-verification.md | 15 ++++++++++++ .../account/create-email-verification.md | 12 ++++++++++ .../account/update-email-verification.md | 13 ++++++++++ .../account/create-email-verification.md | 15 ++++++++++++ .../account/update-email-verification.md | 16 +++++++++++++ .../account/create-email-verification.md | 13 ++++++++++ .../account/update-email-verification.md | 14 +++++++++++ .../account/create-email-verification.md | 11 +++++++++ .../account/update-email-verification.md | 12 ++++++++++ .../account/create-email-verification.md | 14 +++++++++++ .../account/update-email-verification.md | 15 ++++++++++++ .../account/create-email-verification.md | 13 ++++++++++ .../account/update-email-verification.md | 14 +++++++++++ 44 files changed, 610 insertions(+) create mode 100644 docs/examples/1.8.x/client-android/java/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-android/java/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-apple/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-apple/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/client-web/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/client-web/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/console-cli/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/console-cli/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/console-web/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/console-web/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-dart/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-dart/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-go/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-go/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-nodejs/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-php/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-php/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-python/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-python/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/account/update-email-verification.md create mode 100644 docs/examples/1.8.x/server-swift/examples/account/create-email-verification.md create mode 100644 docs/examples/1.8.x/server-swift/examples/account/update-email-verification.md diff --git a/docs/examples/1.8.x/client-android/java/account/create-email-verification.md b/docs/examples/1.8.x/client-android/java/account/create-email-verification.md new file mode 100644 index 0000000000..dfbf059d45 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/account/create-email-verification.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Account; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Account account = new Account(client); + +account.createEmailVerification( + "https://example.com", // url + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/account/update-email-verification.md b/docs/examples/1.8.x/client-android/java/account/update-email-verification.md new file mode 100644 index 0000000000..9d157c8e92 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/account/update-email-verification.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Account; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Account account = new Account(client); + +account.updateEmailVerification( + "", // userId + "", // secret + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/kotlin/account/create-email-verification.md b/docs/examples/1.8.x/client-android/kotlin/account/create-email-verification.md new file mode 100644 index 0000000000..dc87901eaf --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/account/create-email-verification.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Account + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val account = Account(client) + +val result = account.createEmailVerification( + url = "https://example.com", +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/account/update-email-verification.md b/docs/examples/1.8.x/client-android/kotlin/account/update-email-verification.md new file mode 100644 index 0000000000..9fb21d2d7c --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/account/update-email-verification.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Account + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val account = Account(client) + +val result = account.updateEmailVerification( + userId = "", + secret = "", +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-apple/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-apple/examples/account/create-email-verification.md new file mode 100644 index 0000000000..378558ecd6 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/account/create-email-verification.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let account = Account(client) + +let token = try await account.createEmailVerification( + url: "https://example.com" +) + diff --git a/docs/examples/1.8.x/client-apple/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-apple/examples/account/update-email-verification.md new file mode 100644 index 0000000000..77ef28eb49 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/account/update-email-verification.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let account = Account(client) + +let token = try await account.updateEmailVerification( + userId: "", + secret: "" +) + diff --git a/docs/examples/1.8.x/client-flutter/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-flutter/examples/account/create-email-verification.md new file mode 100644 index 0000000000..823ea2f216 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/account/create-email-verification.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Account account = Account(client); + +Token result = await account.createEmailVerification( + url: 'https://example.com', +); diff --git a/docs/examples/1.8.x/client-flutter/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-flutter/examples/account/update-email-verification.md new file mode 100644 index 0000000000..927aadf184 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/account/update-email-verification.md @@ -0,0 +1,12 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Account account = Account(client); + +Token result = await account.updateEmailVerification( + userId: '', + secret: '', +); diff --git a/docs/examples/1.8.x/client-graphql/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-graphql/examples/account/create-email-verification.md new file mode 100644 index 0000000000..1781188527 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/account/create-email-verification.md @@ -0,0 +1,12 @@ +mutation { + accountCreateEmailVerification( + url: "https://example.com" + ) { + _id + _createdAt + userId + secret + expire + phrase + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-graphql/examples/account/update-email-verification.md new file mode 100644 index 0000000000..6386d34bfa --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/account/update-email-verification.md @@ -0,0 +1,13 @@ +mutation { + accountUpdateEmailVerification( + userId: "", + secret: "" + ) { + _id + _createdAt + userId + secret + expire + phrase + } +} diff --git a/docs/examples/1.8.x/client-react-native/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-react-native/examples/account/create-email-verification.md new file mode 100644 index 0000000000..42260501c2 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/account/create-email-verification.md @@ -0,0 +1,13 @@ +import { Client, Account } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.createEmailVerification({ + url: 'https://example.com' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-react-native/examples/account/update-email-verification.md new file mode 100644 index 0000000000..4270380d5f --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/account/update-email-verification.md @@ -0,0 +1,14 @@ +import { Client, Account } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.updateEmailVerification({ + userId: '', + secret: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md new file mode 100644 index 0000000000..ed5479dbe5 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md @@ -0,0 +1,11 @@ +POST /v1/account/verification HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "url": "https://example.com" +} diff --git a/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md new file mode 100644 index 0000000000..a4dcbf76a3 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md @@ -0,0 +1,12 @@ +PUT /v1/account/verification HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "userId": "", + "secret": "" +} diff --git a/docs/examples/1.8.x/client-web/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-web/examples/account/create-email-verification.md new file mode 100644 index 0000000000..8f93533c35 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/account/create-email-verification.md @@ -0,0 +1,13 @@ +import { Client, Account } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.createEmailVerification({ + url: 'https://example.com' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-web/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-web/examples/account/update-email-verification.md new file mode 100644 index 0000000000..4f1e03f3c6 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/account/update-email-verification.md @@ -0,0 +1,14 @@ +import { Client, Account } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.updateEmailVerification({ + userId: '', + secret: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-cli/examples/account/create-email-verification.md b/docs/examples/1.8.x/console-cli/examples/account/create-email-verification.md new file mode 100644 index 0000000000..f9f37f2f8f --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/account/create-email-verification.md @@ -0,0 +1,2 @@ +appwrite account create-email-verification \ + --url https://example.com diff --git a/docs/examples/1.8.x/console-cli/examples/account/update-email-verification.md b/docs/examples/1.8.x/console-cli/examples/account/update-email-verification.md new file mode 100644 index 0000000000..02ff32aa57 --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/account/update-email-verification.md @@ -0,0 +1,3 @@ +appwrite account update-email-verification \ + --user-id \ + --secret diff --git a/docs/examples/1.8.x/console-web/examples/account/create-email-verification.md b/docs/examples/1.8.x/console-web/examples/account/create-email-verification.md new file mode 100644 index 0000000000..b0e52db469 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/account/create-email-verification.md @@ -0,0 +1,13 @@ +import { Client, Account } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.createEmailVerification({ + url: 'https://example.com' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/account/update-email-verification.md b/docs/examples/1.8.x/console-web/examples/account/update-email-verification.md new file mode 100644 index 0000000000..e0e09fd4ce --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/account/update-email-verification.md @@ -0,0 +1,14 @@ +import { Client, Account } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.updateEmailVerification({ + userId: '', + secret: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/server-dart/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-dart/examples/account/create-email-verification.md new file mode 100644 index 0000000000..b10173d190 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/account/create-email-verification.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +Account account = Account(client); + +Token result = await account.createEmailVerification( + url: 'https://example.com', +); diff --git a/docs/examples/1.8.x/server-dart/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-dart/examples/account/update-email-verification.md new file mode 100644 index 0000000000..b48535a31a --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/account/update-email-verification.md @@ -0,0 +1,13 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +Account account = Account(client); + +Token result = await account.updateEmailVerification( + userId: '', + secret: '', +); diff --git a/docs/examples/1.8.x/server-dotnet/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-dotnet/examples/account/create-email-verification.md new file mode 100644 index 0000000000..6efee895e0 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/account/create-email-verification.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetSession(""); // The user session to authenticate with + +Account account = new Account(client); + +Token result = await account.CreateEmailVerification( + url: "https://example.com" +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-dotnet/examples/account/update-email-verification.md new file mode 100644 index 0000000000..a336682be3 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/account/update-email-verification.md @@ -0,0 +1,15 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetSession(""); // The user session to authenticate with + +Account account = new Account(client); + +Token result = await account.UpdateEmailVerification( + userId: "", + secret: "" +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-go/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-go/examples/account/create-email-verification.md new file mode 100644 index 0000000000..d10b88e21d --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/account/create-email-verification.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/account" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithSession("") +) + +service := account.New(client) + +response, error := service.CreateEmailVerification( + "https://example.com", +) diff --git a/docs/examples/1.8.x/server-go/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-go/examples/account/update-email-verification.md new file mode 100644 index 0000000000..780405d514 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/account/update-email-verification.md @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/account" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithSession("") +) + +service := account.New(client) + +response, error := service.UpdateEmailVerification( + "", + "", +) diff --git a/docs/examples/1.8.x/server-graphql/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-graphql/examples/account/create-email-verification.md new file mode 100644 index 0000000000..1781188527 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/account/create-email-verification.md @@ -0,0 +1,12 @@ +mutation { + accountCreateEmailVerification( + url: "https://example.com" + ) { + _id + _createdAt + userId + secret + expire + phrase + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-graphql/examples/account/update-email-verification.md new file mode 100644 index 0000000000..6386d34bfa --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/account/update-email-verification.md @@ -0,0 +1,13 @@ +mutation { + accountUpdateEmailVerification( + userId: "", + secret: "" + ) { + _id + _createdAt + userId + secret + expire + phrase + } +} diff --git a/docs/examples/1.8.x/server-kotlin/java/account/create-email-verification.md b/docs/examples/1.8.x/server-kotlin/java/account/create-email-verification.md new file mode 100644 index 0000000000..de80353b29 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/account/create-email-verification.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Account; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setSession(""); // The user session to authenticate with + +Account account = new Account(client); + +account.createEmailVerification( + "https://example.com", // url + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/account/update-email-verification.md b/docs/examples/1.8.x/server-kotlin/java/account/update-email-verification.md new file mode 100644 index 0000000000..92dfd8d00c --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/account/update-email-verification.md @@ -0,0 +1,24 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Account; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setSession(""); // The user session to authenticate with + +Account account = new Account(client); + +account.updateEmailVerification( + "", // userId + "", // secret + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/account/create-email-verification.md b/docs/examples/1.8.x/server-kotlin/kotlin/account/create-email-verification.md new file mode 100644 index 0000000000..4ef178fb9f --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/account/create-email-verification.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Account + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setSession("") // The user session to authenticate with + +val account = Account(client) + +val response = account.createEmailVerification( + url = "https://example.com" +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/account/update-email-verification.md b/docs/examples/1.8.x/server-kotlin/kotlin/account/update-email-verification.md new file mode 100644 index 0000000000..6eb97bccc2 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/account/update-email-verification.md @@ -0,0 +1,15 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Account + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setSession("") // The user session to authenticate with + +val account = Account(client) + +val response = account.updateEmailVerification( + userId = "", + secret = "" +) diff --git a/docs/examples/1.8.x/server-nodejs/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-nodejs/examples/account/create-email-verification.md new file mode 100644 index 0000000000..e2aaf8015a --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/account/create-email-verification.md @@ -0,0 +1,12 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.createEmailVerification({ + url: 'https://example.com' +}); diff --git a/docs/examples/1.8.x/server-nodejs/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-nodejs/examples/account/update-email-verification.md new file mode 100644 index 0000000000..eb6507e332 --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/account/update-email-verification.md @@ -0,0 +1,13 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.updateEmailVerification({ + userId: '', + secret: '' +}); diff --git a/docs/examples/1.8.x/server-php/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-php/examples/account/create-email-verification.md new file mode 100644 index 0000000000..691d6fa300 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/account/create-email-verification.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setSession(''); // The user session to authenticate with + +$account = new Account($client); + +$result = $account->createEmailVerification( + url: 'https://example.com' +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-php/examples/account/update-email-verification.md new file mode 100644 index 0000000000..95cd1b5e42 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/account/update-email-verification.md @@ -0,0 +1,16 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setSession(''); // The user session to authenticate with + +$account = new Account($client); + +$result = $account->updateEmailVerification( + userId: '', + secret: '' +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-python/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-python/examples/account/create-email-verification.md new file mode 100644 index 0000000000..a76a4bdb89 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/account/create-email-verification.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.account import Account + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +account = Account(client) + +result = account.create_email_verification( + url = 'https://example.com' +) diff --git a/docs/examples/1.8.x/server-python/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-python/examples/account/update-email-verification.md new file mode 100644 index 0000000000..5e99587e86 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/account/update-email-verification.md @@ -0,0 +1,14 @@ +from appwrite.client import Client +from appwrite.services.account import Account + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +account = Account(client) + +result = account.update_email_verification( + user_id = '', + secret = '' +) diff --git a/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md new file mode 100644 index 0000000000..ed5479dbe5 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md @@ -0,0 +1,11 @@ +POST /v1/account/verification HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "url": "https://example.com" +} diff --git a/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md new file mode 100644 index 0000000000..a4dcbf76a3 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md @@ -0,0 +1,12 @@ +PUT /v1/account/verification HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "userId": "", + "secret": "" +} diff --git a/docs/examples/1.8.x/server-ruby/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-ruby/examples/account/create-email-verification.md new file mode 100644 index 0000000000..11bf56b73f --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/account/create-email-verification.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_session('') # The user session to authenticate with + +account = Account.new(client) + +result = account.create_email_verification( + url: 'https://example.com' +) diff --git a/docs/examples/1.8.x/server-ruby/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-ruby/examples/account/update-email-verification.md new file mode 100644 index 0000000000..33b009a549 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/account/update-email-verification.md @@ -0,0 +1,15 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_session('') # The user session to authenticate with + +account = Account.new(client) + +result = account.update_email_verification( + user_id: '', + secret: '' +) diff --git a/docs/examples/1.8.x/server-swift/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-swift/examples/account/create-email-verification.md new file mode 100644 index 0000000000..788fd9585a --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/account/create-email-verification.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setSession("") // The user session to authenticate with + +let account = Account(client) + +let token = try await account.createEmailVerification( + url: "https://example.com" +) + diff --git a/docs/examples/1.8.x/server-swift/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-swift/examples/account/update-email-verification.md new file mode 100644 index 0000000000..10c8afe901 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/account/update-email-verification.md @@ -0,0 +1,14 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setSession("") // The user session to authenticate with + +let account = Account(client) + +let token = try await account.updateEmailVerification( + userId: "", + secret: "" +) + From 56076ad6497de999fee89ed0e6e89b61b51fb877 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 6 Oct 2025 19:13:06 +0530 Subject: [PATCH 242/274] update path and add alias --- app/config/specs/open-api3-1.8.x-client.json | 2 +- app/config/specs/open-api3-1.8.x-console.json | 2 +- app/config/specs/open-api3-1.8.x-server.json | 2 +- app/config/specs/open-api3-latest-client.json | 2 +- app/config/specs/open-api3-latest-console.json | 2 +- app/config/specs/open-api3-latest-server.json | 2 +- app/config/specs/swagger2-1.8.x-client.json | 2 +- app/config/specs/swagger2-1.8.x-console.json | 2 +- app/config/specs/swagger2-1.8.x-server.json | 2 +- app/config/specs/swagger2-latest-client.json | 2 +- app/config/specs/swagger2-latest-console.json | 2 +- app/config/specs/swagger2-latest-server.json | 2 +- app/controllers/api/account.php | 6 ++++-- 13 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index 1ebb051018..0ab37fa677 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -3464,7 +3464,7 @@ } } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 2ecd94f1f9..79ec41cc63 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -3473,7 +3473,7 @@ } } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 7a2fe08274..056010c96f 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -3161,7 +3161,7 @@ } } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 1ebb051018..0ab37fa677 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3464,7 +3464,7 @@ } } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 2ecd94f1f9..79ec41cc63 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3473,7 +3473,7 @@ } } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 7a2fe08274..056010c96f 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3161,7 +3161,7 @@ } } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 60374274c1..bedc855c36 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -3599,7 +3599,7 @@ ] } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 4254bf7924..0f04574558 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -3618,7 +3618,7 @@ ] } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 7b1604ebb2..d137016f47 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -3302,7 +3302,7 @@ ] } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 60374274c1..bedc855c36 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3599,7 +3599,7 @@ ] } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 4254bf7924..0f04574558 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3618,7 +3618,7 @@ ] } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 7b1604ebb2..d137016f47 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3302,7 +3302,7 @@ ] } }, - "\/account\/verification": { + "\/account\/verification\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 57d9e31876..c22c0babee 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -3506,7 +3506,8 @@ App::put('/v1/account/recovery') $response->dynamic($recoveryDocument, Response::MODEL_TOKEN); }); -App::post('/v1/account/verification') +App::post('/v1/account/verification/email') + ->alias('/v1/account/verification') ->desc('Create email verification') ->groups(['api', 'account']) ->label('scope', 'account') @@ -3692,7 +3693,8 @@ App::post('/v1/account/verification') ->dynamic($verification, Response::MODEL_TOKEN); }); -App::put('/v1/account/verification') +App::put('/v1/account/verification/email') + ->alias('/v1/account/verification') ->desc('Update email verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') From 29dbe99840f0076562ba7fb969d368bbf3329cd4 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 6 Oct 2025 19:58:01 +0530 Subject: [PATCH 243/274] simplify config & templates --- app/config/console.php | 41 +--- .../locale/templates/email-auth-styled.tpl | 225 ------------------ .../locale/templates/email-base-styled.tpl | 5 +- app/config/locale/templates/email-base.tpl | 15 ++ .../locale/templates/email-inner-base.tpl | 2 +- .../locale/templates/email-magic-url.tpl | 2 +- .../locale/templates/email-verification.tpl | 9 - app/config/locale/translations/en.json | 3 + app/controllers/api/account.php | 78 ++---- src/Appwrite/Platform/Workers/Mails.php | 1 + tests/e2e/Services/Account/AccountBase.php | 5 - 11 files changed, 40 insertions(+), 346 deletions(-) delete mode 100644 app/config/locale/templates/email-auth-styled.tpl delete mode 100644 app/config/locale/templates/email-verification.tpl diff --git a/app/config/console.php b/app/config/console.php index 6f44368060..70bb3523e6 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -9,8 +9,6 @@ use Appwrite\Network\Platform; use Utopia\Database\Helpers\ID; use Utopia\System\System; -$localeCodes = include __DIR__ . '/locale/codes.php'; - $console = [ '$id' => ID::custom('console'), '$sequence' => ID::custom('console'), @@ -51,44 +49,7 @@ $console = [ 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') ], - 'templates' => [ - 'email.verification-en' => [ - 'subject' => 'Account Verification', - 'preview' => 'Verify your email to activate your {{project}} account.', - 'heading' => 'Verify your email to start using Appwrite Cloud', - 'hello' => 'Hello {{user}},', - 'body' => 'Thanks for signing up for Appwrite Cloud. Before you can get started, please verify your email address.', - 'footer' => 'If you didn’t create an account, you can ignore this email.', - 'buttonText' => 'Verify email', - 'thanks' => 'Thanks,', - "signature" => "{{project}} team", - ], - 'email.mfaChallenge-en' => [ - 'subject' => 'Verification Code for {{project}}', - 'preview' => 'Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.', - 'heading' => 'Complete two-step verification to use Appwrite Cloud', - 'hello' => 'Hello {{user}},', - 'body' => 'Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.', - 'thanks' => 'Thanks,', - "signature" => "{{project}} team", - ], - 'email.otpSession-en' => [ - 'subject' => 'OTP for {{project}} Login', - 'preview' => 'Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.', - 'heading' => 'Login with OTP to use Appwrite Cloud', - 'hello' => 'Hello {{user}},', - 'body' => 'Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.', - 'thanks' => 'Thanks,', - "signature" => "{{project}} team", - ], - ], - 'customEmails' => true, + 'smtpBaseTemplate' => 'email-base-styled', ]; -foreach ($localeCodes as $localeCode) { - $console['templates']['email.verification-' . $localeCode['code']] = $console['templates']['email.verification-en']; - $console['templates']['email.mfaChallenge-' . $localeCode['code']] = $console['templates']['email.mfaChallenge-en']; - $console['templates']['email.otpSession-' . $localeCode['code']] = $console['templates']['email.otpSession-en']; -} - return $console; diff --git a/app/config/locale/templates/email-auth-styled.tpl b/app/config/locale/templates/email-auth-styled.tpl deleted file mode 100644 index a826c62e95..0000000000 --- a/app/config/locale/templates/email-auth-styled.tpl +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - - - - - -
- {{preview}} -
{{previewWhitespace}}
-
- -
- - - - -
- Appwrite logo -
- - - - - -
-

{{heading}}

-
- - - - - -
-{{body}} -
- - - - - -
- - - - - - - -
- - - - - -
- - - - - - -
Terms -
|
-
Privacy
-

- © {{year}} Appwrite | 251 Little Falls Drive, Wilmington 19808, - Delaware, United States -

-
- - \ No newline at end of file diff --git a/app/config/locale/templates/email-base-styled.tpl b/app/config/locale/templates/email-base-styled.tpl index 16036e792c..3f3ba8dd9c 100644 --- a/app/config/locale/templates/email-base-styled.tpl +++ b/app/config/locale/templates/email-base-styled.tpl @@ -147,6 +147,7 @@ Appwrite logo @@ -155,12 +156,12 @@
-

{{subject}}

+

{{heading}}

- +
{{body}} diff --git a/app/config/locale/templates/email-base.tpl b/app/config/locale/templates/email-base.tpl index 8c94c3f63e..5153b3e4fc 100644 --- a/app/config/locale/templates/email-base.tpl +++ b/app/config/locale/templates/email-base.tpl @@ -44,6 +44,21 @@ color: currentColor; word-break: break-all; } + a.button { + box-sizing: border-box; + display: inline-block; + text-align: center; + text-decoration: none; + padding: 9px 14px; + color: #ffffff; + background-color: #2D2D31; + border: 1px solid #414146; + border-radius: 8px; + } + a.button:hover, + a.button:focus { + opacity: 0.8; + } table { width: 100%; border-spacing: 0 !important; diff --git a/app/config/locale/templates/email-inner-base.tpl b/app/config/locale/templates/email-inner-base.tpl index 677f70ce7d..4b68f224db 100644 --- a/app/config/locale/templates/email-inner-base.tpl +++ b/app/config/locale/templates/email-inner-base.tpl @@ -1,6 +1,6 @@

{{hello}}

{{body}}

-

{{buttonText}}

+

{{buttonText}}

{{footer}}

{{thanks}} diff --git a/app/config/locale/templates/email-magic-url.tpl b/app/config/locale/templates/email-magic-url.tpl index 21988c5bc1..618993e0e9 100644 --- a/app/config/locale/templates/email-magic-url.tpl +++ b/app/config/locale/templates/email-magic-url.tpl @@ -5,7 +5,7 @@
- {{buttonText}} + {{buttonText}}
diff --git a/app/config/locale/templates/email-verification.tpl b/app/config/locale/templates/email-verification.tpl deleted file mode 100644 index 4b68f224db..0000000000 --- a/app/config/locale/templates/email-verification.tpl +++ /dev/null @@ -1,9 +0,0 @@ -

{{hello}}

-

{{body}}

-

{{buttonText}}

-

{{footer}}

-

- {{thanks}} -
- {{signature}} -

\ No newline at end of file diff --git a/app/config/locale/translations/en.json b/app/config/locale/translations/en.json index e2ee20b2d7..69328e61a7 100644 --- a/app/config/locale/translations/en.json +++ b/app/config/locale/translations/en.json @@ -5,6 +5,7 @@ "emails.sender": "{{project}} Team", "emails.verification.subject": "Account Verification", "emails.verification.preview": "Verify your email to activate your {{project}} account.", + "emails.verification.heading": "Verify your email to start using Appwrite Cloud", "emails.verification.hello": "Hello {{user}},", "emails.verification.body": "Follow this link to verify your email address to your {{b}}{{project}}{{/b}} account.", "emails.verification.footer": "If you didn’t ask to verify this address, you can ignore this message.", @@ -33,6 +34,7 @@ "emails.sessionAlert.signature": "{{project}} team", "emails.otpSession.subject": "OTP for {{project}} Login", "emails.otpSession.preview": "Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.", + "emails.otpSession.heading": "Login with OTP to use Appwrite Cloud", "emails.otpSession.hello": "Hello {{user}},", "emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.", "emails.otpSession.clientInfo": "This sign in was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the sign in, you can safely ignore this email.", @@ -41,6 +43,7 @@ "emails.otpSession.signature": "{{project}} team", "emails.mfaChallenge.subject": "Verification Code for {{project}}", "emails.mfaChallenge.preview": "Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.", + "emails.mfaChallenge.heading": "Complete two-step verification to use Appwrite Cloud", "emails.mfaChallenge.hello": "Hello {{user}},", "emails.mfaChallenge.description": "Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.", "emails.mfaChallenge.clientInfo": "This verification code was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the verification code, you can safely ignore this email.", diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 3aa9678a72..e98376dfc7 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2296,11 +2296,11 @@ App::post('/v1/account/tokens/email') $subject = $locale->getText("emails.otpSession.subject"); $preview = $locale->getText("emails.otpSession.preview"); - $customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? []; + $heading = $locale->getText("emails.otpSession.heading"); - $customEmails = $project->getAttribute('customEmails', false); - $bodyTemplate = ''; - $heading = ''; + $customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? []; + $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; $detector = new Detector($request->getUserAgent('UNKNOWN')); $agentOs = $detector->getOS(); @@ -2369,23 +2369,6 @@ App::post('/v1/account/tokens/email') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); - } elseif ($customEmails && !empty($customTemplate)) { - $subject = $customTemplate['subject']; - $preview = $customTemplate['preview']; - $heading = $customTemplate['heading']; - - $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-otp.tpl'); - $message - ->setParam('{{hello}}', $customTemplate['hello']) - ->setParam('{{description}}', $customTemplate['body'], escapeHtml: false) - ->setParam('{{thanks}}', $customTemplate['thanks']) - ->setParam('{{signature}}', $customTemplate['signature']) - ->setParam('{{clientInfo}}', '') - ->setParam('{{securityPhrase}}', '') - ->setParam('{{securityPhraseDividerDisplay}}', 'none'); - - $body = $message->render(); - $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; } $emailVariables = [ @@ -2402,7 +2385,7 @@ App::post('/v1/account/tokens/email') 'team' => '', ]; - if ($customEmails && !empty($customTemplate)) { + if ($smtpBaseTemplate === 'email-base-styled') { $emailVariables = array_merge($emailVariables, [ 'heading' => $heading, 'accentColor' => APP_EMAIL_ACCENT_COLOR, @@ -3624,7 +3607,11 @@ App::post('/v1/account/verification') $body = $locale->getText("emails.verification.body"); $preview = $locale->getText("emails.verification.preview"); $subject = $locale->getText("emails.verification.subject"); + $heading = $locale->getText("emails.verification.heading"); + $customTemplate = $project->getAttribute('templates', [])['email.verification-' . $locale->default] ?? []; + $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message @@ -3644,10 +3631,6 @@ App::post('/v1/account/verification') $senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; - $customEmails = $project->getAttribute('customEmails', false); - $bodyTemplate = ''; - $heading = ''; - if ($smtpEnabled) { if (!empty($smtp['senderEmail'])) { $senderEmail = $smtp['senderEmail']; @@ -3685,22 +3668,6 @@ App::post('/v1/account/verification') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); - } elseif ($customEmails && !empty($customTemplate)) { - $subject = $customTemplate['subject']; - $preview = $customTemplate['preview']; - $heading = $customTemplate['heading']; - - $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-verification.tpl'); - $message - ->setParam('{{hello}}', $customTemplate['hello']) - ->setParam('{{body}}', $customTemplate['body'], escapeHtml: false) - ->setParam('{{buttonText}}', $customTemplate['buttonText']) - ->setParam('{{footer}}', $customTemplate['footer']) - ->setParam('{{thanks}}', $customTemplate['thanks']) - ->setParam('{{signature}}', $customTemplate['signature']); - - $body = $message->render(); - $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; } $emailVariables = [ @@ -3713,7 +3680,7 @@ App::post('/v1/account/verification') 'team' => '', ]; - if ($customEmails && !empty($customTemplate)) { + if ($smtpBaseTemplate === 'email-base-styled') { $emailVariables = array_merge($emailVariables, [ 'heading' => $heading, 'accentColor' => APP_EMAIL_ACCENT_COLOR, @@ -4736,7 +4703,11 @@ App::post('/v1/account/mfa/challenge') $subject = $locale->getText("emails.mfaChallenge.subject"); $preview = $locale->getText("emails.mfaChallenge.preview"); + $heading = $locale->getText("emails.mfaChallenge.heading"); + $customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? []; + $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; $detector = new Detector($request->getUserAgent('UNKNOWN')); $agentOs = $detector->getOS(); @@ -4760,10 +4731,6 @@ App::post('/v1/account/mfa/challenge') $senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; - $customEmails = $project->getAttribute('customEmails', false); - $bodyTemplate = ''; - $heading = ''; - if ($smtpEnabled) { if (!empty($smtp['senderEmail'])) { $senderEmail = $smtp['senderEmail']; @@ -4801,21 +4768,6 @@ App::post('/v1/account/mfa/challenge') ->setSmtpReplyTo($replyTo) ->setSmtpSenderEmail($senderEmail) ->setSmtpSenderName($senderName); - } elseif ($customEmails && !empty($customTemplate)) { - $subject = $customTemplate['subject']; - $preview = $customTemplate['preview']; - $heading = $customTemplate['heading']; - - $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-mfa-challenge.tpl'); - $message - ->setParam('{{hello}}', $customTemplate['hello']) - ->setParam('{{description}}', $customTemplate['body'], escapeHtml: false) - ->setParam('{{thanks}}', $customTemplate['thanks']) - ->setParam('{{signature}}', $customTemplate['signature']) - ->setParam('{{clientInfo}}', ''); - - $body = $message->render(); - $bodyTemplate = __DIR__ . '/../../config/locale/templates/email-auth-styled.tpl'; } $emailVariables = [ @@ -4829,7 +4781,7 @@ App::post('/v1/account/mfa/challenge') 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN', ]; - if ($customEmails && !empty($customTemplate)) { + if ($smtpBaseTemplate === 'email-base-styled') { $emailVariables = array_merge($emailVariables, [ 'heading' => $heading, 'accentColor' => APP_EMAIL_ACCENT_COLOR, diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 117b689863..efca484ebf 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -82,6 +82,7 @@ class Mails extends Action $preview = $payload['preview'] ?? ''; $variables['subject'] = $subject; + $variables['heading'] = $variables['heading'] ?? $subject; $variables['year'] = date("Y"); $attachment = $payload['attachment'] ?? []; diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 46283e8f86..b2f85637a8 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -192,11 +192,6 @@ trait AccountBase $this->assertStringNotContainsStringIgnoringCase('Appwrite logo', $lastEmail['html']); } - // TODO: Remove this once OTP login is supported for Console. - if ($isConsoleProject) { - return; - } - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', From eeebea1dbb89a8bd36b4c36ab855214ca104719f Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Mon, 6 Oct 2025 20:13:36 +0530 Subject: [PATCH 244/274] added both collection and table id in the realtime --- src/Appwrite/Messaging/Adapter/Realtime.php | 12 ++++++++---- .../Services/Realtime/RealtimeCustomClientTest.php | 6 ++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 8b5e121dcd..35b8089668 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -312,13 +312,17 @@ class Realtime extends MessagingAdapter throw new \Exception('Collection or the Table needs to be passed to Realtime for Document/Row events in the Database.'); } + $tableId = $payload->getAttribute('$tableId', ''); + $collectionId = $payload->getAttribute('$collectionId', ''); + $resourceId = $tableId ?: $collectionId; + $channels[] = 'rows'; - $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows'; - $channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows.' . $payload->getId(); + $channels[] = 'databases.' . $database->getId() . '.tables.' . $resourceId . '.rows'; + $channels[] = 'databases.' . $database->getId() . '.tables.' . $resourceId . '.rows.' . $payload->getId(); $channels[] = 'documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents'; - $channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId(); + $channels[] = 'databases.' . $database->getId() . '.collections.' . $resourceId . '.documents'; + $channels[] = 'databases.' . $database->getId() . '.collections.' . $resourceId . '.documents.' . $payload->getId(); $roles = $collection->getAttribute('documentSecurity', false) ? \array_merge($collection->getRead(), $payload->getRead()) diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index ef39908658..e44c1170a3 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -787,6 +787,8 @@ class RealtimeCustomClientTest extends Scope $this->assertContains('documents', $response['data']['channels']); $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents.' . $documentId, $response['data']['channels']); $this->assertContains('databases.' . $databaseId . '.collections.' . $actorsId . '.documents', $response['data']['channels']); + $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows.' . $documentId, $response['data']['channels']); + $this->assertContains('databases.' . $databaseId . '.tables.' . $actorsId . '.rows', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.create", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.create", $response['data']['events']); @@ -831,6 +833,8 @@ class RealtimeCustomClientTest extends Scope $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.update", $response['data']['events']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}.update", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.update", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']); @@ -884,6 +888,8 @@ class RealtimeCustomClientTest extends Scope $this->assertContains('documents', $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows.{$documentId}", $response['data']['channels']); + $this->assertContains("databases.{$databaseId}.tables.{$actorsId}.rows", $response['data']['channels']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}.delete", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$documentId}", $response['data']['events']); $this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*.delete", $response['data']['events']); From caf18372ce36eb777fdb894b0c0af368e27ed7bc Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 6 Oct 2025 21:52:18 +0530 Subject: [PATCH 245/274] feedback --- app/config/console.php | 2 +- .../locale/templates/email-base-styled.tpl | 8 +++++ app/config/locale/templates/email-base.tpl | 7 +++- app/config/locale/templates/email-otp.tpl | 4 +-- app/config/locale/translations/en.json | 6 ++-- app/controllers/api/account.php | 35 ++++++++++++++----- app/init/constants.php | 1 + 7 files changed, 47 insertions(+), 16 deletions(-) diff --git a/app/config/console.php b/app/config/console.php index 70bb3523e6..f8f68a8039 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -49,7 +49,7 @@ $console = [ 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') ], - 'smtpBaseTemplate' => 'email-base-styled', + 'smtpBaseTemplate' => APP_BRANDED_EMAIL_BASE_TEMPLATE, ]; return $console; diff --git a/app/config/locale/templates/email-base-styled.tpl b/app/config/locale/templates/email-base-styled.tpl index 3f3ba8dd9c..37ca630d43 100644 --- a/app/config/locale/templates/email-base-styled.tpl +++ b/app/config/locale/templates/email-base-styled.tpl @@ -131,6 +131,14 @@ .social-icon > img { margin: auto; } + p.security-phrase:not(:empty) { + opacity: 0.7; + margin: 0; + padding: 0; + margin-top: 32px; + padding-top: 32px; + border-top: 1px solid #e8e9f0; + } diff --git a/app/config/locale/templates/email-base.tpl b/app/config/locale/templates/email-base.tpl index 5153b3e4fc..de632d7838 100644 --- a/app/config/locale/templates/email-base.tpl +++ b/app/config/locale/templates/email-base.tpl @@ -109,10 +109,15 @@ h* { font-family: 'Poppins', sans-serif; } - p { margin-bottom: 10px; } + p.security-phrase:not(:empty) { + opacity: 0.7; + margin-top: 32px; + padding-top: 32px; + border-top: 1px solid #e8e9f0; + } diff --git a/app/config/locale/templates/email-otp.tpl b/app/config/locale/templates/email-otp.tpl index e18a4ce725..cfcdb8f7af 100644 --- a/app/config/locale/templates/email-otp.tpl +++ b/app/config/locale/templates/email-otp.tpl @@ -15,6 +15,4 @@

{{thanks}}

{{signature}}

-
- -

{{securityPhrase}}

\ No newline at end of file +

{{securityPhrase}}

\ No newline at end of file diff --git a/app/config/locale/translations/en.json b/app/config/locale/translations/en.json index 69328e61a7..0500c4c668 100644 --- a/app/config/locale/translations/en.json +++ b/app/config/locale/translations/en.json @@ -5,7 +5,7 @@ "emails.sender": "{{project}} Team", "emails.verification.subject": "Account Verification", "emails.verification.preview": "Verify your email to activate your {{project}} account.", - "emails.verification.heading": "Verify your email to start using Appwrite Cloud", + "emails.verification.heading": "Verify your email to start using {{project}}", "emails.verification.hello": "Hello {{user}},", "emails.verification.body": "Follow this link to verify your email address to your {{b}}{{project}}{{/b}} account.", "emails.verification.footer": "If you didn’t ask to verify this address, you can ignore this message.", @@ -34,7 +34,7 @@ "emails.sessionAlert.signature": "{{project}} team", "emails.otpSession.subject": "OTP for {{project}} Login", "emails.otpSession.preview": "Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.", - "emails.otpSession.heading": "Login with OTP to use Appwrite Cloud", + "emails.otpSession.heading": "Login with OTP to use {{project}}", "emails.otpSession.hello": "Hello {{user}},", "emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.", "emails.otpSession.clientInfo": "This sign in was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the sign in, you can safely ignore this email.", @@ -43,7 +43,7 @@ "emails.otpSession.signature": "{{project}} team", "emails.mfaChallenge.subject": "Verification Code for {{project}}", "emails.mfaChallenge.preview": "Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.", - "emails.mfaChallenge.heading": "Complete two-step verification to use Appwrite Cloud", + "emails.mfaChallenge.heading": "Complete two-step verification to use {{project}}", "emails.mfaChallenge.hello": "Hello {{user}},", "emails.mfaChallenge.description": "Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.", "emails.mfaChallenge.clientInfo": "This verification code was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the verification code, you can safely ignore this email.", diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e98376dfc7..af9a772b17 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -69,6 +69,12 @@ use Utopia\Validator\WhiteList; $oauthDefaultSuccess = '/console/auth/oauth2/success'; $oauthDefaultFailure = '/console/auth/oauth2/failure'; +function containsDirectoryTraversal(string $path) +{ + // Matches '../', './', '/..', or absolute paths starting with '/' + return preg_match('/(\.\.\/|\.\/|\/\.\.|\/)/', $path); +} + function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails) { $subject = $locale->getText("emails.sessionAlert.subject"); @@ -2300,6 +2306,11 @@ App::post('/v1/account/tokens/email') $customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? []; $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + + if (containsDirectoryTraversal($smtpBaseTemplate)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); + } + $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; $detector = new Detector($request->getUserAgent('UNKNOWN')); @@ -2317,10 +2328,8 @@ App::post('/v1/account/tokens/email') if (!empty($phrase)) { $message->setParam('{{securityPhrase}}', $locale->getText("emails.otpSession.securityPhrase")); - $message->setParam('{{securityPhraseDividerDisplay}}', 'block'); } else { $message->setParam('{{securityPhrase}}', ''); - $message->setParam('{{securityPhraseDividerDisplay}}', 'none'); } $body = $message->render(); @@ -2372,6 +2381,7 @@ App::post('/v1/account/tokens/email') } $emailVariables = [ + 'heading' => $heading, 'direction' => $locale->getText('settings.direction'), // {{user}}, {{project}} and {{otp}} are required in the templates 'user' => $user->getAttribute('name'), @@ -2385,9 +2395,8 @@ App::post('/v1/account/tokens/email') 'team' => '', ]; - if ($smtpBaseTemplate === 'email-base-styled') { + if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) { $emailVariables = array_merge($emailVariables, [ - 'heading' => $heading, 'accentColor' => APP_EMAIL_ACCENT_COLOR, 'logoUrl' => APP_EMAIL_LOGO_URL, 'twitterUrl' => APP_SOCIAL_TWITTER, @@ -3611,6 +3620,11 @@ App::post('/v1/account/verification') $customTemplate = $project->getAttribute('templates', [])['email.verification-' . $locale->default] ?? []; $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + + if (containsDirectoryTraversal($smtpBaseTemplate)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); + } + $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); @@ -3671,6 +3685,7 @@ App::post('/v1/account/verification') } $emailVariables = [ + 'heading' => $heading, 'direction' => $locale->getText('settings.direction'), // {{user}}, {{redirect}} and {{project}} are required in default and custom templates 'user' => $user->getAttribute('name'), @@ -3680,9 +3695,8 @@ App::post('/v1/account/verification') 'team' => '', ]; - if ($smtpBaseTemplate === 'email-base-styled') { + if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) { $emailVariables = array_merge($emailVariables, [ - 'heading' => $heading, 'accentColor' => APP_EMAIL_ACCENT_COLOR, 'logoUrl' => APP_EMAIL_LOGO_URL, 'twitterUrl' => APP_SOCIAL_TWITTER, @@ -4707,6 +4721,11 @@ App::post('/v1/account/mfa/challenge') $customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? []; $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + + if (containsDirectoryTraversal($smtpBaseTemplate)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); + } + $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; $detector = new Detector($request->getUserAgent('UNKNOWN')); @@ -4771,6 +4790,7 @@ App::post('/v1/account/mfa/challenge') } $emailVariables = [ + 'heading' => $heading, 'direction' => $locale->getText('settings.direction'), // {{user}}, {{project}} and {{otp}} are required in the templates 'user' => $user->getAttribute('name'), @@ -4781,9 +4801,8 @@ App::post('/v1/account/mfa/challenge') 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN', ]; - if ($smtpBaseTemplate === 'email-base-styled') { + if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) { $emailVariables = array_merge($emailVariables, [ - 'heading' => $heading, 'accentColor' => APP_EMAIL_ACCENT_COLOR, 'logoUrl' => APP_EMAIL_LOGO_URL, 'twitterUrl' => APP_SOCIAL_TWITTER, diff --git a/app/init/constants.php b/app/init/constants.php index 28cf8a4052..16ddcf5551 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -85,6 +85,7 @@ const APP_PLATFORM_CLIENT = 'client'; const APP_PLATFORM_CONSOLE = 'console'; const APP_VCS_GITHUB_USERNAME = 'Appwrite'; const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io'; +const APP_BRANDED_EMAIL_BASE_TEMPLATE = 'email-base-styled'; // Database Reconnect const DATABASE_RECONNECT_SLEEP = 2; From cb70a6e2c8431bc66b26c4e5a8b8e4e0bb1cd0e4 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 6 Oct 2025 22:14:29 +0530 Subject: [PATCH 246/274] Add project name in email subject --- app/config/locale/translations/en.json | 4 ++-- tests/e2e/Services/Account/AccountCustomClientTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/config/locale/translations/en.json b/app/config/locale/translations/en.json index e2ee20b2d7..36c98c4656 100644 --- a/app/config/locale/translations/en.json +++ b/app/config/locale/translations/en.json @@ -3,7 +3,7 @@ "settings.locale": "en", "settings.direction": "ltr", "emails.sender": "{{project}} Team", - "emails.verification.subject": "Account Verification", + "emails.verification.subject": "Account Verification for {{project}}", "emails.verification.preview": "Verify your email to activate your {{project}} account.", "emails.verification.hello": "Hello {{user}},", "emails.verification.body": "Follow this link to verify your email address to your {{b}}{{project}}{{/b}} account.", @@ -46,7 +46,7 @@ "emails.mfaChallenge.clientInfo": "This verification code was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the verification code, you can safely ignore this email.", "emails.mfaChallenge.thanks": "Thanks,", "emails.mfaChallenge.signature": "{{project}} team", - "emails.recovery.subject": "Password Reset", + "emails.recovery.subject": "Password Reset for {{project}}", "emails.recovery.preview": "Reset your {{project}} password using the link.", "emails.recovery.hello": "Hello {{user}},", "emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.", diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 8322508cf6..b1b4f47b34 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -924,7 +924,7 @@ class AccountCustomClientTest extends Scope $this->assertEquals($email, $lastEmail['to'][0]['address']); $this->assertEquals($name, $lastEmail['to'][0]['name']); - $this->assertEquals('Account Verification', $lastEmail['subject']); + $this->assertEquals('Account Verification for ' . $this->getProject()['name'], $lastEmail['subject']); $this->assertStringContainsStringIgnoringCase('Verify your email to activate your ' . $this->getProject()['name'] . ' account.', $lastEmail['text']); $tokens = $this->extractQueryParamsFromEmailLink($lastEmail['html']); @@ -1228,7 +1228,7 @@ class AccountCustomClientTest extends Scope $this->assertEquals($email, $lastEmail['to'][0]['address']); $this->assertEquals($name, $lastEmail['to'][0]['name']); - $this->assertEquals('Password Reset', $lastEmail['subject']); + $this->assertEquals('Password Reset for ' . $this->getProject()['name'], $lastEmail['subject']); $this->assertStringContainsStringIgnoringCase('Reset your ' . $this->getProject()['name'] . ' password using the link.', $lastEmail['text']); From 99c0ccacf1fd0971d020636220f42580a8a1b532 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 7 Oct 2025 16:11:18 +1300 Subject: [PATCH 247/274] Update database --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index bad4ed32af..d4f1f85151 100644 --- a/composer.lock +++ b/composer.lock @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "2.3.1", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "a91e04080d7f13c35c4885dea0ffebc33cd33e1f" + "reference": "35c978ddd20b649d119296094686d3cc9fcf161f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/a91e04080d7f13c35c4885dea0ffebc33cd33e1f", - "reference": "a91e04080d7f13c35c4885dea0ffebc33cd33e1f", + "url": "https://api.github.com/repos/utopia-php/database/zipball/35c978ddd20b649d119296094686d3cc9fcf161f", + "reference": "35c978ddd20b649d119296094686d3cc9fcf161f", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.3.1" + "source": "https://github.com/utopia-php/database/tree/2.3.2" }, - "time": "2025-10-06T04:29:14+00:00" + "time": "2025-10-07T03:09:32+00:00" }, { "name": "utopia-php/detector", From 52ac68b511603a26e8debe7c2204aa8bf394a709 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 7 Oct 2025 16:12:20 +1300 Subject: [PATCH 248/274] Trigger CI From 024d13c1b244d6cbcb6152c26c68a805807e1ab7 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 7 Oct 2025 09:58:39 +0530 Subject: [PATCH 249/274] update naming --- app/config/specs/open-api3-1.8.x-client.json | 4 ++-- app/config/specs/open-api3-1.8.x-console.json | 4 ++-- app/config/specs/open-api3-1.8.x-server.json | 4 ++-- app/config/specs/open-api3-latest-client.json | 4 ++-- app/config/specs/open-api3-latest-console.json | 4 ++-- app/config/specs/open-api3-latest-server.json | 4 ++-- app/config/specs/swagger2-1.8.x-client.json | 4 ++-- app/config/specs/swagger2-1.8.x-console.json | 4 ++-- app/config/specs/swagger2-1.8.x-server.json | 4 ++-- app/config/specs/swagger2-latest-client.json | 4 ++-- app/config/specs/swagger2-latest-console.json | 4 ++-- app/config/specs/swagger2-latest-server.json | 4 ++-- app/controllers/api/account.php | 10 ++++++---- 13 files changed, 30 insertions(+), 28 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index 0ab37fa677..1b0b97ba7c 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -3464,7 +3464,7 @@ } } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3712,7 +3712,7 @@ } } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 79ec41cc63..582ba3e785 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -3473,7 +3473,7 @@ } } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3719,7 +3719,7 @@ } } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 056010c96f..d6e1ad432f 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -3161,7 +3161,7 @@ } } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3415,7 +3415,7 @@ } } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 0ab37fa677..1b0b97ba7c 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3464,7 +3464,7 @@ } } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3712,7 +3712,7 @@ } } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 79ec41cc63..582ba3e785 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3473,7 +3473,7 @@ } } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3719,7 +3719,7 @@ } } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 056010c96f..d6e1ad432f 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3161,7 +3161,7 @@ } } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3415,7 +3415,7 @@ } } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index bedc855c36..15a66ce85f 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -3599,7 +3599,7 @@ ] } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3854,7 +3854,7 @@ ] } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 0f04574558..d4502a5ef9 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -3618,7 +3618,7 @@ ] } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3871,7 +3871,7 @@ ] } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index d137016f47..42dbd7c4f7 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -3302,7 +3302,7 @@ ] } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3563,7 +3563,7 @@ ] } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index bedc855c36..15a66ce85f 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3599,7 +3599,7 @@ ] } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3854,7 +3854,7 @@ ] } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 0f04574558..d4502a5ef9 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3618,7 +3618,7 @@ ] } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3871,7 +3871,7 @@ ] } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index d137016f47..42dbd7c4f7 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3302,7 +3302,7 @@ ] } }, - "\/account\/verification\/email": { + "\/account\/verifications\/email": { "post": { "summary": "Create email verification", "operationId": "accountCreateEmailVerification", @@ -3563,7 +3563,7 @@ ] } }, - "\/account\/verification\/phone": { + "\/account\/verifications\/phone": { "post": { "summary": "Create phone verification", "operationId": "accountCreatePhoneVerification", diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index c22c0babee..b23e2489fa 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -3506,7 +3506,7 @@ App::put('/v1/account/recovery') $response->dynamic($recoveryDocument, Response::MODEL_TOKEN); }); -App::post('/v1/account/verification/email') +App::post('/v1/account/verifications/email') ->alias('/v1/account/verification') ->desc('Create email verification') ->groups(['api', 'account']) @@ -3693,7 +3693,7 @@ App::post('/v1/account/verification/email') ->dynamic($verification, Response::MODEL_TOKEN); }); -App::put('/v1/account/verification/email') +App::put('/v1/account/verifications/email') ->alias('/v1/account/verification') ->desc('Update email verification (confirmation)') ->groups(['api', 'account']) @@ -3781,7 +3781,8 @@ App::put('/v1/account/verification/email') $response->dynamic($verification, Response::MODEL_TOKEN); }); -App::post('/v1/account/verification/phone') +App::post('/v1/account/verifications/phone') + ->alias('/v1/account/verification/phone') ->desc('Create phone verification') ->groups(['api', 'account', 'auth']) ->label('scope', 'account') @@ -3930,7 +3931,8 @@ App::post('/v1/account/verification/phone') ->dynamic($verification, Response::MODEL_TOKEN); }); -App::put('/v1/account/verification/phone') +App::put('/v1/account/verifications/phone') + ->alias('/v1/account/verification/phone') ->desc('Update phone verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') From 61b073a6a227be9ff4626718a183532440010a06 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 7 Oct 2025 18:53:38 +1300 Subject: [PATCH 250/274] Clean up --- composer.lock | 24 ++-- src/Appwrite/Databases/TransactionState.php | 29 ++--- .../Transactions/Operations/Create.php | 1 - .../Http/Databases/Transactions/Update.php | 106 +++++++----------- 4 files changed, 71 insertions(+), 89 deletions(-) diff --git a/composer.lock b/composer.lock index 8450c9df98..bad4ed32af 100644 --- a/composer.lock +++ b/composer.lock @@ -1927,16 +1927,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.47", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", "shasum": "" }, "require": { @@ -2017,7 +2017,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" }, "funding": [ { @@ -2033,7 +2033,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2025-10-06T01:07:24+00:00" }, { "name": "psr/container", @@ -3635,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "2.2.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "19a3ab2ae99578861dd1a7b4fdbfb62b37d09447" + "reference": "a91e04080d7f13c35c4885dea0ffebc33cd33e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/19a3ab2ae99578861dd1a7b4fdbfb62b37d09447", - "reference": "19a3ab2ae99578861dd1a7b4fdbfb62b37d09447", + "url": "https://api.github.com/repos/utopia-php/database/zipball/a91e04080d7f13c35c4885dea0ffebc33cd33e1f", + "reference": "a91e04080d7f13c35c4885dea0ffebc33cd33e1f", "shasum": "" }, "require": { @@ -3685,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/2.2.0" + "source": "https://github.com/utopia-php/database/tree/2.3.1" }, - "time": "2025-09-26T02:23:30+00:00" + "time": "2025-10-06T04:29:14+00:00" }, { "name": "utopia-php/detector", diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 2d35ce92ae..522e160a87 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -45,32 +45,27 @@ class TransactionState ?string $transactionId = null, array $queries = [] ): Document { - // If no transaction, use normal database retrieval if ($transactionId === null) { return $this->dbForProject->getDocument($collectionId, $documentId, $queries); } $state = $this->getTransactionState($transactionId); - // Check if document exists in transaction state if (isset($state[$collectionId][$documentId])) { $docState = $state[$collectionId][$documentId]; if (!$docState['exists']) { - // Document was deleted in transaction return new Document(); } if ($docState['action'] === 'create') { - // Document was created in transaction, return the created version return $this->applyProjection($docState['document'], $queries); } if ($docState['action'] === 'update' || $docState['action'] === 'upsert') { - // This is an update to an existing document, merge with committed version + // Merge with committed version $committedDoc = $this->dbForProject->getDocument($collectionId, $documentId, $queries); if (!$committedDoc->isEmpty()) { - // Apply the updates from transaction foreach ($docState['document']->getAttributes() as $key => $value) { if ($key !== '$id') { $committedDoc->setAttribute($key, $value); @@ -79,13 +74,11 @@ class TransactionState // Reapply projection in case transaction added new fields return $this->applyProjection($committedDoc, $queries); } elseif ($docState['action'] === 'upsert') { - // Upsert created a new document since committed doc doesn't exist return $this->applyProjection($docState['document'], $queries); } } } - // Document not affected by transaction, return committed version return $this->dbForProject->getDocument($collectionId, $documentId, $queries); } @@ -307,18 +300,21 @@ class TransactionState /** * Apply bulk upsert to documents in transaction state * - * This allows bulk operations within a transaction to see each other's changes. + * This merges partial upsert data with full documents from transaction state, + * preventing validation errors when upserting documents created in the same transaction. * * @param string $collectionId Collection ID - * @param array $documents Array of Document objects to upsert + * @param array $documents Array of Document objects to upsert (can be partial) * @param array &$state Transaction state (passed by reference) - * @return void + * @return array Merged documents ready for database upsert */ public function applyBulkUpsertToState( string $collectionId, array $documents, array &$state - ): void { + ): array { + $mergedDocuments = []; + foreach ($documents as $doc) { if (!($doc instanceof Document)) { continue; @@ -329,7 +325,7 @@ class TransactionState continue; } - // If document exists in state, update it; otherwise it will be handled by DB upsert + // If document exists in state, update it and use the merged version if (isset($state[$collectionId][$docId])) { // Apply updates to existing state document foreach ($doc->getArrayCopy() as $key => $value) { @@ -337,8 +333,15 @@ class TransactionState $state[$collectionId][$docId]->setAttribute($key, $value); } } + // Use the full merged document from state + $mergedDocuments[] = $state[$collectionId][$docId]; + } else { + // Document not in state - use original partial data for DB upsert + $mergedDocuments[] = $doc; } } + + return $mergedDocuments; } /** diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 5f6fa4f1a6..c3ba45bdce 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -113,7 +113,6 @@ class Create extends Action throw new Exception(Exception::COLLECTION_NOT_FOUND); } - // Check if collection has relationships for bulk operations if (\in_array($operation['action'], ['bulkCreate', 'bulkUpdate', 'bulkUpsert', 'bulkDelete'])) { $hasRelationships = \array_filter( $collection->getAttribute('attributes', []), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index d522c699a8..e33a731d0e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -103,7 +103,6 @@ class Update extends Action */ public function action(string $transactionId, bool $commit, bool $rollback, UtopiaResponse $response, Database $dbForProject, Document $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks): void { - if (!$commit && !$rollback) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true'); } @@ -128,8 +127,6 @@ class Update extends Action if ($commit) { $operations = []; - - // Track metrics for usage stats $totalOperations = 0; $databaseOperations = []; @@ -139,15 +136,12 @@ class Update extends Action 'status' => 'committing', ]))); - // Fetch operations ordered by sequence by default to replay operations in exact order they were created $operations = Authorization::skip(fn () => $dbForProject->find('transactionLogs', [ Query::equal('transactionInternalId', [$transaction->getSequence()]), Query::orderAsc(), Query::limit(PHP_INT_MAX), ])); - - // Track transaction state for cross-operation visibility $state = []; foreach ($operations as $operation) { @@ -159,7 +153,6 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; - // For delete operations, fetch the document before deleting for realtime events if ($action === 'delete' && $documentId && empty($data)) { $doc = $dbForProject->getDocument($collectionId, $documentId); if (!$doc->isEmpty()) { @@ -168,7 +161,6 @@ class Update extends Action } } - // Track operations for stats $totalOperations++; $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + 1; @@ -176,7 +168,6 @@ class Update extends Action $data = $data->getArrayCopy(); } - // Execute the operation based on its type switch ($action) { case 'create': $this->handleCreateOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); @@ -217,44 +208,37 @@ class Update extends Action new Document(['status' => 'committed']) )); - // Clear the transaction logs $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); }); } catch (NotFoundException $e) { - // Transaction has been rolled back, now mark it as failed Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); } catch (DuplicateException|ConflictException $e) { - // Transaction has been rolled back, now mark it as failed Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::TRANSACTION_CONFLICT, previous: $e); } catch (StructureException $e) { - // Transaction has been rolled back, now mark it as failed Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); } catch (LimitException $e) { - // Transaction has been rolled back, now mark it as failed Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, $e->getMessage()); } catch (TransactionException $e) { - // Transaction has been rolled back, now mark it as failed Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::TRANSACTION_FAILED, $e->getMessage()); } catch (QueryException $e) { - // Transaction has been rolled back, now mark it as failed Authorization::skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); @@ -264,7 +248,6 @@ class Update extends Action $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $totalOperations); - // Add per-database metrics foreach ($databaseOperations as $sequence => $count) { $queueForStatsUsage->addMetric( str_replace('{databaseInternalId}', $sequence, METRIC_DATABASE_ID_OPERATIONS_WRITES), @@ -272,8 +255,6 @@ class Update extends Action ); } - // Trigger realtime events for each operation - foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; $collectionInternalId = $operation['collectionInternalId']; @@ -316,7 +297,6 @@ class Update extends Action $eventAction = 'create'; $docId = $documentId ?? $data['$id'] ?? null; if ($docId) { - // Fetch the created document from the database $doc = $dbForProject->getDocument($collectionId, $docId); if (!$doc->isEmpty()) { $documentsToTrigger[] = $doc; @@ -328,7 +308,6 @@ class Update extends Action case 'decrement': $eventAction = 'update'; if ($documentId) { - // Fetch the updated document from the database $doc = $dbForProject->getDocument($collectionId, $documentId); if (!$doc->isEmpty()) { $documentsToTrigger[] = $doc; @@ -338,15 +317,13 @@ class Update extends Action case 'delete': $eventAction = 'delete'; if ($documentId && !empty($data)) { - // For delete, use the fetched document data (fetched before deletion) $documentsToTrigger[] = new Document(array_merge($data, ['$id' => $documentId])); } break; case 'upsert': - $eventAction = 'update'; // Upsert is treated as update for events + $eventAction = 'update'; $docId = $documentId ?? $data['$id'] ?? null; if ($docId) { - // Fetch the upserted document from the database $doc = $dbForProject->getDocument($collectionId, $docId); if (!$doc->isEmpty()) { $documentsToTrigger[] = $doc; @@ -360,15 +337,11 @@ class Update extends Action break; } - // Trigger events for each document - $eventString = "databases.[databaseId].{$contextKey}s.[{$groupId}].{$resourcePlural}.[{$resourceId}]." . $eventAction; $queueForEvents->setEvent($eventString); foreach ($documentsToTrigger as $doc) { - - // Add table/collection IDs to the payload for realtime channels $payload = $doc->getArrayCopy(); $payload['$tableId'] = $collection->getId(); $payload['$collectionId'] = $collection->getId(); @@ -464,7 +437,6 @@ class Update extends Action $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { - // Update the state document directly without timestamp wrapper $state[$collectionId][$documentId] = $dbForProject->updateDocument( $collectionId, $documentId, @@ -473,7 +445,6 @@ class Update extends Action return; } - // Use timestamp wrapper for independent operations $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { $document = $dbForProject->updateDocument( $collectionId, @@ -510,15 +481,21 @@ class Update extends Action $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { - // Upsert the state document directly without timestamp wrapper + // Merge partial upsert data with full document from transaction state + $existingDoc = $state[$collectionId][$documentId]; + foreach ($data as $key => $value) { + if ($key !== '$id') { + $existingDoc->setAttribute($key, $value); + } + } + $state[$collectionId][$documentId] = $dbForProject->upsertDocument( $collectionId, - new Document($data), + $existingDoc, ); return; } - // Use timestamp wrapper for independent operations $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { $state[$collectionId][$documentId] = $dbForProject->upsertDocument( $collectionId, @@ -549,13 +526,11 @@ class Update extends Action $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { - // Delete without timestamp wrapper $dbForProject->deleteDocument($collectionId, $documentId); unset($state[$collectionId][$documentId]); return; } - // Use timestamp wrapper for independent operations $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, &$state) { $deleted = $dbForProject->deleteDocument($collectionId, $documentId); if (!$deleted) { @@ -591,7 +566,6 @@ class Update extends Action $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { - // Increment without timestamp wrapper $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, @@ -602,7 +576,6 @@ class Update extends Action return; } - // Use timestamp wrapper for independent operations $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data) { $dbForProject->increaseDocumentAttribute( collection: $collectionId, @@ -638,7 +611,6 @@ class Update extends Action $dependent = isset($state[$collectionId][$documentId]); if ($dependent) { - // Decrement without timestamp wrapper $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, @@ -649,8 +621,6 @@ class Update extends Action return; } - // Use timestamp wrapper for independent operations - // Use timestamp wrapper for independent operations $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, @@ -681,7 +651,6 @@ class Update extends Action array &$state ): void { $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { - // Convert data arrays to Document objects if needed $documents = \array_map(function ($doc) { return $doc instanceof Document ? $doc : new Document($doc); }, $data); @@ -721,29 +690,50 @@ class Update extends Action $queries = Query::parseQueries($data['queries'] ?? []); $updateData = new Document($data['data']); - // First, update documents in the committed database + $dependentDocs = []; + + $transactionState->applyBulkUpdateToState($collectionId, $updateData, $queries, $state); + + // Clone the document before passing to updateDocuments to prevent mutation + // The database layer mutates the input document, which would corrupt transaction state $dbForProject->updateDocuments( $collectionId, - $updateData, + clone $updateData, $queries, - onNext: function (Document $updated, Document $old) use (&$state, $collectionId, $createdAt) { - // Check if this document was created/modified in this transaction + onNext: function (Document $updated, Document $old) use (&$state, $collectionId, $createdAt, &$dependentDocs) { $dependent = isset($state[$collectionId][$updated->getId()]); - // If not in transaction state, check for timestamp conflicts - if (!$dependent) { + if ($dependent) { + $dependentDocs[] = $updated->getId(); + } else { $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); if ($oldUpdatedAt > $createdAt) { throw new ConflictException('Document was updated after the request timestamp'); } + $state[$collectionId][$updated->getId()] = $updated; } - - $state[$collectionId][$updated->getId()] = $updated; } ); - // Also update documents in the transaction state that match the query - $transactionState->applyBulkUpdateToState($collectionId, $updateData, $queries, $state); + // Re-write dependent documents from state to database to fix partial updates + if (!empty($dependentDocs)) { + $documentsToRewrite = []; + foreach ($dependentDocs as $docId) { + if (isset($state[$collectionId][$docId])) { + $documentsToRewrite[] = $state[$collectionId][$docId]; + } + } + + if (!empty($documentsToRewrite)) { + $dbForProject->upsertDocuments( + $collectionId, + $documentsToRewrite, + onNext: function (Document $upserted) use (&$state, $collectionId) { + $state[$collectionId][$upserted->getId()] = $upserted; + } + ); + } + } } /** @@ -767,25 +757,19 @@ class Update extends Action \DateTime $createdAt, array &$state ): void { - // Convert data arrays to Document objects if needed $documents = \array_map(function ($doc) { return $doc instanceof Document ? $doc : new Document($doc); }, $data); - // First, apply upserts to documents in the transaction state - // This ensures documents created in this transaction are updated properly - $transactionState->applyBulkUpsertToState($collectionId, $documents, $state); + $mergedDocuments = $transactionState->applyBulkUpsertToState($collectionId, $documents, $state); - // Then run bulk upsert on committed database, checking manually in callback $dbForProject->upsertDocuments( $collectionId, - $documents, + $mergedDocuments, onNext: function (Document $upserted, ?Document $old) use (&$state, $collectionId, $createdAt) { if ($old !== null) { - // This is an update - check if document was created/modified in this transaction $dependent = isset($state[$collectionId][$upserted->getId()]); - // If not in transaction state, check for timestamp conflicts if (!$dependent) { $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); if ($oldUpdatedAt > $createdAt) { @@ -794,7 +778,6 @@ class Update extends Action } } - // If $old is null, this is a create operation - no timestamp check needed $state[$collectionId][$upserted->getId()] = $upserted; } ); @@ -830,7 +813,6 @@ class Update extends Action onNext: function (Document $deleted, Document $old) use (&$state, $collectionId, $createdAt) { $dependent = isset($state[$collectionId][$deleted->getId()]); - // If not in transaction state, check for timestamp conflicts if (!$dependent) { $oldUpdatedAt = new \DateTime($old->getUpdatedAt()); if ($oldUpdatedAt > $createdAt) { @@ -838,14 +820,12 @@ class Update extends Action } } - // Remove from state after successful deletion if (isset($state[$collectionId][$deleted->getId()])) { unset($state[$collectionId][$deleted->getId()]); } } ); - // Also delete documents in the transaction state that match the query $transactionState->applyBulkDeleteToState($collectionId, $queries, $state); } } From eb7306a4fa7f4105d8afbe9dc39e6eae077e699c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 7 Oct 2025 22:17:59 +1300 Subject: [PATCH 251/274] Add missing state handlers --- src/Appwrite/Databases/TransactionState.php | 105 +++++++++++++++ .../Transactions/TransactionsBase.php | 123 ++++++++++++++++++ 2 files changed, 228 insertions(+) diff --git a/src/Appwrite/Databases/TransactionState.php b/src/Appwrite/Databases/TransactionState.php index 8df2bc657c..23dc6fc2e9 100644 --- a/src/Appwrite/Databases/TransactionState.php +++ b/src/Appwrite/Databases/TransactionState.php @@ -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; } } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index 1ea8d0cc9a..e50bb9f2bf 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -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'); + } + } } From 066e0bc236a8bc2a38345effe3d907621cc34ee4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 7 Oct 2025 23:33:19 +1300 Subject: [PATCH 252/274] Validate inc/dec operation value is numeric --- src/Appwrite/Utopia/Database/Validator/Operation.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Appwrite/Utopia/Database/Validator/Operation.php b/src/Appwrite/Utopia/Database/Validator/Operation.php index 4e421689e3..e6884ac677 100644 --- a/src/Appwrite/Utopia/Database/Validator/Operation.php +++ b/src/Appwrite/Utopia/Database/Validator/Operation.php @@ -202,6 +202,11 @@ class Operation extends Validator $this->description = "Key '{$attributeKey}' is required in data for {$action}"; return false; } + // Validate 'value' is numeric if provided (defaults to 1 if omitted) + if (\array_key_exists('value', $value['data']) && !\is_numeric($value['data']['value'])) { + $this->description = "Key 'value' must be a numeric value for {$action}"; + return false; + } } return true; From 6e0d3767465716cbe0bb82110685a89622ac9980 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 7 Oct 2025 16:07:47 +0530 Subject: [PATCH 253/274] use filevalidator --- app/controllers/api/account.php | 16 +- composer.lock | 269 ++++++++++++++++---------------- 2 files changed, 144 insertions(+), 141 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index af9a772b17..6836c6bfaf 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -58,6 +58,7 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; +use Utopia\Storage\Validator\FileName; use Utopia\System\System; use Utopia\Validator\ArrayList; use Utopia\Validator\Assoc; @@ -69,12 +70,6 @@ use Utopia\Validator\WhiteList; $oauthDefaultSuccess = '/console/auth/oauth2/success'; $oauthDefaultFailure = '/console/auth/oauth2/failure'; -function containsDirectoryTraversal(string $path) -{ - // Matches '../', './', '/..', or absolute paths starting with '/' - return preg_match('/(\.\.\/|\.\/|\/\.\.|\/)/', $path); -} - function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails) { $subject = $locale->getText("emails.sessionAlert.subject"); @@ -2307,7 +2302,8 @@ App::post('/v1/account/tokens/email') $customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? []; $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); - if (containsDirectoryTraversal($smtpBaseTemplate)) { + $validator = new FileName(); + if (!$validator->isValid($smtpBaseTemplate)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); } @@ -3621,7 +3617,8 @@ App::post('/v1/account/verification') $customTemplate = $project->getAttribute('templates', [])['email.verification-' . $locale->default] ?? []; $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); - if (containsDirectoryTraversal($smtpBaseTemplate)) { + $validator = new FileName(); + if (!$validator->isValid($smtpBaseTemplate)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); } @@ -4722,7 +4719,8 @@ App::post('/v1/account/mfa/challenge') $customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? []; $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); - if (containsDirectoryTraversal($smtpBaseTemplate)) { + $validator = new FileName(); + if (!$validator->isValid($smtpBaseTemplate)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); } diff --git a/composer.lock b/composer.lock index bd70f229ad..8e600c326f 100644 --- a/composer.lock +++ b/composer.lock @@ -756,24 +756,21 @@ }, { "name": "google/protobuf", - "version": "v4.32.0", + "version": "v4.32.1", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646" + "reference": "c4ed1c1f9bbc1e91766e2cd6c0af749324fe87cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646", - "reference": "9a9a92ecbe9c671dc1863f6d4a91ea3ea12c8646", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/c4ed1c1f9bbc1e91766e2cd6c0af749324fe87cb", + "reference": "c4ed1c1f9bbc1e91766e2cd6c0af749324fe87cb", "shasum": "" }, "require": { "php": ">=8.1.0" }, - "provide": { - "ext-protobuf": "*" - }, "require-dev": { "phpunit/phpunit": ">=5.0.0 <8.5.27" }, @@ -797,9 +794,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.32.0" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.32.1" }, - "time": "2025-08-14T20:00:33+00:00" + "time": "2025-09-14T05:14:52+00:00" }, { "name": "league/csv", @@ -1162,20 +1159,20 @@ }, { "name": "open-telemetry/api", - "version": "1.5.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "7692075f486c14d8cfd37fba98a08a5667f089e5" + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/7692075f486c14d8cfd37fba98a08a5667f089e5", - "reference": "7692075f486c14d8cfd37fba98a08a5667f089e5", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522", + "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522", "shasum": "" }, "require": { - "open-telemetry/context": "^1.0", + "open-telemetry/context": "^1.4", "php": "^8.1", "psr/log": "^1.1|^2.0|^3.0", "symfony/polyfill-php82": "^1.26" @@ -1191,7 +1188,7 @@ ] }, "branch-alias": { - "dev-main": "1.4.x-dev" + "dev-main": "1.7.x-dev" } }, "autoload": { @@ -1228,20 +1225,20 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-07T23:07:38+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/context", - "version": "1.3.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "438f71812242db3f196fb4c717c6f92cbc819be6" + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6", - "reference": "438f71812242db3f196fb4c717c6f92cbc819be6", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/d4c4470b541ce72000d18c339cfee633e4c8e0cf", + "reference": "d4c4470b541ce72000d18c339cfee633e4c8e0cf", "shasum": "" }, "require": { @@ -1287,7 +1284,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-13T01:12:00+00:00" + "time": "2025-09-19T00:05:49+00:00" }, { "name": "open-telemetry/exporter-otlp", @@ -1355,16 +1352,16 @@ }, { "name": "open-telemetry/gen-otlp-protobuf", - "version": "1.5.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git", - "reference": "585bafddd4ae6565de154610b10a787a455c9ba0" + "reference": "673af5b06545b513466081884b47ef15a536edde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/585bafddd4ae6565de154610b10a787a455c9ba0", - "reference": "585bafddd4ae6565de154610b10a787a455c9ba0", + "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/673af5b06545b513466081884b47ef15a536edde", + "reference": "673af5b06545b513466081884b47ef15a536edde", "shasum": "" }, "require": { @@ -1414,27 +1411,27 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-01-15T23:07:07+00:00" + "time": "2025-09-17T23:10:12+00:00" }, { "name": "open-telemetry/sdk", - "version": "1.7.1", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06" + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/52690d4b37ae4f091af773eef3c238ed2bc0aa06", - "reference": "52690d4b37ae4f091af773eef3c238ed2bc0aa06", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", + "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", "shasum": "" }, "require": { "ext-json": "*", "nyholm/psr7-server": "^1.1", - "open-telemetry/api": "^1.4", - "open-telemetry/context": "^1.0", + "open-telemetry/api": "^1.7", + "open-telemetry/context": "^1.4", "open-telemetry/sem-conv": "^1.0", "php": "^8.1", "php-http/discovery": "^1.14", @@ -1468,7 +1465,7 @@ ] }, "branch-alias": { - "dev-main": "1.0.x-dev" + "dev-main": "1.9.x-dev" } }, "autoload": { @@ -1511,7 +1508,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-09-05T07:17:06+00:00" + "time": "2025-10-02T23:44:28+00:00" }, { "name": "open-telemetry/sem-conv", @@ -1572,16 +1569,16 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105" + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226", + "reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226", "shasum": "" }, "require": { @@ -1635,7 +1632,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:18:48+00:00" + "time": "2025-09-24T15:12:37+00:00" }, { "name": "paragonie/random_compat", @@ -1930,16 +1927,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.46", + "version": "3.0.47", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", - "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", "shasum": "" }, "require": { @@ -2020,7 +2017,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" }, "funding": [ { @@ -2036,7 +2033,7 @@ "type": "tidelift" } ], - "time": "2025-06-26T16:29:55+00:00" + "time": "2025-10-06T01:07:24+00:00" }, { "name": "psr/container", @@ -2599,16 +2596,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", - "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", + "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", "shasum": "" }, "require": { @@ -2675,7 +2672,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.3" + "source": "https://github.com/symfony/http-client/tree/v7.3.4" }, "funding": [ { @@ -2695,7 +2692,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T07:45:05+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/http-client-contracts", @@ -3638,16 +3635,16 @@ }, { "name": "utopia-php/database", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "b5ea4d133a1a4e747b7522e61e072289129a06f4" + "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/b5ea4d133a1a4e747b7522e61e072289129a06f4", - "reference": "b5ea4d133a1a4e747b7522e61e072289129a06f4", + "url": "https://api.github.com/repos/utopia-php/database/zipball/56efe4daaf23abb753553acffccdcc04cd6178c9", + "reference": "56efe4daaf23abb753553acffccdcc04cd6178c9", "shasum": "" }, "require": { @@ -3688,9 +3685,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.4.1" + "source": "https://github.com/utopia-php/database/tree/1.5.1" }, - "time": "2025-09-05T13:23:52+00:00" + "time": "2025-10-01T04:44:14+00:00" }, { "name": "utopia-php/detector", @@ -3795,16 +3792,16 @@ }, { "name": "utopia-php/domains", - "version": "0.8.0", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "650463d2a1525273eb03223c48da9fb1a768bbf7" + "reference": "d5f903e93c105407da6374e411c4805b7decd8a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/650463d2a1525273eb03223c48da9fb1a768bbf7", - "reference": "650463d2a1525273eb03223c48da9fb1a768bbf7", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/d5f903e93c105407da6374e411c4805b7decd8a8", + "reference": "d5f903e93c105407da6374e411c4805b7decd8a8", "shasum": "" }, "require": { @@ -3850,9 +3847,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.8.0" + "source": "https://github.com/utopia-php/domains/tree/0.8.1" }, - "time": "2025-05-16T10:03:59+00:00" + "time": "2025-10-03T11:58:53+00:00" }, { "name": "utopia-php/dsn", @@ -3942,16 +3939,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.27", + "version": "0.33.28", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37" + "reference": "5aaa94d406577b0059ad28c78022606890dc6de0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d9d10a895e85c8c7675220347cc6109db9d3bd37", - "reference": "d9d10a895e85c8c7675220347cc6109db9d3bd37", + "url": "https://api.github.com/repos/utopia-php/http/zipball/5aaa94d406577b0059ad28c78022606890dc6de0", + "reference": "5aaa94d406577b0059ad28c78022606890dc6de0", "shasum": "" }, "require": { @@ -3983,9 +3980,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.27" + "source": "https://github.com/utopia-php/http/tree/0.33.28" }, - "time": "2025-09-07T18:40:53+00:00" + "time": "2025-09-25T10:44:24+00:00" }, { "name": "utopia-php/image", @@ -4190,16 +4187,16 @@ }, { "name": "utopia-php/migration", - "version": "1.0.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "38171023efd3abe650d2abc5ac65f5df52311da6" + "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/38171023efd3abe650d2abc5ac65f5df52311da6", - "reference": "38171023efd3abe650d2abc5ac65f5df52311da6", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/42ff497c5231f5a727d1e229419ff1d2195d8093", + "reference": "42ff497c5231f5a727d1e229419ff1d2195d8093", "shasum": "" }, "require": { @@ -4240,9 +4237,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.0.1" + "source": "https://github.com/utopia-php/migration/tree/1.2.0" }, - "time": "2025-08-28T13:41:25+00:00" + "time": "2025-09-24T10:32:24+00:00" }, { "name": "utopia-php/orchestration", @@ -4569,16 +4566,16 @@ }, { "name": "utopia-php/storage", - "version": "0.18.13", + "version": "0.18.14", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "3d8ce53ae042173bf230445e996056c5f65ded22" + "reference": "4f14ec952c6f4006dd0613e55bbf7631814fbc00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/3d8ce53ae042173bf230445e996056c5f65ded22", - "reference": "3d8ce53ae042173bf230445e996056c5f65ded22", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/4f14ec952c6f4006dd0613e55bbf7631814fbc00", + "reference": "4f14ec952c6f4006dd0613e55bbf7631814fbc00", "shasum": "" }, "require": { @@ -4621,9 +4618,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.13" + "source": "https://github.com/utopia-php/storage/tree/0.18.14" }, - "time": "2025-05-26T13:10:35+00:00" + "time": "2025-10-07T10:21:47+00:00" }, { "name": "utopia-php/swoole", @@ -5007,16 +5004,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.3.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "375a6c9b168db6fdf58fbe0d49d2261d80700b4a" + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/375a6c9b168db6fdf58fbe0d49d2261d80700b4a", - "reference": "375a6c9b168db6fdf58fbe0d49d2261d80700b4a", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", + "reference": "e1ca749398189f36ec6d6afb8e9f64e9cb37e0a3", "shasum": "" }, "require": { @@ -5052,9 +5049,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.3.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.4.3" }, - "time": "2025-09-05T15:50:35+00:00" + "time": "2025-10-01T06:25:19+00:00" }, { "name": "doctrine/annotations", @@ -5281,16 +5278,16 @@ }, { "name": "laravel/pint", - "version": "v1.24.0", + "version": "v1.25.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a" + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a", - "reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a", + "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", "shasum": "" }, "require": { @@ -5301,9 +5298,9 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.82.2", - "illuminate/view": "^11.45.1", - "larastan/larastan": "^3.5.0", + "friendsofphp/php-cs-fixer": "^3.87.2", + "illuminate/view": "^11.46.0", + "larastan/larastan": "^3.7.1", "laravel-zero/framework": "^11.45.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^2.3.1", @@ -5314,9 +5311,6 @@ ], "type": "project", "autoload": { - "files": [ - "overrides/Runner/Parallel/ProcessFactory.php" - ], "psr-4": { "App\\": "app/", "Database\\Seeders\\": "database/seeders/", @@ -5346,7 +5340,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-07-10T18:09:32+00:00" + "time": "2025-09-19T02:57:12+00:00" }, { "name": "matthiasmullie/minify", @@ -6236,16 +6230,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.25", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "049c011e01be805202d8eebedef49f769a8ec7b7" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/049c011e01be805202d8eebedef49f769a8ec7b7", - "reference": "049c011e01be805202d8eebedef49f769a8ec7b7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -6270,7 +6264,7 @@ "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", + "sebastian/exporter": "^4.0.8", "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", @@ -6319,7 +6313,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.25" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -6343,7 +6337,7 @@ "type": "tidelift" } ], - "time": "2025-08-20T14:38:31+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "psr/cache", @@ -6835,16 +6829,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -6900,15 +6894,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", @@ -7491,16 +7497,16 @@ }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -7565,7 +7571,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -7585,7 +7591,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/filesystem", @@ -8128,16 +8134,16 @@ }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -8169,7 +8175,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -8189,20 +8195,20 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -8217,7 +8223,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -8260,7 +8265,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -8280,7 +8285,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "textalk/websocket", @@ -8512,7 +8517,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8536,5 +8541,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From e935fc251026d21a4433672ab26072a1f799b671 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 8 Oct 2025 09:52:28 +0530 Subject: [PATCH 254/274] Add minor releases for all SDKs - deprecate createVerification, add createEmailVerification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds minor version releases for all client and server SDKs to deprecate the `createVerification` method in the Account service and introduce the new `createEmailVerification` method as its replacement. Updated SDKs: Client SDKs: - Android: 11.0.0 → 11.1.0 - Web: 21.0.0 → 21.1.0 - Flutter: 20.0.0 → 20.1.0 - Apple: 13.0.0 → 13.1.0 - React Native: 0.15.0 → 0.16.0 Server SDKs: - Node.js: 20.0.0 → 20.1.0 - PHP: 17.2.0 → 17.3.0 - Python: 13.2.0 → 13.3.0 - Ruby: 19.0.0 → 19.1.0 - Go: 0.13.0 → 0.14.0 - .NET: 0.19.0 → 0.20.0 - Dart: 19.0.0 → 19.1.0 - Kotlin: 12.0.0 → 12.1.0 - Swift: 13.0.0 → 13.1.0 Console SDK: - CLI: 10.0.1 → 10.1.0 --- app/config/platforms.php | 30 +++++++++---------- .../account/create-email-verification.md | 2 +- .../account/create-phone-verification.md | 2 +- .../examples/account/create-verification.md | 2 +- .../account/update-email-verification.md | 2 +- .../account/update-phone-verification.md | 2 +- .../examples/account/update-verification.md | 2 +- .../account/create-email-verification.md | 2 +- .../account/create-phone-verification.md | 2 +- .../examples/account/create-verification.md | 2 +- .../account/update-email-verification.md | 2 +- .../account/update-phone-verification.md | 2 +- .../examples/account/update-verification.md | 2 +- docs/sdks/android/CHANGELOG.md | 5 ++++ docs/sdks/apple/CHANGELOG.md | 5 ++++ docs/sdks/cli/CHANGELOG.md | 5 ++++ docs/sdks/dart/CHANGELOG.md | 5 ++++ docs/sdks/dotnet/CHANGELOG.md | 5 ++++ docs/sdks/flutter/CHANGELOG.md | 5 ++++ docs/sdks/go/CHANGELOG.md | 5 ++++ docs/sdks/kotlin/CHANGELOG.md | 5 ++++ docs/sdks/nodejs/CHANGELOG.md | 5 ++++ docs/sdks/php/CHANGELOG.md | 5 ++++ docs/sdks/python/CHANGELOG.md | 5 ++++ docs/sdks/react-native/CHANGELOG.md | 5 ++++ docs/sdks/ruby/CHANGELOG.md | 5 ++++ docs/sdks/swift/CHANGELOG.md | 5 ++++ docs/sdks/web/CHANGELOG.md | 5 ++++ src/Appwrite/Platform/Tasks/SDKs.php | 28 ++++++++++++++++- 29 files changed, 129 insertions(+), 28 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index e5a297fb0c..663c254b66 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '21.0.0', + 'version' => '21.1.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '20.0.0', + 'version' => '20.1.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '13.0.0', + 'version' => '13.1.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '11.0.0', + 'version' => '11.1.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -139,7 +139,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.15.0', + 'version' => '0.16.0', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '10.0.1', + 'version' => '10.1.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '20.0.0', + 'version' => '20.1.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -281,7 +281,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '17.2.0', + 'version' => '17.3.0', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -300,7 +300,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '13.2.0', + 'version' => '13.3.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '19.0.0', + 'version' => '19.1.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => '0.13.0', + 'version' => '0.14.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, @@ -357,7 +357,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.19.0', + 'version' => '0.20.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '19.0.0', + 'version' => '19.1.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '12.0.0', + 'version' => '12.1.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '13.0.0', + 'version' => '13.1.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md b/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md index ed5479dbe5..63fcd5765e 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md +++ b/docs/examples/1.8.x/client-rest/examples/account/create-email-verification.md @@ -1,4 +1,4 @@ -POST /v1/account/verification HTTP/1.1 +POST /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-rest/examples/account/create-phone-verification.md b/docs/examples/1.8.x/client-rest/examples/account/create-phone-verification.md index 57b3b7d160..b48c9249b3 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/create-phone-verification.md +++ b/docs/examples/1.8.x/client-rest/examples/account/create-phone-verification.md @@ -1,4 +1,4 @@ -POST /v1/account/verification/phone HTTP/1.1 +POST /v1/account/verifications/phone HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-rest/examples/account/create-verification.md b/docs/examples/1.8.x/client-rest/examples/account/create-verification.md index ed5479dbe5..63fcd5765e 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/create-verification.md +++ b/docs/examples/1.8.x/client-rest/examples/account/create-verification.md @@ -1,4 +1,4 @@ -POST /v1/account/verification HTTP/1.1 +POST /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md b/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md index a4dcbf76a3..c7c6c34e52 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md +++ b/docs/examples/1.8.x/client-rest/examples/account/update-email-verification.md @@ -1,4 +1,4 @@ -PUT /v1/account/verification HTTP/1.1 +PUT /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-rest/examples/account/update-phone-verification.md b/docs/examples/1.8.x/client-rest/examples/account/update-phone-verification.md index 1d4dc22520..faa6478150 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/update-phone-verification.md +++ b/docs/examples/1.8.x/client-rest/examples/account/update-phone-verification.md @@ -1,4 +1,4 @@ -PUT /v1/account/verification/phone HTTP/1.1 +PUT /v1/account/verifications/phone HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-rest/examples/account/update-verification.md b/docs/examples/1.8.x/client-rest/examples/account/update-verification.md index a4dcbf76a3..c7c6c34e52 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/update-verification.md +++ b/docs/examples/1.8.x/client-rest/examples/account/update-verification.md @@ -1,4 +1,4 @@ -PUT /v1/account/verification HTTP/1.1 +PUT /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md b/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md index ed5479dbe5..63fcd5765e 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md +++ b/docs/examples/1.8.x/server-rest/examples/account/create-email-verification.md @@ -1,4 +1,4 @@ -POST /v1/account/verification HTTP/1.1 +POST /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/create-phone-verification.md b/docs/examples/1.8.x/server-rest/examples/account/create-phone-verification.md index 57b3b7d160..b48c9249b3 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/create-phone-verification.md +++ b/docs/examples/1.8.x/server-rest/examples/account/create-phone-verification.md @@ -1,4 +1,4 @@ -POST /v1/account/verification/phone HTTP/1.1 +POST /v1/account/verifications/phone HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/create-verification.md b/docs/examples/1.8.x/server-rest/examples/account/create-verification.md index ed5479dbe5..63fcd5765e 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/create-verification.md +++ b/docs/examples/1.8.x/server-rest/examples/account/create-verification.md @@ -1,4 +1,4 @@ -POST /v1/account/verification HTTP/1.1 +POST /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md b/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md index a4dcbf76a3..c7c6c34e52 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md +++ b/docs/examples/1.8.x/server-rest/examples/account/update-email-verification.md @@ -1,4 +1,4 @@ -PUT /v1/account/verification HTTP/1.1 +PUT /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/update-phone-verification.md b/docs/examples/1.8.x/server-rest/examples/account/update-phone-verification.md index 1d4dc22520..faa6478150 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/update-phone-verification.md +++ b/docs/examples/1.8.x/server-rest/examples/account/update-phone-verification.md @@ -1,4 +1,4 @@ -PUT /v1/account/verification/phone HTTP/1.1 +PUT /v1/account/verifications/phone HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/update-verification.md b/docs/examples/1.8.x/server-rest/examples/account/update-verification.md index a4dcbf76a3..c7c6c34e52 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/update-verification.md +++ b/docs/examples/1.8.x/server-rest/examples/account/update-verification.md @@ -1,4 +1,4 @@ -PUT /v1/account/verification HTTP/1.1 +PUT /v1/account/verifications/email HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/sdks/android/CHANGELOG.md b/docs/sdks/android/CHANGELOG.md index 38d0c4be2d..b6b9e4072b 100644 --- a/docs/sdks/android/CHANGELOG.md +++ b/docs/sdks/android/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 11.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 8.2.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/apple/CHANGELOG.md b/docs/sdks/apple/CHANGELOG.md index f62eb708f1..dce2df1804 100644 --- a/docs/sdks/apple/CHANGELOG.md +++ b/docs/sdks/apple/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 13.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 10.2.0 * Update sdk to use swift-native doc comments instead of jsdoc styled comments as per [Swift Documentation Comments](https://github.com/swiftlang/swift/blob/main/docs/DocumentationComments.md) diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 5af193d491..289c1ef0cb 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 10.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 10.0.1 * Fix CLI Dart model generation issues diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 7e33794153..c25e66708b 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 19.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 18.1.0 * Add `orderRandom` query support diff --git a/docs/sdks/dotnet/CHANGELOG.md b/docs/sdks/dotnet/CHANGELOG.md index bbfee108a7..7bd7d1d267 100644 --- a/docs/sdks/dotnet/CHANGELOG.md +++ b/docs/sdks/dotnet/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 0.20.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 0.15.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index f704415675..176efe4a5d 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 20.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 19.1.0 * Add `orderRandom` query support diff --git a/docs/sdks/go/CHANGELOG.md b/docs/sdks/go/CHANGELOG.md index 418c7db5e5..7c72fd03be 100644 --- a/docs/sdks/go/CHANGELOG.md +++ b/docs/sdks/go/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 0.14.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 0.9.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/kotlin/CHANGELOG.md b/docs/sdks/kotlin/CHANGELOG.md index c7194d5391..40c32751d7 100644 --- a/docs/sdks/kotlin/CHANGELOG.md +++ b/docs/sdks/kotlin/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 12.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 9.1.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/nodejs/CHANGELOG.md b/docs/sdks/nodejs/CHANGELOG.md index 7d6926dd1d..7225d3d92f 100644 --- a/docs/sdks/nodejs/CHANGELOG.md +++ b/docs/sdks/nodejs/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 20.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 17.2.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/php/CHANGELOG.md b/docs/sdks/php/CHANGELOG.md index d5a323c4cb..b1221264b2 100644 --- a/docs/sdks/php/CHANGELOG.md +++ b/docs/sdks/php/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 17.3.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 15.1.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/python/CHANGELOG.md b/docs/sdks/python/CHANGELOG.md index ff63134a20..584aa5cf5c 100644 --- a/docs/sdks/python/CHANGELOG.md +++ b/docs/sdks/python/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 13.3.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 11.1.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index eda0bca26e..79ade8cc6c 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1,5 +1,10 @@ # Change log +## 0.16.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 0.11.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/ruby/CHANGELOG.md b/docs/sdks/ruby/CHANGELOG.md index 1f2e10beea..0498d7cfb5 100644 --- a/docs/sdks/ruby/CHANGELOG.md +++ b/docs/sdks/ruby/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 19.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 16.1.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/docs/sdks/swift/CHANGELOG.md b/docs/sdks/swift/CHANGELOG.md index 6cb1cca595..3d73a868f3 100644 --- a/docs/sdks/swift/CHANGELOG.md +++ b/docs/sdks/swift/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 13.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 10.2.0 * Update sdk to use swift-native doc comments instead of jsdoc styled comments as per [Swift Documentation Comments](https://github.com/swiftlang/swift/blob/main/docs/DocumentationComments.md) diff --git a/docs/sdks/web/CHANGELOG.md b/docs/sdks/web/CHANGELOG.md index 328ef1d5a8..a28d868075 100644 --- a/docs/sdks/web/CHANGELOG.md +++ b/docs/sdks/web/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 21.1.0 + +* Deprecate `createVerification` method in `Account` service +* Add `createEmailVerification` method in `Account` service + ## 18.2.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 63ffddf950..47c72c4424 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -58,6 +58,8 @@ class SDKs extends Action $git ??= Console::confirm('Should we use git push? (yes/no)'); $git = $git === 'yes'; + $prUrls = []; + if ($git) { $production ??= Console::confirm('Type "Appwrite" to push code to production git repos'); $production = $production === 'Appwrite'; @@ -346,7 +348,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND if ($prReturnCode === 0) { Console::success("Successfully created pull request for {$language['name']} SDK"); if (!empty($prOutput)) { - Console::info("PR URL: " . end($prOutput)); + $prUrls[$language['name']] = end($prOutput); } } else { $errorMessage = implode("\n", $prOutput); @@ -366,6 +368,21 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND if ($updateReturnCode === 0) { Console::success("Successfully updated pull request for {$language['name']} SDK"); + + $prUrlCommand = 'cd ' . $target . ' && \ + gh pr view "' . $gitBranch . '" \ + --repo "' . $repoName . '" \ + --json url \ + --jq .url \ + 2>&1'; + + $prUrlOutput = []; + $prUrlReturnCode = 0; + \exec($prUrlCommand, $prUrlOutput, $prUrlReturnCode); + + if ($prUrlReturnCode === 0 && !empty($prUrlOutput)) { + $prUrls[$language['name']] = $prUrlOutput[0]; + } } else { $updateErrorMessage = implode("\n", $updateOutput); Console::error("Failed to update pull request for {$language['name']} SDK: " . $updateErrorMessage); @@ -396,5 +413,14 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND } } } + + if (!empty($prUrls)) { + Console::log(''); + Console::log('Pull Request Summary'); + foreach ($prUrls as $sdkName => $url) { + Console::log("{$sdkName}: {$url}"); + } + Console::log(''); + } } } From adf9ec3e75608d88008e0b5eff6584714ad8f22a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 8 Oct 2025 13:05:46 +0530 Subject: [PATCH 255/274] add automatic releases --- src/Appwrite/Platform/Tasks/SDKs.php | 162 +++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 47c72c4424..87c70d214b 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -46,27 +46,35 @@ class SDKs extends Action ->param('git', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we use git push?', optional: true) ->param('production', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we push to production?', optional: true) ->param('message', null, new Nullable(new Text(256)), 'Commit Message', optional: true) + ->param('release', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we create releases?', optional: true) + ->param('commit', null, new Nullable(new WhiteList(['yes', 'no'])), 'Actually create releases (yes) or dry-run (no)?', optional: true) ->callback($this->action(...)); } - public function action(?string $selectedPlatform, ?string $selectedSDK, ?string $version, ?string $git, ?string $production, ?string $message): void + public function action(?string $selectedPlatform, ?string $selectedSDK, ?string $version, ?string $git, ?string $production, ?string $message, ?string $release, ?string $commit): void { $selectedPlatform ??= Console::confirm('Choose Platform ("' . APP_PLATFORM_CLIENT . '", "' . APP_PLATFORM_SERVER . '", "' . APP_PLATFORM_CONSOLE . '" or "*" for all):'); $selectedSDK ??= \strtolower(Console::confirm('Choose SDK ("*" for all):')); $version ??= Console::confirm('Choose an Appwrite version'); - $git ??= Console::confirm('Should we use git push? (yes/no)'); - $git = $git === 'yes'; + $createRelease = ($release === 'yes'); + $commitRelease = ($commit === 'yes'); - $prUrls = []; + if (!$createRelease) { + $git ??= Console::confirm('Should we use git push? (yes/no)'); + $git = ($git === 'yes'); - if ($git) { - $production ??= Console::confirm('Type "Appwrite" to push code to production git repos'); - $production = $production === 'Appwrite'; - $message ??= Console::confirm('Please enter your commit message:'); + $prUrls = []; + $createPr = false; - $createPr = Console::confirm('Should we create pull request automatically? (yes/no)'); - $createPr = $createPr === 'yes'; + if ($git) { + $production ??= Console::confirm('Type "Appwrite" to push code to production git repos'); + $production = $production === 'Appwrite'; + $message ??= Console::confirm('Please enter your commit message:'); + + $createPr = Console::confirm('Should we create pull request automatically? (yes/no)'); + $createPr = ($createPr === 'yes'); + } } if (!\in_array($version, [ @@ -245,6 +253,97 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND throw new \Exception('Language "' . $language['key'] . '" not supported'); } + if ($createRelease) { + $releaseVersion = $language['version']; + + $repoName = $language['gitUserName'] . '/' . $language['gitRepoName']; + $releaseNotes = $this->extractReleaseNotes($changelog, $releaseVersion); + if (empty($releaseNotes)) { + $releaseNotes = "Release version {$releaseVersion}"; + } + + $releaseTitle = $releaseVersion; + $releaseTarget = $language['repoBranch'] ?? 'main'; + + if ($repoName === '/') { + Console::warning("{$language['name']} SDK is not an SDK, skipping release"); + continue; + } + + // Check if release already exists + $checkReleaseCommand = 'gh release view "' . $releaseVersion . '" --repo "' . $repoName . '" --json url --jq ".url" 2>/dev/null'; + $existingReleaseUrl = trim(\shell_exec($checkReleaseCommand) ?? ''); + + if (!empty($existingReleaseUrl)) { + Console::warning("Release {$releaseVersion} already exists for {$language['name']} SDK, skipping..."); + Console::info("Existing release: {$existingReleaseUrl}"); + continue; + } + + $previousVersion = ''; + $tagListCommand = 'gh release list --repo "' . $repoName . '" --limit 1 --json tagName --jq ".[0].tagName" 2>&1'; + $previousVersion = trim(\shell_exec($tagListCommand) ?? ''); + + $formattedNotes = "## What's Changed\n\n"; + $formattedNotes .= $releaseNotes . "\n\n"; + + if (!empty($previousVersion)) { + $formattedNotes .= "**Full Changelog**: https://github.com/" . $repoName . "/compare/" . $previousVersion . "..." . $releaseVersion; + } else { + $formattedNotes .= "**Full Changelog**: https://github.com/" . $repoName . "/releases/tag/" . $releaseVersion; + } + + if (!$commitRelease) { + Console::info("[DRY RUN] Would create release for {$language['name']} SDK:"); + Console::log(" Repository: {$repoName}"); + Console::log(" Version: {$releaseVersion}"); + Console::log(" Title: {$releaseTitle}"); + Console::log(" Target Branch: {$releaseTarget}"); + Console::log(" Previous Version: " . ($previousVersion ?: 'N/A')); + Console::log(" Release Notes:"); + Console::log(" " . str_replace("\n", "\n ", $formattedNotes)); + Console::log(''); + } else { + Console::info("Creating release {$releaseVersion} for {$language['name']} SDK..."); + + $tempNotesFile = \tempnam(\sys_get_temp_dir(), 'release_notes_'); + \file_put_contents($tempNotesFile, $formattedNotes); + + $releaseCommand = 'gh release create "' . $releaseVersion . '" \ + --repo "' . $repoName . '" \ + --title "' . $releaseTitle . '" \ + --notes-file "' . $tempNotesFile . '" \ + --target "' . $releaseTarget . '" \ + 2>&1'; + + $releaseOutput = []; + $releaseReturnCode = 0; + \exec($releaseCommand, $releaseOutput, $releaseReturnCode); + + \unlink($tempNotesFile); + + if ($releaseReturnCode === 0) { + // Extract release URL from output + $releaseUrl = ''; + foreach ($releaseOutput as $line) { + if (strpos($line, 'https://github.com/') !== false) { + $releaseUrl = trim($line); + break; + } + } + + Console::success("Successfully created release {$releaseVersion} for {$language['name']} SDK"); + if (!empty($releaseUrl)) { + Console::info("Release URL: {$releaseUrl}"); + } + } else { + $errorMessage = implode("\n", $releaseOutput); + Console::error("Failed to create release for {$language['name']} SDK: " . $errorMessage); + } + } + continue; + } + Console::info("Generating {$language['name']} SDK..."); $sdk = new SDK($config, new Swagger2($spec)); @@ -423,4 +522,47 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND Console::log(''); } } + + /** + * Extract release notes from changelog for a specific version + * + * @param string $changelog + * @param string $version + * @return string + */ + private function extractReleaseNotes(string $changelog, string $version): string + { + if (empty($changelog)) { + return ''; + } + + // Changelog version header pattern: ## 0.14.0 + $pattern = '/^##\s+' . preg_quote($version, '/') . '\s*$/m'; + $startPos = false; + if (preg_match($pattern, $changelog, $matches, PREG_OFFSET_CAPTURE)) { + $startPos = $matches[0][1]; + } + + if ($startPos === false) { + return ''; + } + + $contentStart = strpos($changelog, "\n", $startPos); + if ($contentStart === false) { + return ''; + } + $contentStart++; + + $nextHeaderPattern = '/^##?\s+/m'; + $remainingContent = substr($changelog, $contentStart); + + if (preg_match($nextHeaderPattern, $remainingContent, $matches, PREG_OFFSET_CAPTURE)) { + $endPos = $matches[0][1]; + $notes = substr($remainingContent, 0, $endPos); + } else { + $notes = $remainingContent; + } + + return trim($notes); + } } From 8d2df2b744c75533050411f65fb8e27c70d15682 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 8 Oct 2025 13:25:28 +0530 Subject: [PATCH 256/274] add go changes --- app/config/platforms.php | 2 +- docs/sdks/go/CHANGELOG.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 663c254b66..51930d1728 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => '0.14.0', + 'version' => 'v0.12.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, diff --git a/docs/sdks/go/CHANGELOG.md b/docs/sdks/go/CHANGELOG.md index 7c72fd03be..9191f77aa3 100644 --- a/docs/sdks/go/CHANGELOG.md +++ b/docs/sdks/go/CHANGELOG.md @@ -1,9 +1,10 @@ # Change Log -## 0.14.0 +## v0.12.0 * Deprecate `createVerification` method in `Account` service * Add `createEmailVerification` method in `Account` service +* Add `orderRandom` query support ## 0.9.0 From dcad82c46b9e70ecf007188bda6c84522ca54fff Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 13:45:19 +1300 Subject: [PATCH 257/274] Update db --- composer.lock | 13 +++++++------ .../Http/Databases/Transactions/Update.php | 5 +---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index d4f1f85151..1afa3f5109 100644 --- a/composer.lock +++ b/composer.lock @@ -4566,16 +4566,16 @@ }, { "name": "utopia-php/storage", - "version": "0.18.13", + "version": "0.18.14", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "3d8ce53ae042173bf230445e996056c5f65ded22" + "reference": "4f14ec952c6f4006dd0613e55bbf7631814fbc00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/3d8ce53ae042173bf230445e996056c5f65ded22", - "reference": "3d8ce53ae042173bf230445e996056c5f65ded22", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/4f14ec952c6f4006dd0613e55bbf7631814fbc00", + "reference": "4f14ec952c6f4006dd0613e55bbf7631814fbc00", "shasum": "" }, "require": { @@ -4618,9 +4618,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.13" + "source": "https://github.com/utopia-php/storage/tree/0.18.14" }, - "time": "2025-05-26T13:10:35+00:00" + "time": "2025-10-07T10:21:47+00:00" }, { "name": "utopia-php/swoole", @@ -5127,6 +5127,7 @@ "issues": "https://github.com/doctrine/annotations/issues", "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, + "abandoned": true, "time": "2024-09-05T10:17:24+00:00" }, { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index e33a731d0e..ec0fab2486 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -263,7 +263,6 @@ class Update extends Action $documentId = $operation['documentId']; $data = $operation['data']; - if ($data instanceof Document) { $data = $data->getArrayCopy(); } @@ -351,9 +350,7 @@ class Update extends Action ->setParam('rowId', $doc->getId()) ->setPayload($payload); - $project = $queueForEvents->getProject(); - $result = $queueForRealtime->from($queueForEvents)->trigger(); - + $queueForRealtime->from($queueForEvents)->trigger(); $queueForFunctions->from($queueForEvents)->trigger(); $queueForWebhooks->from($queueForEvents)->trigger(); } From 3d3f50064ddb4f520bd627c4181cbedb0bb57a31 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 15:50:18 +1300 Subject: [PATCH 258/274] Force set state on increment --- .../Http/Databases/Transactions/Update.php | 4 +- .../Transactions/TransactionsBase.php | 440 ++++++++++++++++++ 2 files changed, 442 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index ec0fab2486..311a5c6e7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -573,8 +573,8 @@ class Update extends Action return; } - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data) { - $dbForProject->increaseDocumentAttribute( + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { + $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, attribute: $data[$this->getAttributeKey()], diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index e50bb9f2bf..dc18338aa0 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -3732,6 +3732,446 @@ trait TransactionsBase $this->assertEquals(0, $row['body']['score']); } + /** + * Test increment followed by update (read-your-writes) + * This test ensures that after an increment operation, subsequent operations + * in the same transaction can see the incremented value in the transaction state. + */ + public function testIncrementThenUpdate(): void + { + // Create database and table + $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' => 'IncrementUpdateTestDB' + ]); + + $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' => 'CounterTable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add columns + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'default' => 0, + ]); + + $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' => 50, + 'required' => false, + ]); + + sleep(2); + + // Create initial row + $row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'test_row', + 'data' => [ + 'counter' => 10, + 'status' => 'initial' + ] + ]); + + $this->assertEquals(201, $row['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'], + ], $this->getHeaders())); + + $transactionId = $transaction['body']['$id']; + + // Add operations: increment then update + // The update operation needs to see the document in transaction state + // to properly merge the changes + $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, + 'tableId' => $tableId, + 'action' => 'increment', + 'rowId' => 'test_row', + 'data' => [ + 'column' => 'counter', + 'value' => 5, + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'update', + 'rowId' => 'test_row', + 'data' => [ + 'status' => 'updated' + ] + ], + ] + ]); + + $this->assertEquals(201, $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 final values - both increment and update should be applied + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test_row", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals(15, $row['body']['counter'], 'Counter should be incremented: 10 + 5 = 15'); + $this->assertEquals('updated', $row['body']['status'], 'Status should be updated'); + } + + public function testBulkUpdateWithDependentDocuments(): void + { + // Create database and table + $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' => 'BulkUpdateDependentDB' + ]); + + $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::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add columns + $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' => 50, + 'required' => false, + ]); + + sleep(2); + + // Create transaction + $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 a document, then bulk update it - this triggers the state structure bug + $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, + 'tableId' => $tableId, + 'action' => 'create', + 'rowId' => 'doc1', + 'data' => [ + 'status' => 'pending' + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'bulkUpdate', + 'data' => [ + 'queries' => [], + 'data' => [ + 'status' => 'approved' + ] + ] + ], + ] + ]); + + $this->assertEquals(201, $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'], 'Bulk update should succeed on dependent documents'); + + // Verify the document was updated + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals('approved', $row['body']['status']); + } + + /** + * Test bulk delete with dependent documents (Bug #2 regression test) + */ + public function testBulkDeleteWithDependentDocuments(): 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' => 'BulkDeleteDependentDB' + ]); + + $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::create(Role::any()), + Permission::read(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' => 'name', + 'size' => 50, + 'required' => false, + ]); + + sleep(2); + + $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 then bulk delete + $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, + 'tableId' => $tableId, + 'action' => 'create', + 'rowId' => 'doc1', + 'data' => [ + 'name' => 'Test' + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'bulkDelete', + 'data' => [ + 'queries' => [], + ] + ], + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $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'], 'Bulk delete should succeed on dependent documents'); + + // Verify document was deleted + $rows = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(0, $rows['body']['total']); + } + + /** + * Test bulk upsert with dependent documents (Bug #3 regression test) + */ + public function testBulkUpsertWithDependentDocuments(): 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' => 'BulkUpsertDependentDB' + ]); + + $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::create(Role::any()), + Permission::read(Role::any()), + Permission::update(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' => 50, + 'required' => false, + ]); + + sleep(2); + + $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 then bulk upsert same document + $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, + 'tableId' => $tableId, + 'action' => 'create', + 'rowId' => 'doc1', + 'data' => [ + 'status' => 'pending' + ] + ], + [ + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'action' => 'bulkUpsert', + 'data' => [ + [ + '$id' => 'doc1', + 'status' => 'approved' + ] + ] + ], + ] + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + $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'], 'Bulk upsert should succeed on dependent documents'); + + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals('approved', $row['body']['status']); + } + /** * Test bulk update operations in transaction */ From 8193f0fcac1db1c8ca74a66fce53a7d80d39a8ed Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 16:24:25 +1300 Subject: [PATCH 259/274] Ensure create/upsert stores in state by generated ID not unique string --- .../Http/Databases/Transactions/Update.php | 8 +- .../Transactions/TransactionsBase.php | 139 ++++++++++++++++++ 2 files changed, 144 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 311a5c6e7a..5d29bba34b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -403,10 +403,11 @@ class Update extends Action $data['$id'] = $documentId; } $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { - $state[$collectionId][$data['$id']] = $dbForProject->createDocument( + $doc = $dbForProject->createDocument( $collectionId, new Document($data), ); + $state[$collectionId][$doc->getId()] = $doc; }); } @@ -493,11 +494,12 @@ class Update extends Action return; } - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { - $state[$collectionId][$documentId] = $dbForProject->upsertDocument( + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { + $doc = $dbForProject->upsertDocument( $collectionId, new Document($data), ); + $state[$collectionId][$doc->getId()] = $doc; }); } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index dc18338aa0..b64d249e97 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -5176,4 +5176,143 @@ trait TransactionsBase $this->assertEquals('processed', $response['body']['flag'], 'Bulk update should have matched document created in same transaction'); } } + + /** + * Test upsert with auto-generated ID followed by update + * This tests that the transaction state properly stores the document under its actual ID, + * not under null when the ID is auto-generated + */ + public function testUpsertAutoIdThenUpdate(): void + { + // Create database and table + $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' => 'UpsertAutoIDTestDB' + ]); + + $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' => 'TestCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Create columns + $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' => 'name', + 'size' => 256, + 'required' => true, + ]); + + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'counter', + 'required' => false, + 'min' => 0, + 'max' => 10000, + ]); + + 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'], + ], $this->getHeaders())); + + $transactionId = $transaction['body']['$id']; + + // First create a document in the transaction + $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' => ID::unique(), + 'data' => [ + 'name' => 'Initial document', + 'counter' => 5 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $docId = $response['body']['$id']; + + // Now upsert the same document using ID::unique() in the path + // The database will recognize it exists and update it, generating a new auto ID if needed + // This tests that handleUpsertOperation properly captures the actual document ID + $response = $this->client->call(Client::METHOD_PUT, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/{$docId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'name' => 'Upserted in transaction', + 'counter' => 10 + ], + 'transactionId' => $transactionId + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Now try to update the same document again in the same transaction + // This verifies that the upsert properly stored the document under its actual ID in state + $response = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/{$docId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'data' => [ + 'name' => 'Updated after upsert', + 'counter' => 20 + ], + '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 the document has the final updated values + $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('Updated after upsert', $response['body']['name']); + $this->assertEquals(20, $response['body']['counter']); + } } From 3b225b8ec31827829ff7674ad005e0bac44da284 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 16:52:40 +1300 Subject: [PATCH 260/274] Update specs --- app/config/specs/open-api3-1.8.x-client.json | 4 ++-- app/config/specs/open-api3-1.8.x-console.json | 4 ++-- app/config/specs/open-api3-1.8.x-server.json | 4 ++-- app/config/specs/open-api3-latest-client.json | 4 ++-- app/config/specs/open-api3-latest-console.json | 4 ++-- app/config/specs/open-api3-latest-server.json | 4 ++-- app/config/specs/swagger2-1.8.x-client.json | 4 ++-- app/config/specs/swagger2-1.8.x-console.json | 4 ++-- app/config/specs/swagger2-1.8.x-server.json | 4 ++-- app/config/specs/swagger2-latest-client.json | 4 ++-- app/config/specs/swagger2-latest-console.json | 4 ++-- app/config/specs/swagger2-latest-server.json | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index 87897125dc..bf9f8bbece 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -4942,7 +4942,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -5075,7 +5075,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index a389651dd1..73b8ebb1de 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -5341,7 +5341,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -5474,7 +5474,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 497912cbff..b345a36501 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -4883,7 +4883,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -5020,7 +5020,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 87897125dc..bf9f8bbece 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -4942,7 +4942,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -5075,7 +5075,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index a389651dd1..73b8ebb1de 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -5341,7 +5341,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -5474,7 +5474,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 497912cbff..b345a36501 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -4883,7 +4883,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", @@ -5020,7 +5020,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 102f0357e1..756d19fd53 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -5084,7 +5084,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -5217,7 +5217,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 85a62cffce..df5d64cb8d 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -5503,7 +5503,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -5636,7 +5636,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 64f94cb1e6..345b787f79 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -5033,7 +5033,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -5170,7 +5170,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 102f0357e1..756d19fd53 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -5084,7 +5084,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -5217,7 +5217,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 85a62cffce..df5d64cb8d 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -5503,7 +5503,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -5636,7 +5636,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 64f94cb1e6..345b787f79 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -5033,7 +5033,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" @@ -5170,7 +5170,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "transactions.read", + "scope": "rows.read", "platforms": [ "server", "client" From 0877aa2964f88f0560cb023e3c295842fed32a10 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 21:04:50 +1300 Subject: [PATCH 261/274] Generate SDKs --- app/config/platforms.php | 34 +++++++++---------- .../java/databases/create-document.md | 1 + .../java/databases/create-operations.md | 33 ++++++++++++++++++ .../java/databases/create-transaction.md | 22 ++++++++++++ .../databases/decrement-document-attribute.md | 1 + .../java/databases/delete-document.md | 1 + .../java/databases/delete-transaction.md | 22 ++++++++++++ .../java/databases/get-document.md | 1 + .../java/databases/get-transaction.md | 22 ++++++++++++ .../databases/increment-document-attribute.md | 1 + .../java/databases/list-documents.md | 1 + .../java/databases/list-transactions.md | 22 ++++++++++++ .../java/databases/update-document.md | 1 + .../java/databases/update-transaction.md | 24 +++++++++++++ .../java/databases/upsert-document.md | 1 + .../java/tablesdb/create-operations.md | 33 ++++++++++++++++++ .../java/tablesdb/create-row.md | 1 + .../java/tablesdb/create-transaction.md | 22 ++++++++++++ .../java/tablesdb/decrement-row-column.md | 1 + .../java/tablesdb/delete-row.md | 1 + .../java/tablesdb/delete-transaction.md | 22 ++++++++++++ .../client-android/java/tablesdb/get-row.md | 1 + .../java/tablesdb/get-transaction.md | 22 ++++++++++++ .../java/tablesdb/increment-row-column.md | 1 + .../client-android/java/tablesdb/list-rows.md | 1 + .../java/tablesdb/list-transactions.md | 22 ++++++++++++ .../java/tablesdb/update-row.md | 1 + .../java/tablesdb/update-transaction.md | 24 +++++++++++++ .../java/tablesdb/upsert-row.md | 1 + .../kotlin/databases/create-document.md | 1 + .../kotlin/databases/create-operations.md | 24 +++++++++++++ .../kotlin/databases/create-transaction.md | 13 +++++++ .../databases/decrement-document-attribute.md | 1 + .../kotlin/databases/delete-document.md | 1 + .../kotlin/databases/delete-transaction.md | 13 +++++++ .../kotlin/databases/get-document.md | 1 + .../kotlin/databases/get-transaction.md | 13 +++++++ .../databases/increment-document-attribute.md | 1 + .../kotlin/databases/list-documents.md | 1 + .../kotlin/databases/list-transactions.md | 13 +++++++ .../kotlin/databases/update-document.md | 1 + .../kotlin/databases/update-transaction.md | 15 ++++++++ .../kotlin/databases/upsert-document.md | 1 + .../kotlin/tablesdb/create-operations.md | 24 +++++++++++++ .../kotlin/tablesdb/create-row.md | 1 + .../kotlin/tablesdb/create-transaction.md | 13 +++++++ .../kotlin/tablesdb/decrement-row-column.md | 1 + .../kotlin/tablesdb/delete-row.md | 1 + .../kotlin/tablesdb/delete-transaction.md | 13 +++++++ .../client-android/kotlin/tablesdb/get-row.md | 1 + .../kotlin/tablesdb/get-transaction.md | 13 +++++++ .../kotlin/tablesdb/increment-row-column.md | 1 + .../kotlin/tablesdb/list-rows.md | 1 + .../kotlin/tablesdb/list-transactions.md | 13 +++++++ .../kotlin/tablesdb/update-row.md | 1 + .../kotlin/tablesdb/update-transaction.md | 15 ++++++++ .../kotlin/tablesdb/upsert-row.md | 1 + .../examples/databases/create-document.md | 3 +- .../examples/databases/create-operations.md | 23 +++++++++++++ .../examples/databases/create-transaction.md | 12 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-transaction.md | 12 +++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 12 +++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 12 +++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-transaction.md | 14 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/tablesdb/create-operations.md | 23 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-transaction.md | 12 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-transaction.md | 12 +++++++ .../client-apple/examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 12 +++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 12 +++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-transaction.md | 14 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/databases/create-document.md | 1 + .../examples/databases/create-operations.md | 22 ++++++++++++ .../examples/databases/create-transaction.md | 11 ++++++ .../databases/decrement-document-attribute.md | 1 + .../examples/databases/delete-document.md | 1 + .../examples/databases/delete-transaction.md | 11 ++++++ .../examples/databases/get-document.md | 1 + .../examples/databases/get-transaction.md | 11 ++++++ .../databases/increment-document-attribute.md | 1 + .../examples/databases/list-documents.md | 1 + .../examples/databases/list-transactions.md | 11 ++++++ .../examples/databases/update-document.md | 1 + .../examples/databases/update-transaction.md | 13 +++++++ .../examples/databases/upsert-document.md | 1 + .../examples/tablesdb/create-operations.md | 22 ++++++++++++ .../examples/tablesdb/create-row.md | 1 + .../examples/tablesdb/create-transaction.md | 11 ++++++ .../examples/tablesdb/decrement-row-column.md | 1 + .../examples/tablesdb/delete-row.md | 1 + .../examples/tablesdb/delete-transaction.md | 11 ++++++ .../examples/tablesdb/get-row.md | 1 + .../examples/tablesdb/get-transaction.md | 11 ++++++ .../examples/tablesdb/increment-row-column.md | 1 + .../examples/tablesdb/list-rows.md | 1 + .../examples/tablesdb/list-transactions.md | 11 ++++++ .../examples/tablesdb/update-row.md | 1 + .../examples/tablesdb/update-transaction.md | 13 +++++++ .../examples/tablesdb/upsert-row.md | 1 + .../examples/databases/create-document.md | 3 +- .../examples/databases/create-operations.md | 23 +++++++++++++ .../examples/databases/create-transaction.md | 12 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-transaction.md | 7 ++++ .../examples/databases/get-transaction.md | 0 .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-transactions.md | 0 .../examples/databases/update-document.md | 3 +- .../examples/databases/update-transaction.md | 14 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/tablesdb/create-operations.md | 23 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-transaction.md | 12 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-transaction.md | 7 ++++ .../examples/tablesdb/get-transaction.md | 0 .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-transactions.md | 0 .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-transaction.md | 14 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-operations.md | 24 +++++++++++++ .../examples/databases/create-transaction.md | 13 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-transaction.md | 13 +++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 13 +++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 13 +++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-transaction.md | 15 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/tablesdb/create-operations.md | 24 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-transaction.md | 13 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-transaction.md | 13 +++++++ .../examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 13 +++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 13 +++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-transaction.md | 15 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-operations.md | 21 ++++++++++++ .../examples/databases/create-transaction.md | 11 ++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 ++ .../examples/databases/delete-transaction.md | 8 +++++ .../examples/databases/get-transaction.md | 6 ++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-transactions.md | 6 ++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-transaction.md | 12 +++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/tablesdb/create-operations.md | 21 ++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-transaction.md | 11 ++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 ++ .../examples/tablesdb/delete-transaction.md | 8 +++++ .../examples/tablesdb/get-transaction.md | 6 ++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-transactions.md | 6 ++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-transaction.md | 12 +++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/databases/create-operations.md | 2 ++ .../examples/databases/create-transaction.md | 1 + .../examples/databases/delete-transaction.md | 2 ++ .../examples/databases/get-transaction.md | 2 ++ .../examples/databases/list-transactions.md | 1 + .../examples/databases/update-transaction.md | 2 ++ .../migrations/create-csv-migration.md | 2 +- .../examples/tablesdb/create-operations.md | 2 ++ .../examples/tablesdb/create-transaction.md | 1 + .../examples/tablesdb/delete-transaction.md | 2 ++ .../examples/tablesdb/get-transaction.md | 2 ++ .../examples/tablesdb/list-transactions.md | 1 + .../examples/tablesdb/update-transaction.md | 2 ++ .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 24 +++++++++++++ .../examples/databases/create-transaction.md | 13 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 13 +++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 13 +++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 13 +++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 15 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../migrations/create-csv-migration.md | 2 +- .../examples/tablesdb/create-operations.md | 24 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 13 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 13 +++++++ .../console-web/examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 13 +++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 13 +++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 15 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 1 + .../examples/databases/create-documents.md | 1 + .../examples/databases/create-operations.md | 23 +++++++++++++ .../examples/databases/create-transaction.md | 12 +++++++ .../databases/decrement-document-attribute.md | 1 + .../examples/databases/delete-document.md | 1 + .../examples/databases/delete-documents.md | 1 + .../examples/databases/delete-transaction.md | 12 +++++++ .../examples/databases/get-document.md | 1 + .../examples/databases/get-transaction.md | 12 +++++++ .../databases/increment-document-attribute.md | 1 + .../examples/databases/list-documents.md | 1 + .../examples/databases/list-transactions.md | 12 +++++++ .../examples/databases/update-document.md | 1 + .../examples/databases/update-documents.md | 1 + .../examples/databases/update-transaction.md | 14 ++++++++ .../examples/databases/upsert-document.md | 1 + .../examples/databases/upsert-documents.md | 1 + .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 23 +++++++++++++ .../examples/tablesdb/create-row.md | 1 + .../examples/tablesdb/create-rows.md | 1 + .../examples/tablesdb/create-transaction.md | 12 +++++++ .../examples/tablesdb/decrement-row-column.md | 1 + .../examples/tablesdb/delete-row.md | 1 + .../examples/tablesdb/delete-rows.md | 1 + .../examples/tablesdb/delete-transaction.md | 12 +++++++ .../server-dart/examples/tablesdb/get-row.md | 1 + .../examples/tablesdb/get-transaction.md | 12 +++++++ .../examples/tablesdb/increment-row-column.md | 1 + .../examples/tablesdb/list-rows.md | 1 + .../examples/tablesdb/list-transactions.md | 12 +++++++ .../examples/tablesdb/update-row.md | 1 + .../examples/tablesdb/update-rows.md | 1 + .../examples/tablesdb/update-transaction.md | 14 ++++++++ .../examples/tablesdb/upsert-row.md | 1 + .../examples/tablesdb/upsert-rows.md | 1 + .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 25 ++++++++++++++ .../examples/databases/create-transaction.md | 14 ++++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 14 ++++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 14 ++++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 14 ++++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 16 +++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 25 ++++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 14 ++++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 14 ++++++++ .../examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 14 ++++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 14 ++++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 16 +++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 1 + .../examples/databases/create-documents.md | 1 + .../examples/databases/create-operations.md | 30 ++++++++++++++++ .../examples/databases/create-transaction.md | 19 +++++++++++ .../databases/decrement-document-attribute.md | 1 + .../examples/databases/delete-document.md | 1 + .../examples/databases/delete-documents.md | 1 + .../examples/databases/delete-transaction.md | 19 +++++++++++ .../examples/databases/get-document.md | 1 + .../examples/databases/get-transaction.md | 19 +++++++++++ .../databases/increment-document-attribute.md | 1 + .../examples/databases/list-documents.md | 1 + .../examples/databases/list-transactions.md | 19 +++++++++++ .../examples/databases/update-document.md | 1 + .../examples/databases/update-documents.md | 1 + .../examples/databases/update-transaction.md | 21 ++++++++++++ .../examples/databases/upsert-document.md | 1 + .../examples/databases/upsert-documents.md | 1 + .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 30 ++++++++++++++++ .../server-go/examples/tablesdb/create-row.md | 1 + .../examples/tablesdb/create-rows.md | 1 + .../examples/tablesdb/create-transaction.md | 19 +++++++++++ .../examples/tablesdb/decrement-row-column.md | 1 + .../server-go/examples/tablesdb/delete-row.md | 1 + .../examples/tablesdb/delete-rows.md | 1 + .../examples/tablesdb/delete-transaction.md | 19 +++++++++++ .../server-go/examples/tablesdb/get-row.md | 1 + .../examples/tablesdb/get-transaction.md | 19 +++++++++++ .../examples/tablesdb/increment-row-column.md | 1 + .../server-go/examples/tablesdb/list-rows.md | 1 + .../examples/tablesdb/list-transactions.md | 19 +++++++++++ .../server-go/examples/tablesdb/update-row.md | 1 + .../examples/tablesdb/update-rows.md | 1 + .../examples/tablesdb/update-transaction.md | 21 ++++++++++++ .../server-go/examples/tablesdb/upsert-row.md | 1 + .../examples/tablesdb/upsert-rows.md | 1 + .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 23 +++++++++++++ .../examples/databases/create-transaction.md | 12 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 7 ++++ .../examples/databases/get-transaction.md | 0 .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-transactions.md | 0 .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 14 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 23 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 12 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 7 ++++ .../examples/tablesdb/get-transaction.md | 0 .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-transactions.md | 0 .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 14 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../java/databases/create-document.md | 1 + .../java/databases/create-documents.md | 1 + .../java/databases/create-operations.md | 34 +++++++++++++++++++ .../java/databases/create-transaction.md | 23 +++++++++++++ .../databases/decrement-document-attribute.md | 1 + .../java/databases/delete-document.md | 1 + .../java/databases/delete-documents.md | 1 + .../java/databases/delete-transaction.md | 23 +++++++++++++ .../java/databases/get-document.md | 1 + .../java/databases/get-transaction.md | 23 +++++++++++++ .../databases/increment-document-attribute.md | 1 + .../java/databases/list-documents.md | 1 + .../java/databases/list-transactions.md | 23 +++++++++++++ .../java/databases/update-document.md | 1 + .../java/databases/update-documents.md | 1 + .../java/databases/update-transaction.md | 25 ++++++++++++++ .../java/databases/upsert-document.md | 1 + .../java/databases/upsert-documents.md | 1 + .../java/messaging/create-push.md | 2 +- .../java/messaging/update-push.md | 2 +- .../java/tablesdb/create-operations.md | 34 +++++++++++++++++++ .../server-kotlin/java/tablesdb/create-row.md | 1 + .../java/tablesdb/create-rows.md | 1 + .../java/tablesdb/create-transaction.md | 23 +++++++++++++ .../java/tablesdb/decrement-row-column.md | 1 + .../server-kotlin/java/tablesdb/delete-row.md | 1 + .../java/tablesdb/delete-rows.md | 1 + .../java/tablesdb/delete-transaction.md | 23 +++++++++++++ .../server-kotlin/java/tablesdb/get-row.md | 1 + .../java/tablesdb/get-transaction.md | 23 +++++++++++++ .../java/tablesdb/increment-row-column.md | 1 + .../server-kotlin/java/tablesdb/list-rows.md | 1 + .../java/tablesdb/list-transactions.md | 23 +++++++++++++ .../server-kotlin/java/tablesdb/update-row.md | 1 + .../java/tablesdb/update-rows.md | 1 + .../java/tablesdb/update-transaction.md | 25 ++++++++++++++ .../server-kotlin/java/tablesdb/upsert-row.md | 1 + .../java/tablesdb/upsert-rows.md | 1 + .../kotlin/databases/create-document.md | 3 +- .../kotlin/databases/create-documents.md | 3 +- .../kotlin/databases/create-operations.md | 25 ++++++++++++++ .../kotlin/databases/create-transaction.md | 14 ++++++++ .../databases/decrement-document-attribute.md | 3 +- .../kotlin/databases/delete-document.md | 3 +- .../kotlin/databases/delete-documents.md | 3 +- .../kotlin/databases/delete-transaction.md | 14 ++++++++ .../kotlin/databases/get-document.md | 3 +- .../kotlin/databases/get-transaction.md | 14 ++++++++ .../databases/increment-document-attribute.md | 3 +- .../kotlin/databases/list-documents.md | 3 +- .../kotlin/databases/list-transactions.md | 14 ++++++++ .../kotlin/databases/update-document.md | 3 +- .../kotlin/databases/update-documents.md | 3 +- .../kotlin/databases/update-transaction.md | 16 +++++++++ .../kotlin/databases/upsert-document.md | 3 +- .../kotlin/databases/upsert-documents.md | 3 +- .../kotlin/messaging/create-push.md | 2 +- .../kotlin/messaging/update-push.md | 2 +- .../kotlin/tablesdb/create-operations.md | 25 ++++++++++++++ .../kotlin/tablesdb/create-row.md | 3 +- .../kotlin/tablesdb/create-rows.md | 3 +- .../kotlin/tablesdb/create-transaction.md | 14 ++++++++ .../kotlin/tablesdb/decrement-row-column.md | 3 +- .../kotlin/tablesdb/delete-row.md | 3 +- .../kotlin/tablesdb/delete-rows.md | 3 +- .../kotlin/tablesdb/delete-transaction.md | 14 ++++++++ .../server-kotlin/kotlin/tablesdb/get-row.md | 3 +- .../kotlin/tablesdb/get-transaction.md | 14 ++++++++ .../kotlin/tablesdb/increment-row-column.md | 3 +- .../kotlin/tablesdb/list-rows.md | 3 +- .../kotlin/tablesdb/list-transactions.md | 14 ++++++++ .../kotlin/tablesdb/update-row.md | 3 +- .../kotlin/tablesdb/update-rows.md | 3 +- .../kotlin/tablesdb/update-transaction.md | 16 +++++++++ .../kotlin/tablesdb/upsert-row.md | 3 +- .../kotlin/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 26 ++++++++++++++ .../examples/databases/create-transaction.md | 15 ++++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 15 ++++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 15 ++++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 15 ++++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 17 ++++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 26 ++++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 15 ++++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 15 ++++++++ .../server-php/examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 15 ++++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../server-php/examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 15 ++++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 17 ++++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 24 +++++++++++++ .../examples/databases/create-transaction.md | 13 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 13 +++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 13 +++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 13 +++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 15 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 24 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 13 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 13 +++++++ .../examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 13 +++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 13 +++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 15 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 22 ++++++++++++ .../examples/databases/create-transaction.md | 12 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 ++ .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 9 +++++ .../examples/databases/get-transaction.md | 7 ++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-transactions.md | 7 ++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 13 +++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 22 ++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 12 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 ++ .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 9 +++++ .../examples/tablesdb/get-transaction.md | 7 ++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-transactions.md | 7 ++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 13 +++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 25 ++++++++++++++ .../examples/databases/create-transaction.md | 14 ++++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 14 ++++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 14 ++++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 14 ++++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 16 +++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 25 ++++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 14 ++++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 14 ++++++++ .../server-ruby/examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 14 ++++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 14 ++++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 16 +++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- .../examples/databases/create-document.md | 3 +- .../examples/databases/create-documents.md | 3 +- .../examples/databases/create-operations.md | 24 +++++++++++++ .../examples/databases/create-transaction.md | 13 +++++++ .../databases/decrement-document-attribute.md | 3 +- .../examples/databases/delete-document.md | 3 +- .../examples/databases/delete-documents.md | 3 +- .../examples/databases/delete-transaction.md | 13 +++++++ .../examples/databases/get-document.md | 3 +- .../examples/databases/get-transaction.md | 13 +++++++ .../databases/increment-document-attribute.md | 3 +- .../examples/databases/list-documents.md | 3 +- .../examples/databases/list-transactions.md | 13 +++++++ .../examples/databases/update-document.md | 3 +- .../examples/databases/update-documents.md | 3 +- .../examples/databases/update-transaction.md | 15 ++++++++ .../examples/databases/upsert-document.md | 3 +- .../examples/databases/upsert-documents.md | 3 +- .../examples/messaging/create-push.md | 2 +- .../examples/messaging/update-push.md | 2 +- .../examples/tablesdb/create-operations.md | 24 +++++++++++++ .../examples/tablesdb/create-row.md | 3 +- .../examples/tablesdb/create-rows.md | 3 +- .../examples/tablesdb/create-transaction.md | 13 +++++++ .../examples/tablesdb/decrement-row-column.md | 3 +- .../examples/tablesdb/delete-row.md | 3 +- .../examples/tablesdb/delete-rows.md | 3 +- .../examples/tablesdb/delete-transaction.md | 13 +++++++ .../server-swift/examples/tablesdb/get-row.md | 3 +- .../examples/tablesdb/get-transaction.md | 13 +++++++ .../examples/tablesdb/increment-row-column.md | 3 +- .../examples/tablesdb/list-rows.md | 3 +- .../examples/tablesdb/list-transactions.md | 13 +++++++ .../examples/tablesdb/update-row.md | 3 +- .../examples/tablesdb/update-rows.md | 3 +- .../examples/tablesdb/update-transaction.md | 15 ++++++++ .../examples/tablesdb/upsert-row.md | 3 +- .../examples/tablesdb/upsert-rows.md | 3 +- docs/sdks/android/CHANGELOG.md | 4 +++ docs/sdks/apple/CHANGELOG.md | 4 +++ docs/sdks/cli/CHANGELOG.md | 4 +++ docs/sdks/dart/CHANGELOG.md | 4 +++ docs/sdks/dotnet/CHANGELOG.md | 4 +++ docs/sdks/flutter/CHANGELOG.md | 4 +++ docs/sdks/go/CHANGELOG.md | 4 +++ docs/sdks/kotlin/CHANGELOG.md | 4 +++ docs/sdks/nodejs/CHANGELOG.md | 4 +++ docs/sdks/php/CHANGELOG.md | 4 +++ docs/sdks/python/CHANGELOG.md | 4 +++ docs/sdks/react-native/CHANGELOG.md | 4 +++ docs/sdks/ruby/CHANGELOG.md | 4 +++ docs/sdks/swift/CHANGELOG.md | 4 +++ docs/sdks/web/CHANGELOG.md | 4 +++ 666 files changed, 4309 insertions(+), 303 deletions(-) create mode 100644 docs/examples/1.8.x/client-android/java/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-android/java/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-android/java/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-android/java/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-android/java/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-android/kotlin/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-apple/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-apple/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-apple/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-apple/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-apple/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-flutter/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-graphql/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-react-native/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/client-rest/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/client-rest/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/client-rest/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/client-rest/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/client-rest/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/console-cli/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/console-cli/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/console-cli/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/console-cli/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/console-cli/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/console-web/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/console-web/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/console-web/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/console-web/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/console-web/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-dart/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-dart/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-dart/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-dart/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-dart/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-go/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-go/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-go/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-go/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-go/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-graphql/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-kotlin/java/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-php/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-php/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-php/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-php/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-php/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-python/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-python/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-python/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-python/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-python/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-rest/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-rest/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-rest/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-rest/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-rest/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-ruby/examples/tablesdb/update-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/databases/create-operations.md create mode 100644 docs/examples/1.8.x/server-swift/examples/databases/create-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/databases/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/databases/get-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/databases/list-transactions.md create mode 100644 docs/examples/1.8.x/server-swift/examples/databases/update-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/tablesdb/create-operations.md create mode 100644 docs/examples/1.8.x/server-swift/examples/tablesdb/create-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/tablesdb/delete-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/tablesdb/get-transaction.md create mode 100644 docs/examples/1.8.x/server-swift/examples/tablesdb/list-transactions.md create mode 100644 docs/examples/1.8.x/server-swift/examples/tablesdb/update-transaction.md diff --git a/app/config/platforms.php b/app/config/platforms.php index 9ea8fc4df4..0f32c9f45c 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '21.1.0', + 'version' => '21.2.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -24,7 +24,7 @@ return [ 'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git', 'gitRepoName' => 'sdk-for-web', 'gitUserName' => 'appwrite', - 'gitBranch' => 'feat-txn', + 'gitBranch' => 'dev', 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'), 'demos' => [ [ @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '20.1.0', + 'version' => '20.2.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '13.1.0', + 'version' => '13.2.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '11.1.0', + 'version' => '11.2.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -139,7 +139,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.16.0', + 'version' => '0.17.0', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '10.1.0', + 'version' => '10.2.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '20.1.0', + 'version' => '20.2.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -275,13 +275,13 @@ return [ 'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git', 'gitRepoName' => 'sdk-for-node', 'gitUserName' => 'appwrite', - 'gitBranch' => 'feat-txn', + 'gitBranch' => 'dev', 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'), ], [ 'key' => 'php', 'name' => 'PHP', - 'version' => '17.3.0', + 'version' => '17.4.0', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -300,7 +300,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '13.3.0', + 'version' => '13.4.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '19.1.0', + 'version' => '19.2.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => 'v0.12.0', + 'version' => 'v0.13.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, @@ -357,7 +357,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.20.0', + 'version' => '0.21.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '19.1.0', + 'version' => '19.2.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '12.1.0', + 'version' => '12.2.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '13.1.0', + 'version' => '13.2.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/docs/examples/1.8.x/client-android/java/databases/create-document.md b/docs/examples/1.8.x/client-android/java/databases/create-document.md index bd0b57ac4c..694d99a089 100644 --- a/docs/examples/1.8.x/client-android/java/databases/create-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/create-document.md @@ -20,6 +20,7 @@ databases.createDocument( "isAdmin" to false ), // data listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/create-operations.md b/docs/examples/1.8.x/client-android/java/databases/create-operations.md new file mode 100644 index 0000000000..def58af773 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/databases/create-operations.md @@ -0,0 +1,33 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Databases databases = new Databases(client); + +databases.createOperations( + "", // transactionId + listOf( + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ), // operations (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/databases/create-transaction.md b/docs/examples/1.8.x/client-android/java/databases/create-transaction.md new file mode 100644 index 0000000000..871d1907f6 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/databases/create-transaction.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Databases databases = new Databases(client); + +databases.createTransaction( + 60, // ttl (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-android/java/databases/decrement-document-attribute.md index de6a4ab48d..8669494532 100644 --- a/docs/examples/1.8.x/client-android/java/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-android/java/databases/decrement-document-attribute.md @@ -15,6 +15,7 @@ databases.decrementDocumentAttribute( "", // attribute 0, // value (optional) 0, // min (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/delete-document.md b/docs/examples/1.8.x/client-android/java/databases/delete-document.md index 5288e53bed..2795670807 100644 --- a/docs/examples/1.8.x/client-android/java/databases/delete-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/delete-document.md @@ -12,6 +12,7 @@ databases.deleteDocument( "", // databaseId "", // collectionId "", // documentId + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/delete-transaction.md b/docs/examples/1.8.x/client-android/java/databases/delete-transaction.md new file mode 100644 index 0000000000..ac1121e9b9 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/databases/delete-transaction.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Databases databases = new Databases(client); + +databases.deleteTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/databases/get-document.md b/docs/examples/1.8.x/client-android/java/databases/get-document.md index e7ae2079ed..92d6b5ed23 100644 --- a/docs/examples/1.8.x/client-android/java/databases/get-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/get-document.md @@ -13,6 +13,7 @@ databases.getDocument( "", // collectionId "", // documentId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/get-transaction.md b/docs/examples/1.8.x/client-android/java/databases/get-transaction.md new file mode 100644 index 0000000000..5dee850305 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/databases/get-transaction.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Databases databases = new Databases(client); + +databases.getTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-android/java/databases/increment-document-attribute.md index 94ffa9d749..5928b455c6 100644 --- a/docs/examples/1.8.x/client-android/java/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-android/java/databases/increment-document-attribute.md @@ -15,6 +15,7 @@ databases.incrementDocumentAttribute( "", // attribute 0, // value (optional) 0, // max (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/list-documents.md b/docs/examples/1.8.x/client-android/java/databases/list-documents.md index 606d67705d..7b2ba23453 100644 --- a/docs/examples/1.8.x/client-android/java/databases/list-documents.md +++ b/docs/examples/1.8.x/client-android/java/databases/list-documents.md @@ -12,6 +12,7 @@ databases.listDocuments( "", // databaseId "", // collectionId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/list-transactions.md b/docs/examples/1.8.x/client-android/java/databases/list-transactions.md new file mode 100644 index 0000000000..39f58f3490 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/databases/list-transactions.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Databases databases = new Databases(client); + +databases.listTransactions( + listOf(), // queries (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/databases/update-document.md b/docs/examples/1.8.x/client-android/java/databases/update-document.md index baa827cdf5..a6103e44d1 100644 --- a/docs/examples/1.8.x/client-android/java/databases/update-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/update-document.md @@ -14,6 +14,7 @@ databases.updateDocument( "", // documentId mapOf( "a" to "b" ), // data (optional) listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/update-transaction.md b/docs/examples/1.8.x/client-android/java/databases/update-transaction.md new file mode 100644 index 0000000000..c5f586fce3 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/databases/update-transaction.md @@ -0,0 +1,24 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +Databases databases = new Databases(client); + +databases.updateTransaction( + "", // transactionId + false, // commit (optional) + false, // rollback (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/databases/upsert-document.md b/docs/examples/1.8.x/client-android/java/databases/upsert-document.md index 868576b982..52be46ebe6 100644 --- a/docs/examples/1.8.x/client-android/java/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/upsert-document.md @@ -14,6 +14,7 @@ databases.upsertDocument( "", // documentId mapOf( "a" to "b" ), // data listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md b/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md new file mode 100644 index 0000000000..7ca288c2ae --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md @@ -0,0 +1,33 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.createOperations( + "", // transactionId + listOf( + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ), // operations (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md index 8273573ddd..92a9058401 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md @@ -20,6 +20,7 @@ tablesDB.createRow( "isAdmin" to false ), // data listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-android/java/tablesdb/create-transaction.md new file mode 100644 index 0000000000..c232c56991 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/tablesdb/create-transaction.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.createTransaction( + 60, // ttl (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-android/java/tablesdb/decrement-row-column.md index 73f2128a8f..2252564e73 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/decrement-row-column.md @@ -15,6 +15,7 @@ tablesDB.decrementRowColumn( "", // column 0, // value (optional) 0, // min (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/delete-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/delete-row.md index d4c351bc32..6680100499 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/delete-row.md @@ -12,6 +12,7 @@ tablesDB.deleteRow( "", // databaseId "", // tableId "", // rowId + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-android/java/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..18cb2357d3 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/tablesdb/delete-transaction.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.deleteTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md index 191fc896e9..45efc6b061 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md @@ -13,6 +13,7 @@ tablesDB.getRow( "", // tableId "", // rowId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-android/java/tablesdb/get-transaction.md new file mode 100644 index 0000000000..fb51916264 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/tablesdb/get-transaction.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.getTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-android/java/tablesdb/increment-row-column.md index dc2120ea07..a1194a67a5 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/increment-row-column.md @@ -15,6 +15,7 @@ tablesDB.incrementRowColumn( "", // column 0, // value (optional) 0, // max (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md b/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md index dc50574f1e..21f0005b2d 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md @@ -12,6 +12,7 @@ tablesDB.listRows( "", // databaseId "", // tableId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md new file mode 100644 index 0000000000..c37ccf1931 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md @@ -0,0 +1,22 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.listTransactions( + listOf(), // queries (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md index 0f1fabc91e..cea7baaa16 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md @@ -14,6 +14,7 @@ tablesDB.updateRow( "", // rowId mapOf( "a" to "b" ), // data (optional) listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-android/java/tablesdb/update-transaction.md new file mode 100644 index 0000000000..804b5d5023 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/tablesdb/update-transaction.md @@ -0,0 +1,24 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject(""); // Your project ID + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.updateTransaction( + "", // transactionId + false, // commit (optional) + false, // rollback (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md index 22c0e94b44..9d6323fffa 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md @@ -14,6 +14,7 @@ tablesDB.upsertRow( "", // rowId mapOf( "a" to "b" ), // data (optional) listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/create-document.md b/docs/examples/1.8.x/client-android/kotlin/databases/create-document.md index 7f0aaf81f4..7d4b04d13a 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/create-document.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/create-document.md @@ -20,4 +20,5 @@ val result = databases.createDocument( "isAdmin" to false ), permissions = listOf("read("any")"), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md b/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md new file mode 100644 index 0000000000..62c93351e7 --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md @@ -0,0 +1,24 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val databases = Databases(client) + +val result = databases.createOperations( + transactionId = "", + operations = listOf( + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ), // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/create-transaction.md b/docs/examples/1.8.x/client-android/kotlin/databases/create-transaction.md new file mode 100644 index 0000000000..3b953c3bda --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/databases/create-transaction.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val databases = Databases(client) + +val result = databases.createTransaction( + ttl = 60, // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-android/kotlin/databases/decrement-document-attribute.md index c500fa8687..84a4a0edea 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/decrement-document-attribute.md @@ -15,4 +15,5 @@ val result = databases.decrementDocumentAttribute( attribute = "", value = 0, // (optional) min = 0, // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/delete-document.md b/docs/examples/1.8.x/client-android/kotlin/databases/delete-document.md index 052bf97f89..242655ec1f 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/delete-document.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/delete-document.md @@ -12,4 +12,5 @@ val result = databases.deleteDocument( databaseId = "", collectionId = "", documentId = "", + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/delete-transaction.md b/docs/examples/1.8.x/client-android/kotlin/databases/delete-transaction.md new file mode 100644 index 0000000000..bbce98c661 --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/databases/delete-transaction.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val databases = Databases(client) + +val result = databases.deleteTransaction( + transactionId = "", +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/get-document.md b/docs/examples/1.8.x/client-android/kotlin/databases/get-document.md index 157af2b562..f21b6f34f0 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/get-document.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/get-document.md @@ -13,4 +13,5 @@ val result = databases.getDocument( collectionId = "", documentId = "", queries = listOf(), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/get-transaction.md b/docs/examples/1.8.x/client-android/kotlin/databases/get-transaction.md new file mode 100644 index 0000000000..0a1fda69df --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/databases/get-transaction.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val databases = Databases(client) + +val result = databases.getTransaction( + transactionId = "", +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-android/kotlin/databases/increment-document-attribute.md index 0ae6b02d3d..ca32cfcb5c 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/increment-document-attribute.md @@ -15,4 +15,5 @@ val result = databases.incrementDocumentAttribute( attribute = "", value = 0, // (optional) max = 0, // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/list-documents.md b/docs/examples/1.8.x/client-android/kotlin/databases/list-documents.md index 1cc785fab1..a2b6e0e0b6 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/list-documents.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/list-documents.md @@ -12,4 +12,5 @@ val result = databases.listDocuments( databaseId = "", collectionId = "", queries = listOf(), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/list-transactions.md b/docs/examples/1.8.x/client-android/kotlin/databases/list-transactions.md new file mode 100644 index 0000000000..da5cd4ba7a --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/databases/list-transactions.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val databases = Databases(client) + +val result = databases.listTransactions( + queries = listOf(), // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/update-document.md b/docs/examples/1.8.x/client-android/kotlin/databases/update-document.md index d61fdea5b1..2cacce6f95 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/update-document.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/update-document.md @@ -14,4 +14,5 @@ val result = databases.updateDocument( documentId = "", data = mapOf( "a" to "b" ), // (optional) permissions = listOf("read("any")"), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/update-transaction.md b/docs/examples/1.8.x/client-android/kotlin/databases/update-transaction.md new file mode 100644 index 0000000000..c9d6a852ce --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/databases/update-transaction.md @@ -0,0 +1,15 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val databases = Databases(client) + +val result = databases.updateTransaction( + transactionId = "", + commit = false, // (optional) + rollback = false, // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/upsert-document.md b/docs/examples/1.8.x/client-android/kotlin/databases/upsert-document.md index a31dfc8797..e5ac4375e8 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/upsert-document.md @@ -14,4 +14,5 @@ val result = databases.upsertDocument( documentId = "", data = mapOf( "a" to "b" ), permissions = listOf("read("any")"), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md new file mode 100644 index 0000000000..3073a00bca --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md @@ -0,0 +1,24 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val tablesDB = TablesDB(client) + +val result = tablesDB.createOperations( + transactionId = "", + operations = listOf( + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ), // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-row.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-row.md index eb44cc48d6..f5850b23be 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-row.md @@ -20,4 +20,5 @@ val result = tablesDB.createRow( "isAdmin" to false ), permissions = listOf("read("any")"), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-transaction.md new file mode 100644 index 0000000000..b011fa051f --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-transaction.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val tablesDB = TablesDB(client) + +val result = tablesDB.createTransaction( + ttl = 60, // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/decrement-row-column.md index 645e9f4a66..1a8964078e 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/decrement-row-column.md @@ -15,4 +15,5 @@ val result = tablesDB.decrementRowColumn( column = "", value = 0, // (optional) min = 0, // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-row.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-row.md index 3fcd409295..6912afa10a 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-row.md @@ -12,4 +12,5 @@ val result = tablesDB.deleteRow( databaseId = "", tableId = "", rowId = "", + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..92c0074acc --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/delete-transaction.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val tablesDB = TablesDB(client) + +val result = tablesDB.deleteTransaction( + transactionId = "", +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-row.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-row.md index b754cba915..adea429759 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-row.md @@ -13,4 +13,5 @@ val result = tablesDB.getRow( tableId = "", rowId = "", queries = listOf(), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-transaction.md new file mode 100644 index 0000000000..38b7c33e83 --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/get-transaction.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val tablesDB = TablesDB(client) + +val result = tablesDB.getTransaction( + transactionId = "", +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/increment-row-column.md index 230408abac..1b679e181b 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/increment-row-column.md @@ -15,4 +15,5 @@ val result = tablesDB.incrementRowColumn( column = "", value = 0, // (optional) max = 0, // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-rows.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-rows.md index 05d9ca33b3..6d2a4b8b40 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-rows.md @@ -12,4 +12,5 @@ val result = tablesDB.listRows( databaseId = "", tableId = "", queries = listOf(), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-transactions.md new file mode 100644 index 0000000000..b7764c719f --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/list-transactions.md @@ -0,0 +1,13 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val tablesDB = TablesDB(client) + +val result = tablesDB.listTransactions( + queries = listOf(), // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-row.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-row.md index f99f8f275e..a17f231418 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-row.md @@ -14,4 +14,5 @@ val result = tablesDB.updateRow( rowId = "", data = mapOf( "a" to "b" ), // (optional) permissions = listOf("read("any")"), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-transaction.md new file mode 100644 index 0000000000..791649ff09 --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/update-transaction.md @@ -0,0 +1,15 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +val tablesDB = TablesDB(client) + +val result = tablesDB.updateTransaction( + transactionId = "", + commit = false, // (optional) + rollback = false, // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/upsert-row.md index d82406a7f8..074fa339cb 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/upsert-row.md @@ -14,4 +14,5 @@ val result = tablesDB.upsertRow( rowId = "", data = mapOf( "a" to "b" ), // (optional) permissions = listOf("read("any")"), // (optional) + transactionId = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-apple/examples/databases/create-document.md b/docs/examples/1.8.x/client-apple/examples/databases/create-document.md index c044eee17a..d7fa796ed1 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/create-document.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/create-document.md @@ -17,6 +17,7 @@ let document = try await databases.createDocument( "age": 30, "isAdmin": false ], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/create-operations.md b/docs/examples/1.8.x/client-apple/examples/databases/create-operations.md new file mode 100644 index 0000000000..7c22de38ca --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/databases/create-operations.md @@ -0,0 +1,23 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let databases = Databases(client) + +let transaction = try await databases.createOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/databases/create-transaction.md b/docs/examples/1.8.x/client-apple/examples/databases/create-transaction.md new file mode 100644 index 0000000000..4319907a65 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/databases/create-transaction.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let databases = Databases(client) + +let transaction = try await databases.createTransaction( + ttl: 60 // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-apple/examples/databases/decrement-document-attribute.md index 8ef2637bf2..714d7baabb 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/decrement-document-attribute.md @@ -12,6 +12,7 @@ let document = try await databases.decrementDocumentAttribute( documentId: "", attribute: "", value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/delete-document.md b/docs/examples/1.8.x/client-apple/examples/databases/delete-document.md index 301203dc7f..4d8f5074ea 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/delete-document.md @@ -9,6 +9,7 @@ let databases = Databases(client) let result = try await databases.deleteDocument( databaseId: "", collectionId: "", - documentId: "" + documentId: "", + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/delete-transaction.md b/docs/examples/1.8.x/client-apple/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..134d72df9c --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/databases/delete-transaction.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let databases = Databases(client) + +let result = try await databases.deleteTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/client-apple/examples/databases/get-document.md b/docs/examples/1.8.x/client-apple/examples/databases/get-document.md index 6e4dc55c6d..89c89a3868 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/get-document.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/get-document.md @@ -10,6 +10,7 @@ let document = try await databases.getDocument( databaseId: "", collectionId: "", documentId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/get-transaction.md b/docs/examples/1.8.x/client-apple/examples/databases/get-transaction.md new file mode 100644 index 0000000000..6274ff8d15 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/databases/get-transaction.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let databases = Databases(client) + +let transaction = try await databases.getTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/client-apple/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-apple/examples/databases/increment-document-attribute.md index f64b2cd76c..9c771a7490 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/increment-document-attribute.md @@ -12,6 +12,7 @@ let document = try await databases.incrementDocumentAttribute( documentId: "", attribute: "", value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/list-documents.md b/docs/examples/1.8.x/client-apple/examples/databases/list-documents.md index 0d624f3a92..528d9992a4 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/list-documents.md @@ -9,6 +9,7 @@ let databases = Databases(client) let documentList = try await databases.listDocuments( databaseId: "", collectionId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/list-transactions.md b/docs/examples/1.8.x/client-apple/examples/databases/list-transactions.md new file mode 100644 index 0000000000..3f889c213c --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/databases/list-transactions.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let databases = Databases(client) + +let transactionList = try await databases.listTransactions( + queries: [] // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/databases/update-document.md b/docs/examples/1.8.x/client-apple/examples/databases/update-document.md index af224c8e09..d626d7d3c0 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/update-document.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/update-document.md @@ -11,6 +11,7 @@ let document = try await databases.updateDocument( collectionId: "", documentId: "", data: [:], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/databases/update-transaction.md b/docs/examples/1.8.x/client-apple/examples/databases/update-transaction.md new file mode 100644 index 0000000000..96705d019f --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/databases/update-transaction.md @@ -0,0 +1,14 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let databases = Databases(client) + +let transaction = try await databases.updateTransaction( + transactionId: "", + commit: false, // optional + rollback: false // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/databases/upsert-document.md b/docs/examples/1.8.x/client-apple/examples/databases/upsert-document.md index 3e1bf83a66..8e2a4a09e9 100644 --- a/docs/examples/1.8.x/client-apple/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-apple/examples/databases/upsert-document.md @@ -11,6 +11,7 @@ let document = try await databases.upsertDocument( collectionId: "", documentId: "", data: [:], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..c4032cc02c --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/create-operations.md @@ -0,0 +1,23 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.createOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/create-row.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/create-row.md index 2ee601340f..4d66980bb5 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/create-row.md @@ -17,6 +17,7 @@ let row = try await tablesDB.createRow( "age": 30, "isAdmin": false ], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..366aa5b663 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/create-transaction.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.createTransaction( + ttl: 60 // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/decrement-row-column.md index ab8ac5b0b8..8a41d43362 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/decrement-row-column.md @@ -12,6 +12,7 @@ let row = try await tablesDB.decrementRowColumn( rowId: "", column: "", value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-row.md index b527acaed9..6ddd1c5fc2 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-row.md @@ -9,6 +9,7 @@ let tablesDB = TablesDB(client) let result = try await tablesDB.deleteRow( databaseId: "", tableId: "", - rowId: "" + rowId: "", + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..12cf45fa2c --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/delete-transaction.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let tablesDB = TablesDB(client) + +let result = try await tablesDB.deleteTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/get-row.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/get-row.md index bc2e2c6b55..7a3aa40806 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/get-row.md @@ -10,6 +10,7 @@ let row = try await tablesDB.getRow( databaseId: "", tableId: "", rowId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..fe3cbf78b2 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/get-transaction.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.getTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/increment-row-column.md index 550d6685af..29b346e27d 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/increment-row-column.md @@ -12,6 +12,7 @@ let row = try await tablesDB.incrementRowColumn( rowId: "", column: "", value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/list-rows.md index 94853c24a0..dee2ab9e81 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/list-rows.md @@ -9,6 +9,7 @@ let tablesDB = TablesDB(client) let rowList = try await tablesDB.listRows( databaseId: "", tableId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..b7edd2d1a0 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/list-transactions.md @@ -0,0 +1,12 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let tablesDB = TablesDB(client) + +let transactionList = try await tablesDB.listTransactions( + queries: [] // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/update-row.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/update-row.md index 87a8f27313..cd5591db80 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/update-row.md @@ -11,6 +11,7 @@ let row = try await tablesDB.updateRow( tableId: "", rowId: "", data: [:], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..2c0e6a7a37 --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/update-transaction.md @@ -0,0 +1,14 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.updateTransaction( + transactionId: "", + commit: false, // optional + rollback: false // optional +) + diff --git a/docs/examples/1.8.x/client-apple/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-apple/examples/tablesdb/upsert-row.md index ed95da7b62..955935adef 100644 --- a/docs/examples/1.8.x/client-apple/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-apple/examples/tablesdb/upsert-row.md @@ -11,6 +11,7 @@ let row = try await tablesDB.upsertRow( tableId: "", rowId: "", data: [:], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/create-document.md b/docs/examples/1.8.x/client-flutter/examples/databases/create-document.md index 3becbcf1fc..0acbe689dc 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/create-document.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/create-document.md @@ -18,4 +18,5 @@ Document result = await databases.createDocument( "isAdmin": false }, permissions: ["read("any")"], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/create-operations.md b/docs/examples/1.8.x/client-flutter/examples/databases/create-operations.md new file mode 100644 index 0000000000..2dec7ff7c8 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/databases/create-operations.md @@ -0,0 +1,22 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Databases databases = Databases(client); + +Transaction result = await databases.createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ], // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/create-transaction.md b/docs/examples/1.8.x/client-flutter/examples/databases/create-transaction.md new file mode 100644 index 0000000000..3d7ddc3efa --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/databases/create-transaction.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Databases databases = Databases(client); + +Transaction result = await databases.createTransaction( + ttl: 60, // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-flutter/examples/databases/decrement-document-attribute.md index ec0d9ee300..dad45bc836 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/decrement-document-attribute.md @@ -13,4 +13,5 @@ Document result = await databases.decrementDocumentAttribute( attribute: '', value: 0, // optional min: 0, // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/delete-document.md b/docs/examples/1.8.x/client-flutter/examples/databases/delete-document.md index 3354917c20..bd101370a6 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/delete-document.md @@ -10,4 +10,5 @@ await databases.deleteDocument( databaseId: '', collectionId: '', documentId: '', + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/delete-transaction.md b/docs/examples/1.8.x/client-flutter/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..333dd1d3a1 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/databases/delete-transaction.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Databases databases = Databases(client); + +await databases.deleteTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/get-document.md b/docs/examples/1.8.x/client-flutter/examples/databases/get-document.md index f85c1f9b5a..9dcf2cf119 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/get-document.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/get-document.md @@ -11,4 +11,5 @@ Document result = await databases.getDocument( collectionId: '', documentId: '', queries: [], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/get-transaction.md b/docs/examples/1.8.x/client-flutter/examples/databases/get-transaction.md new file mode 100644 index 0000000000..153b0f3856 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/databases/get-transaction.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Databases databases = Databases(client); + +Transaction result = await databases.getTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-flutter/examples/databases/increment-document-attribute.md index 78f5b0cb6f..855fd8f51e 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/increment-document-attribute.md @@ -13,4 +13,5 @@ Document result = await databases.incrementDocumentAttribute( attribute: '', value: 0, // optional max: 0, // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/list-documents.md b/docs/examples/1.8.x/client-flutter/examples/databases/list-documents.md index 31fec1f522..b53120cb4e 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/list-documents.md @@ -10,4 +10,5 @@ DocumentList result = await databases.listDocuments( databaseId: '', collectionId: '', queries: [], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/list-transactions.md b/docs/examples/1.8.x/client-flutter/examples/databases/list-transactions.md new file mode 100644 index 0000000000..467a1ced84 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/databases/list-transactions.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Databases databases = Databases(client); + +TransactionList result = await databases.listTransactions( + queries: [], // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/update-document.md b/docs/examples/1.8.x/client-flutter/examples/databases/update-document.md index 1f444d875a..44ade30c3a 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/update-document.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/update-document.md @@ -12,4 +12,5 @@ Document result = await databases.updateDocument( documentId: '', data: {}, // optional permissions: ["read("any")"], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/update-transaction.md b/docs/examples/1.8.x/client-flutter/examples/databases/update-transaction.md new file mode 100644 index 0000000000..a51f9d05d6 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/databases/update-transaction.md @@ -0,0 +1,13 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +Databases databases = Databases(client); + +Transaction result = await databases.updateTransaction( + transactionId: '', + commit: false, // optional + rollback: false, // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/databases/upsert-document.md b/docs/examples/1.8.x/client-flutter/examples/databases/upsert-document.md index 398a99cb1d..10117ac78d 100644 --- a/docs/examples/1.8.x/client-flutter/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-flutter/examples/databases/upsert-document.md @@ -12,4 +12,5 @@ Document result = await databases.upsertDocument( documentId: '', data: {}, permissions: ["read("any")"], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..631aefe603 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-operations.md @@ -0,0 +1,22 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ], // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-row.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-row.md index 038bb2bac6..345f0c239a 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-row.md @@ -18,4 +18,5 @@ Row result = await tablesDB.createRow( "isAdmin": false }, permissions: ["read("any")"], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..0ad0eb5223 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/create-transaction.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.createTransaction( + ttl: 60, // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/decrement-row-column.md index 4f3b4bdb61..65f67513f4 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/decrement-row-column.md @@ -13,4 +13,5 @@ Row result = await tablesDB.decrementRowColumn( column: '', value: 0, // optional min: 0, // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-row.md index cc902fa198..b8ea1d2656 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-row.md @@ -10,4 +10,5 @@ await tablesDB.deleteRow( databaseId: '', tableId: '', rowId: '', + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..2d27c6afda --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/delete-transaction.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +TablesDB tablesDB = TablesDB(client); + +await tablesDB.deleteTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-row.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-row.md index 29e6eaab93..eb75da5073 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-row.md @@ -11,4 +11,5 @@ Row result = await tablesDB.getRow( tableId: '', rowId: '', queries: [], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..000e230227 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/get-transaction.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.getTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/increment-row-column.md index e05dc76753..91cd0ce351 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/increment-row-column.md @@ -13,4 +13,5 @@ Row result = await tablesDB.incrementRowColumn( column: '', value: 0, // optional max: 0, // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-rows.md index 7763e2ae3d..01d7066501 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-rows.md @@ -10,4 +10,5 @@ RowList result = await tablesDB.listRows( databaseId: '', tableId: '', queries: [], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..5e088cedc3 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/list-transactions.md @@ -0,0 +1,11 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +TablesDB tablesDB = TablesDB(client); + +TransactionList result = await tablesDB.listTransactions( + queries: [], // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-row.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-row.md index c2cc84d7f6..08ab309363 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-row.md @@ -12,4 +12,5 @@ Row result = await tablesDB.updateRow( rowId: '', data: {}, // optional permissions: ["read("any")"], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..ef56443e99 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/update-transaction.md @@ -0,0 +1,13 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.updateTransaction( + transactionId: '', + commit: false, // optional + rollback: false, // optional +); diff --git a/docs/examples/1.8.x/client-flutter/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-flutter/examples/tablesdb/upsert-row.md index 6a958a1898..061bb0b85a 100644 --- a/docs/examples/1.8.x/client-flutter/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-flutter/examples/tablesdb/upsert-row.md @@ -12,4 +12,5 @@ Row result = await tablesDB.upsertRow( rowId: '', data: {}, // optional permissions: ["read("any")"], // optional + transactionId: '', // optional ); diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/create-document.md b/docs/examples/1.8.x/client-graphql/examples/databases/create-document.md index 39e4bba1cb..411615f7a7 100644 --- a/docs/examples/1.8.x/client-graphql/examples/databases/create-document.md +++ b/docs/examples/1.8.x/client-graphql/examples/databases/create-document.md @@ -4,7 +4,8 @@ mutation { collectionId: "", documentId: "", data: "{\"username\":\"walter.obrien\",\"email\":\"walter.obrien@example.com\",\"fullName\":\"Walter O'Brien\",\"age\":30,\"isAdmin\":false}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/create-operations.md b/docs/examples/1.8.x/client-graphql/examples/databases/create-operations.md new file mode 100644 index 0000000000..1be3b39ee1 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/databases/create-operations.md @@ -0,0 +1,23 @@ +mutation { + databasesCreateOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/create-transaction.md b/docs/examples/1.8.x/client-graphql/examples/databases/create-transaction.md new file mode 100644 index 0000000000..7fea034ab6 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/databases/create-transaction.md @@ -0,0 +1,12 @@ +mutation { + databasesCreateTransaction( + ttl: 60 + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-graphql/examples/databases/decrement-document-attribute.md index 2e7970049d..e6032fd0e7 100644 --- a/docs/examples/1.8.x/client-graphql/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-graphql/examples/databases/decrement-document-attribute.md @@ -5,7 +5,8 @@ mutation { documentId: "", attribute: "", value: 0, - min: 0 + min: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/delete-document.md b/docs/examples/1.8.x/client-graphql/examples/databases/delete-document.md index 848371bca0..2e172aa5dd 100644 --- a/docs/examples/1.8.x/client-graphql/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/client-graphql/examples/databases/delete-document.md @@ -2,7 +2,8 @@ mutation { databasesDeleteDocument( databaseId: "", collectionId: "", - documentId: "" + documentId: "", + transactionId: "" ) { status } diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/delete-transaction.md b/docs/examples/1.8.x/client-graphql/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..cd29a0b8a6 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/databases/delete-transaction.md @@ -0,0 +1,7 @@ +mutation { + databasesDeleteTransaction( + transactionId: "" + ) { + status + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/get-transaction.md b/docs/examples/1.8.x/client-graphql/examples/databases/get-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-graphql/examples/databases/increment-document-attribute.md index 322ed69ced..3518ff1583 100644 --- a/docs/examples/1.8.x/client-graphql/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-graphql/examples/databases/increment-document-attribute.md @@ -5,7 +5,8 @@ mutation { documentId: "", attribute: "", value: 0, - max: 0 + max: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/list-transactions.md b/docs/examples/1.8.x/client-graphql/examples/databases/list-transactions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/update-document.md b/docs/examples/1.8.x/client-graphql/examples/databases/update-document.md index aea605d9d7..cf43d9eed0 100644 --- a/docs/examples/1.8.x/client-graphql/examples/databases/update-document.md +++ b/docs/examples/1.8.x/client-graphql/examples/databases/update-document.md @@ -4,7 +4,8 @@ mutation { collectionId: "", documentId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/update-transaction.md b/docs/examples/1.8.x/client-graphql/examples/databases/update-transaction.md new file mode 100644 index 0000000000..b56c7139ac --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/databases/update-transaction.md @@ -0,0 +1,14 @@ +mutation { + databasesUpdateTransaction( + transactionId: "", + commit: false, + rollback: false + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/databases/upsert-document.md b/docs/examples/1.8.x/client-graphql/examples/databases/upsert-document.md index 9d1e753081..d487c0d303 100644 --- a/docs/examples/1.8.x/client-graphql/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-graphql/examples/databases/upsert-document.md @@ -4,7 +4,8 @@ mutation { collectionId: "", documentId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..bb2be8085a --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-operations.md @@ -0,0 +1,23 @@ +mutation { + tablesDBCreateOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-row.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-row.md index c7d2ec7d03..109bc008d6 100644 --- a/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-row.md @@ -4,7 +4,8 @@ mutation { tableId: "", rowId: "", data: "{\"username\":\"walter.obrien\",\"email\":\"walter.obrien@example.com\",\"fullName\":\"Walter O'Brien\",\"age\":30,\"isAdmin\":false}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..0e874f0c78 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/create-transaction.md @@ -0,0 +1,12 @@ +mutation { + tablesDBCreateTransaction( + ttl: 60 + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/decrement-row-column.md index 398ec19901..1d57d79b54 100644 --- a/docs/examples/1.8.x/client-graphql/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/decrement-row-column.md @@ -5,7 +5,8 @@ mutation { rowId: "", column: "", value: 0, - min: 0 + min: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-row.md index 1a08b0f60d..3b44913049 100644 --- a/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-row.md @@ -2,7 +2,8 @@ mutation { tablesDBDeleteRow( databaseId: "", tableId: "", - rowId: "" + rowId: "", + transactionId: "" ) { status } diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..4a2d6f15a2 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/delete-transaction.md @@ -0,0 +1,7 @@ +mutation { + tablesDBDeleteTransaction( + transactionId: "" + ) { + status + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/increment-row-column.md index b7ff87f387..3ae008e718 100644 --- a/docs/examples/1.8.x/client-graphql/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/increment-row-column.md @@ -5,7 +5,8 @@ mutation { rowId: "", column: "", value: 0, - max: 0 + max: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-row.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-row.md index 5a5b288ab8..aa89e6ae01 100644 --- a/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-row.md @@ -4,7 +4,8 @@ mutation { tableId: "", rowId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..2094877303 --- /dev/null +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/update-transaction.md @@ -0,0 +1,14 @@ +mutation { + tablesDBUpdateTransaction( + transactionId: "", + commit: false, + rollback: false + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/client-graphql/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-graphql/examples/tablesdb/upsert-row.md index cc3b63de4a..3fe36ee7f1 100644 --- a/docs/examples/1.8.x/client-graphql/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-graphql/examples/tablesdb/upsert-row.md @@ -4,7 +4,8 @@ mutation { tableId: "", rowId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/create-document.md b/docs/examples/1.8.x/client-react-native/examples/databases/create-document.md index e7cffc13d4..3f7fd9af8f 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/create-document.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/create-document.md @@ -17,7 +17,8 @@ const result = await databases.createDocument({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/create-operations.md b/docs/examples/1.8.x/client-react-native/examples/databases/create-operations.md new file mode 100644 index 0000000000..bf02fd2c29 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/databases/create-operations.md @@ -0,0 +1,24 @@ +import { Client, Databases } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/create-transaction.md b/docs/examples/1.8.x/client-react-native/examples/databases/create-transaction.md new file mode 100644 index 0000000000..07a2103e59 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/databases/create-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.createTransaction({ + ttl: 60 // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-react-native/examples/databases/decrement-document-attribute.md index ddf43c9758..5edce7b091 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/decrement-document-attribute.md @@ -12,7 +12,8 @@ const result = await databases.decrementDocumentAttribute({ documentId: '', attribute: '', value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/delete-document.md b/docs/examples/1.8.x/client-react-native/examples/databases/delete-document.md index 828cefdda8..6cad3f9585 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/delete-document.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.deleteDocument({ databaseId: '', collectionId: '', - documentId: '' + documentId: '', + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/delete-transaction.md b/docs/examples/1.8.x/client-react-native/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..9ad2661362 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/databases/delete-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.deleteTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/get-document.md b/docs/examples/1.8.x/client-react-native/examples/databases/get-document.md index 7d28ee03d5..c61d396d3e 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/get-document.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/get-document.md @@ -10,7 +10,8 @@ const result = await databases.getDocument({ databaseId: '', collectionId: '', documentId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/get-transaction.md b/docs/examples/1.8.x/client-react-native/examples/databases/get-transaction.md new file mode 100644 index 0000000000..47f93691e0 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/databases/get-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.getTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-react-native/examples/databases/increment-document-attribute.md index c129c38a25..259a184e3a 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/increment-document-attribute.md @@ -12,7 +12,8 @@ const result = await databases.incrementDocumentAttribute({ documentId: '', attribute: '', value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/list-documents.md b/docs/examples/1.8.x/client-react-native/examples/databases/list-documents.md index 8d210b08e9..a744a531a1 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/list-documents.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.listDocuments({ databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/list-transactions.md b/docs/examples/1.8.x/client-react-native/examples/databases/list-transactions.md new file mode 100644 index 0000000000..2339673803 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/databases/list-transactions.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.listTransactions({ + queries: [] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/update-document.md b/docs/examples/1.8.x/client-react-native/examples/databases/update-document.md index ce4a6f2222..29674bd3d0 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/update-document.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/update-document.md @@ -11,7 +11,8 @@ const result = await databases.updateDocument({ collectionId: '', documentId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/update-transaction.md b/docs/examples/1.8.x/client-react-native/examples/databases/update-transaction.md new file mode 100644 index 0000000000..c333850656 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/databases/update-transaction.md @@ -0,0 +1,15 @@ +import { Client, Databases } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/databases/upsert-document.md b/docs/examples/1.8.x/client-react-native/examples/databases/upsert-document.md index a351ed7d4d..aa8fd1ca94 100644 --- a/docs/examples/1.8.x/client-react-native/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-react-native/examples/databases/upsert-document.md @@ -11,7 +11,8 @@ const result = await databases.upsertDocument({ collectionId: '', documentId: '', data: {}, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..1c76de77d2 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-operations.md @@ -0,0 +1,24 @@ +import { Client, TablesDB } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-row.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-row.md index a02a8376d5..6be799f547 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-row.md @@ -17,7 +17,8 @@ const result = await tablesDB.createRow({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..c2eca27695 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/create-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.createTransaction({ + ttl: 60 // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/decrement-row-column.md index e00eeb84bf..7bf6d77a46 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/decrement-row-column.md @@ -12,7 +12,8 @@ const result = await tablesDB.decrementRowColumn({ rowId: '', column: '', value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-row.md index 624f1a191f..3ab81237eb 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-row.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.deleteRow({ databaseId: '', tableId: '', - rowId: '' + rowId: '', + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..121e6b3f67 --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/delete-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.deleteTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-row.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-row.md index 081db46f18..a3a8775b4a 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-row.md @@ -10,7 +10,8 @@ const result = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..475e2d83ee --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/get-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.getTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/increment-row-column.md index d4b2cf98ad..4bda1efb24 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/increment-row-column.md @@ -12,7 +12,8 @@ const result = await tablesDB.incrementRowColumn({ rowId: '', column: '', value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-rows.md index 6148e97e90..7cab86bc64 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-rows.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.listRows({ databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..9d3004a90c --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/list-transactions.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.listTransactions({ + queries: [] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-row.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-row.md index 01ed6e6acc..a83e3ea3e1 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-row.md @@ -11,7 +11,8 @@ const result = await tablesDB.updateRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..de29a5bd2c --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/update-transaction.md @@ -0,0 +1,15 @@ +import { Client, TablesDB } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-react-native/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-react-native/examples/tablesdb/upsert-row.md index 72fad15ac4..7a82e0711e 100644 --- a/docs/examples/1.8.x/client-react-native/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-react-native/examples/tablesdb/upsert-row.md @@ -11,7 +11,8 @@ const result = await tablesDB.upsertRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/client-rest/examples/databases/create-document.md b/docs/examples/1.8.x/client-rest/examples/databases/create-document.md index 12f2159402..c53babd482 100644 --- a/docs/examples/1.8.x/client-rest/examples/databases/create-document.md +++ b/docs/examples/1.8.x/client-rest/examples/databases/create-document.md @@ -15,5 +15,6 @@ X-Appwrite-JWT: "age": 30, "isAdmin": false }, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/databases/create-operations.md b/docs/examples/1.8.x/client-rest/examples/databases/create-operations.md new file mode 100644 index 0000000000..602effd9a2 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/databases/create-operations.md @@ -0,0 +1,21 @@ +POST /v1/databases/transactions/{transactionId}/operations HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "operations": [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] +} diff --git a/docs/examples/1.8.x/client-rest/examples/databases/create-transaction.md b/docs/examples/1.8.x/client-rest/examples/databases/create-transaction.md new file mode 100644 index 0000000000..c58528731e --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/databases/create-transaction.md @@ -0,0 +1,11 @@ +POST /v1/databases/transactions HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "ttl": 60 +} diff --git a/docs/examples/1.8.x/client-rest/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/client-rest/examples/databases/decrement-document-attribute.md index 85ee70588b..be1d8d5bb6 100644 --- a/docs/examples/1.8.x/client-rest/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/client-rest/examples/databases/decrement-document-attribute.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "value": 0, - "min": 0 + "min": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/databases/delete-document.md b/docs/examples/1.8.x/client-rest/examples/databases/delete-document.md index 2ee4f92ec4..3fa0a3ca21 100644 --- a/docs/examples/1.8.x/client-rest/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/client-rest/examples/databases/delete-document.md @@ -6,3 +6,6 @@ X-Appwrite-Project: X-Appwrite-Session: X-Appwrite-JWT: +{ + "transactionId": "" +} diff --git a/docs/examples/1.8.x/client-rest/examples/databases/delete-transaction.md b/docs/examples/1.8.x/client-rest/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..1982dbb5a4 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/databases/delete-transaction.md @@ -0,0 +1,8 @@ +DELETE /v1/databases/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + diff --git a/docs/examples/1.8.x/client-rest/examples/databases/get-transaction.md b/docs/examples/1.8.x/client-rest/examples/databases/get-transaction.md new file mode 100644 index 0000000000..09cc2e6c95 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/databases/get-transaction.md @@ -0,0 +1,6 @@ +GET /v1/databases/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/client-rest/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/client-rest/examples/databases/increment-document-attribute.md index 68b091a31e..9eb873d6ff 100644 --- a/docs/examples/1.8.x/client-rest/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/client-rest/examples/databases/increment-document-attribute.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "value": 0, - "max": 0 + "max": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/databases/list-transactions.md b/docs/examples/1.8.x/client-rest/examples/databases/list-transactions.md new file mode 100644 index 0000000000..f080df9228 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/databases/list-transactions.md @@ -0,0 +1,6 @@ +GET /v1/databases/transactions HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/client-rest/examples/databases/update-document.md b/docs/examples/1.8.x/client-rest/examples/databases/update-document.md index ffc5d36011..f39cabfec3 100644 --- a/docs/examples/1.8.x/client-rest/examples/databases/update-document.md +++ b/docs/examples/1.8.x/client-rest/examples/databases/update-document.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/databases/update-transaction.md b/docs/examples/1.8.x/client-rest/examples/databases/update-transaction.md new file mode 100644 index 0000000000..e8358a9051 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/databases/update-transaction.md @@ -0,0 +1,12 @@ +PATCH /v1/databases/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "commit": false, + "rollback": false +} diff --git a/docs/examples/1.8.x/client-rest/examples/databases/upsert-document.md b/docs/examples/1.8.x/client-rest/examples/databases/upsert-document.md index d2baeac6a8..c84d74a5ff 100644 --- a/docs/examples/1.8.x/client-rest/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-rest/examples/databases/upsert-document.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..dd3a05e271 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/create-operations.md @@ -0,0 +1,21 @@ +POST /v1/tablesdb/transactions/{transactionId}/operations HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "operations": [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] +} diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/create-row.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/create-row.md index 34d8ab5152..2abe0cc316 100644 --- a/docs/examples/1.8.x/client-rest/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/create-row.md @@ -15,5 +15,6 @@ X-Appwrite-JWT: "age": 30, "isAdmin": false }, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..e796ea2b57 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/create-transaction.md @@ -0,0 +1,11 @@ +POST /v1/tablesdb/transactions HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "ttl": 60 +} diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/decrement-row-column.md index 676e090150..b8dd25fdc5 100644 --- a/docs/examples/1.8.x/client-rest/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/decrement-row-column.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "value": 0, - "min": 0 + "min": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-row.md index f3ba056f7e..908bc4cc1f 100644 --- a/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-row.md @@ -6,3 +6,6 @@ X-Appwrite-Project: X-Appwrite-Session: X-Appwrite-JWT: +{ + "transactionId": "" +} diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..54421d1c47 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/delete-transaction.md @@ -0,0 +1,8 @@ +DELETE /v1/tablesdb/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..4276a3fb94 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/get-transaction.md @@ -0,0 +1,6 @@ +GET /v1/tablesdb/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/increment-row-column.md index 5172cb420b..be9effd073 100644 --- a/docs/examples/1.8.x/client-rest/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/increment-row-column.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "value": 0, - "max": 0 + "max": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..91b6108463 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/list-transactions.md @@ -0,0 +1,6 @@ +GET /v1/tablesdb/transactions HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/update-row.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/update-row.md index 40d276a0fe..7249d93906 100644 --- a/docs/examples/1.8.x/client-rest/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/update-row.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..f0f96d735f --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/update-transaction.md @@ -0,0 +1,12 @@ +PATCH /v1/tablesdb/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "commit": false, + "rollback": false +} diff --git a/docs/examples/1.8.x/client-rest/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-rest/examples/tablesdb/upsert-row.md index 581790fc01..93f6236eca 100644 --- a/docs/examples/1.8.x/client-rest/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-rest/examples/tablesdb/upsert-row.md @@ -8,5 +8,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/console-cli/examples/databases/create-operations.md b/docs/examples/1.8.x/console-cli/examples/databases/create-operations.md new file mode 100644 index 0000000000..367b435c9d --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/databases/create-operations.md @@ -0,0 +1,2 @@ +appwrite databases create-operations \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/databases/create-transaction.md b/docs/examples/1.8.x/console-cli/examples/databases/create-transaction.md new file mode 100644 index 0000000000..ef348e75ad --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/databases/create-transaction.md @@ -0,0 +1 @@ +appwrite databases create-transaction diff --git a/docs/examples/1.8.x/console-cli/examples/databases/delete-transaction.md b/docs/examples/1.8.x/console-cli/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..13c02b676b --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/databases/delete-transaction.md @@ -0,0 +1,2 @@ +appwrite databases delete-transaction \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/databases/get-transaction.md b/docs/examples/1.8.x/console-cli/examples/databases/get-transaction.md new file mode 100644 index 0000000000..7fc80e40d2 --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/databases/get-transaction.md @@ -0,0 +1,2 @@ +appwrite databases get-transaction \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/databases/list-transactions.md b/docs/examples/1.8.x/console-cli/examples/databases/list-transactions.md new file mode 100644 index 0000000000..f0cc259b43 --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/databases/list-transactions.md @@ -0,0 +1 @@ +appwrite databases list-transactions diff --git a/docs/examples/1.8.x/console-cli/examples/databases/update-transaction.md b/docs/examples/1.8.x/console-cli/examples/databases/update-transaction.md new file mode 100644 index 0000000000..cda11d4e5f --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/databases/update-transaction.md @@ -0,0 +1,2 @@ +appwrite databases update-transaction \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-migration.md b/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-migration.md index 4b72b6f689..10e7b42b6c 100644 --- a/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-migration.md +++ b/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-migration.md @@ -1,4 +1,4 @@ appwrite migrations create-csv-migration \ --bucket-id \ --file-id \ - --resource-id [ID1:ID2] + --resource-id diff --git a/docs/examples/1.8.x/console-cli/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/console-cli/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..dbea61862b --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/tablesdb/create-operations.md @@ -0,0 +1,2 @@ +appwrite tables-db create-operations \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/console-cli/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..c6487fd74a --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/tablesdb/create-transaction.md @@ -0,0 +1 @@ +appwrite tables-db create-transaction diff --git a/docs/examples/1.8.x/console-cli/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/console-cli/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..59e40d2fda --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/tablesdb/delete-transaction.md @@ -0,0 +1,2 @@ +appwrite tables-db delete-transaction \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/console-cli/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..29ea9d75da --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/tablesdb/get-transaction.md @@ -0,0 +1,2 @@ +appwrite tables-db get-transaction \ + --transaction-id diff --git a/docs/examples/1.8.x/console-cli/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/console-cli/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..4dfbc2e567 --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/tablesdb/list-transactions.md @@ -0,0 +1 @@ +appwrite tables-db list-transactions diff --git a/docs/examples/1.8.x/console-cli/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/console-cli/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..6fa6d9510e --- /dev/null +++ b/docs/examples/1.8.x/console-cli/examples/tablesdb/update-transaction.md @@ -0,0 +1,2 @@ +appwrite tables-db update-transaction \ + --transaction-id diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-document.md b/docs/examples/1.8.x/console-web/examples/databases/create-document.md index 40fbd4ad85..80f3fe66ad 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-document.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-document.md @@ -17,7 +17,8 @@ const result = await databases.createDocument({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-documents.md b/docs/examples/1.8.x/console-web/examples/databases/create-documents.md index 901133ac0a..10738b0f11 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-documents.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.createDocuments({ databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-operations.md b/docs/examples/1.8.x/console-web/examples/databases/create-operations.md new file mode 100644 index 0000000000..ec511b0ddc --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/databases/create-operations.md @@ -0,0 +1,24 @@ +import { Client, Databases } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-transaction.md b/docs/examples/1.8.x/console-web/examples/databases/create-transaction.md new file mode 100644 index 0000000000..fc84f1d14f --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/databases/create-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.createTransaction({ + ttl: 60 // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/decrement-document-attribute.md index ec014643bb..64f469c6ef 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/decrement-document-attribute.md @@ -12,7 +12,8 @@ const result = await databases.decrementDocumentAttribute({ documentId: '', attribute: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/delete-document.md b/docs/examples/1.8.x/console-web/examples/databases/delete-document.md index 11cf317147..c2cdad3d84 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/console-web/examples/databases/delete-document.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.deleteDocument({ databaseId: '', collectionId: '', - documentId: '' + documentId: '', + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/delete-documents.md b/docs/examples/1.8.x/console-web/examples/databases/delete-documents.md index 45601a5985..0749194c97 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/console-web/examples/databases/delete-documents.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.deleteDocuments({ databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/delete-transaction.md b/docs/examples/1.8.x/console-web/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..16b7c64022 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/databases/delete-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.deleteTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/get-document.md b/docs/examples/1.8.x/console-web/examples/databases/get-document.md index 00595b1033..8d893df7d9 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/get-document.md +++ b/docs/examples/1.8.x/console-web/examples/databases/get-document.md @@ -10,7 +10,8 @@ const result = await databases.getDocument({ databaseId: '', collectionId: '', documentId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/get-transaction.md b/docs/examples/1.8.x/console-web/examples/databases/get-transaction.md new file mode 100644 index 0000000000..8b6733b423 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/databases/get-transaction.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.getTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/increment-document-attribute.md index 2207e94563..dbba4b0688 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/increment-document-attribute.md @@ -12,7 +12,8 @@ const result = await databases.incrementDocumentAttribute({ documentId: '', attribute: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/list-documents.md b/docs/examples/1.8.x/console-web/examples/databases/list-documents.md index dda036951c..f0a7a27890 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/console-web/examples/databases/list-documents.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.listDocuments({ databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/list-transactions.md b/docs/examples/1.8.x/console-web/examples/databases/list-transactions.md new file mode 100644 index 0000000000..53e8d61f3e --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/databases/list-transactions.md @@ -0,0 +1,13 @@ +import { Client, Databases } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.listTransactions({ + queries: [] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/update-document.md b/docs/examples/1.8.x/console-web/examples/databases/update-document.md index 168ad11bda..8a92d5b9f4 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/update-document.md +++ b/docs/examples/1.8.x/console-web/examples/databases/update-document.md @@ -11,7 +11,8 @@ const result = await databases.updateDocument({ collectionId: '', documentId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/update-documents.md b/docs/examples/1.8.x/console-web/examples/databases/update-documents.md index 7462502c81..9f9054bad2 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/console-web/examples/databases/update-documents.md @@ -10,7 +10,8 @@ const result = await databases.updateDocuments({ databaseId: '', collectionId: '', data: {}, // optional - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/update-transaction.md b/docs/examples/1.8.x/console-web/examples/databases/update-transaction.md new file mode 100644 index 0000000000..4a219f4beb --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/databases/update-transaction.md @@ -0,0 +1,15 @@ +import { Client, Databases } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const databases = new Databases(client); + +const result = await databases.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/upsert-document.md b/docs/examples/1.8.x/console-web/examples/databases/upsert-document.md index 3f8705f7c8..e7a52fd7cd 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/console-web/examples/databases/upsert-document.md @@ -11,7 +11,8 @@ const result = await databases.upsertDocument({ collectionId: '', documentId: '', data: {}, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/upsert-documents.md b/docs/examples/1.8.x/console-web/examples/databases/upsert-documents.md index b045516281..cc561de247 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/console-web/examples/databases/upsert-documents.md @@ -9,7 +9,8 @@ const databases = new Databases(client); const result = await databases.upsertDocuments({ databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/messaging/create-push.md b/docs/examples/1.8.x/console-web/examples/messaging/create-push.md index f0f54aa72f..783d8fe535 100644 --- a/docs/examples/1.8.x/console-web/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/console-web/examples/messaging/create-push.md @@ -15,7 +15,7 @@ const result = await messaging.createPush({ targets: [], // optional data: {}, // optional action: '', // optional - image: '[ID1:ID2]', // optional + image: '', // optional icon: '', // optional sound: '', // optional color: '', // optional diff --git a/docs/examples/1.8.x/console-web/examples/messaging/update-push.md b/docs/examples/1.8.x/console-web/examples/messaging/update-push.md index 5f5d6e5da2..e1b0cc6b9d 100644 --- a/docs/examples/1.8.x/console-web/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/console-web/examples/messaging/update-push.md @@ -15,7 +15,7 @@ const result = await messaging.updatePush({ body: '', // optional data: {}, // optional action: '', // optional - image: '[ID1:ID2]', // optional + image: '', // optional icon: '', // optional sound: '', // optional color: '', // optional diff --git a/docs/examples/1.8.x/console-web/examples/migrations/create-csv-migration.md b/docs/examples/1.8.x/console-web/examples/migrations/create-csv-migration.md index b25193ed21..f32adcb53c 100644 --- a/docs/examples/1.8.x/console-web/examples/migrations/create-csv-migration.md +++ b/docs/examples/1.8.x/console-web/examples/migrations/create-csv-migration.md @@ -9,7 +9,7 @@ const migrations = new Migrations(client); const result = await migrations.createCsvMigration({ bucketId: '', fileId: '', - resourceId: '[ID1:ID2]', + resourceId: '', internalFile: false // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..744627adae --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-operations.md @@ -0,0 +1,24 @@ +import { Client, TablesDB } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.createOperations({ + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-row.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-row.md index 6123b5fc31..1991d44258 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-row.md @@ -17,7 +17,8 @@ const result = await tablesDB.createRow({ "age": 30, "isAdmin": false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-rows.md index b827beb048..1054433a74 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-rows.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.createRows({ databaseId: '', tableId: '', - rows: [] + rows: [], + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..68465d4968 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.createTransaction({ + ttl: 60 // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/decrement-row-column.md index 067afc7820..2f46b6d958 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/decrement-row-column.md @@ -12,7 +12,8 @@ const result = await tablesDB.decrementRowColumn({ rowId: '', column: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/console-web/examples/tablesdb/delete-row.md index 9b7def4f00..1bcb477c18 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/delete-row.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.deleteRow({ databaseId: '', tableId: '', - rowId: '' + rowId: '', + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/console-web/examples/tablesdb/delete-rows.md index 16868a0c6e..c955326753 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/delete-rows.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.deleteRows({ databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/console-web/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..b4f427a727 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/delete-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.deleteTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/get-row.md b/docs/examples/1.8.x/console-web/examples/tablesdb/get-row.md index f170078430..831a9d1af2 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/get-row.md @@ -10,7 +10,8 @@ const result = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/console-web/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..99d405e0a2 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/get-transaction.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.getTransaction({ + transactionId: '' +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/increment-row-column.md index 95694d49a4..a0047abb86 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/increment-row-column.md @@ -12,7 +12,8 @@ const result = await tablesDB.incrementRowColumn({ rowId: '', column: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/console-web/examples/tablesdb/list-rows.md index 46159b3d42..d87bd450d0 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/list-rows.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.listRows({ databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/console-web/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..8ecfcefee1 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/list-transactions.md @@ -0,0 +1,13 @@ +import { Client, TablesDB } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.listTransactions({ + queries: [] // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/update-row.md b/docs/examples/1.8.x/console-web/examples/tablesdb/update-row.md index 5d6109c04c..6193d79567 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/update-row.md @@ -11,7 +11,8 @@ const result = await tablesDB.updateRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/console-web/examples/tablesdb/update-rows.md index 0ce117efa3..7601955b8b 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/update-rows.md @@ -10,7 +10,8 @@ const result = await tablesDB.updateRows({ databaseId: '', tableId: '', data: {}, // optional - queries: [] // optional + queries: [], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/console-web/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..9095edc161 --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/update-transaction.md @@ -0,0 +1,15 @@ +import { Client, TablesDB } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const tablesDB = new TablesDB(client); + +const result = await tablesDB.updateTransaction({ + transactionId: '', + commit: false, // optional + rollback: false // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-row.md index 665f181d8b..f56eff55fa 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-row.md @@ -11,7 +11,8 @@ const result = await tablesDB.upsertRow({ tableId: '', rowId: '', data: {}, // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-rows.md index 05e78e3efa..173d0e3065 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/upsert-rows.md @@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client); const result = await tablesDB.upsertRows({ databaseId: '', tableId: '', - rows: [] + rows: [], + transactionId: '' // optional }); console.log(result); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/create-document.md b/docs/examples/1.8.x/server-dart/examples/databases/create-document.md index e3bae98162..0f663a67f6 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/create-document.md @@ -19,4 +19,5 @@ Document result = await databases.createDocument( "isAdmin": false }, permissions: ["read("any")"], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/create-documents.md b/docs/examples/1.8.x/server-dart/examples/databases/create-documents.md index ba0e34950b..6c77b78fe2 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/create-documents.md @@ -11,4 +11,5 @@ DocumentList result = await databases.createDocuments( databaseId: '', collectionId: '', documents: [], + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/create-operations.md b/docs/examples/1.8.x/server-dart/examples/databases/create-operations.md new file mode 100644 index 0000000000..2fe1121bf4 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/databases/create-operations.md @@ -0,0 +1,23 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Databases databases = Databases(client); + +Transaction result = await databases.createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ], // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-dart/examples/databases/create-transaction.md new file mode 100644 index 0000000000..69af666c73 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/databases/create-transaction.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Databases databases = Databases(client); + +Transaction result = await databases.createTransaction( + ttl: 60, // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-dart/examples/databases/decrement-document-attribute.md index e46244874d..6fb7ab68e6 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/decrement-document-attribute.md @@ -14,4 +14,5 @@ Document result = await databases.decrementDocumentAttribute( attribute: '', value: 0, // (optional) min: 0, // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/delete-document.md b/docs/examples/1.8.x/server-dart/examples/databases/delete-document.md index dd04d89959..eb9c3eba36 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/delete-document.md @@ -11,4 +11,5 @@ await databases.deleteDocument( databaseId: '', collectionId: '', documentId: '', + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-dart/examples/databases/delete-documents.md index 66bd5584c7..2e4e0c3cc2 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/delete-documents.md @@ -11,4 +11,5 @@ await databases.deleteDocuments( databaseId: '', collectionId: '', queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-dart/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..6cebc33f83 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/databases/delete-transaction.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Databases databases = Databases(client); + +await databases.deleteTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/get-document.md b/docs/examples/1.8.x/server-dart/examples/databases/get-document.md index 45745186e6..cd87138b7e 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/get-document.md @@ -12,4 +12,5 @@ Document result = await databases.getDocument( collectionId: '', documentId: '', queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-dart/examples/databases/get-transaction.md new file mode 100644 index 0000000000..dfa1b583c1 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/databases/get-transaction.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Databases databases = Databases(client); + +Transaction result = await databases.getTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-dart/examples/databases/increment-document-attribute.md index a2d2622629..ab108ebb27 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/increment-document-attribute.md @@ -14,4 +14,5 @@ Document result = await databases.incrementDocumentAttribute( attribute: '', value: 0, // (optional) max: 0, // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/list-documents.md b/docs/examples/1.8.x/server-dart/examples/databases/list-documents.md index cdecc59e33..74d849a2cb 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/list-documents.md @@ -11,4 +11,5 @@ DocumentList result = await databases.listDocuments( databaseId: '', collectionId: '', queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-dart/examples/databases/list-transactions.md new file mode 100644 index 0000000000..856e5e86ac --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/databases/list-transactions.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Databases databases = Databases(client); + +TransactionList result = await databases.listTransactions( + queries: [], // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/update-document.md b/docs/examples/1.8.x/server-dart/examples/databases/update-document.md index 47a1867c10..077a08f606 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/update-document.md @@ -13,4 +13,5 @@ Document result = await databases.updateDocument( documentId: '', data: {}, // (optional) permissions: ["read("any")"], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/update-documents.md b/docs/examples/1.8.x/server-dart/examples/databases/update-documents.md index 70b7cbf86d..babc2d96fb 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/update-documents.md @@ -12,4 +12,5 @@ DocumentList result = await databases.updateDocuments( collectionId: '', data: {}, // (optional) queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-dart/examples/databases/update-transaction.md new file mode 100644 index 0000000000..b2b35f20eb --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/databases/update-transaction.md @@ -0,0 +1,14 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Databases databases = Databases(client); + +Transaction result = await databases.updateTransaction( + transactionId: '', + commit: false, // (optional) + rollback: false, // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-dart/examples/databases/upsert-document.md index 93e306ebce..be4216da67 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/upsert-document.md @@ -13,4 +13,5 @@ Document result = await databases.upsertDocument( documentId: '', data: {}, permissions: ["read("any")"], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-dart/examples/databases/upsert-documents.md index cd35014f63..009fe18b4a 100644 --- a/docs/examples/1.8.x/server-dart/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-dart/examples/databases/upsert-documents.md @@ -11,4 +11,5 @@ DocumentList result = await databases.upsertDocuments( databaseId: '', collectionId: '', documents: [], + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/messaging/create-push.md b/docs/examples/1.8.x/server-dart/examples/messaging/create-push.md index 58d82c7a0a..4b9f7d3c52 100644 --- a/docs/examples/1.8.x/server-dart/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-dart/examples/messaging/create-push.md @@ -16,7 +16,7 @@ Message result = await messaging.createPush( targets: [], // (optional) data: {}, // (optional) action: '', // (optional) - image: '[ID1:ID2]', // (optional) + image: '', // (optional) icon: '', // (optional) sound: '', // (optional) color: '', // (optional) diff --git a/docs/examples/1.8.x/server-dart/examples/messaging/update-push.md b/docs/examples/1.8.x/server-dart/examples/messaging/update-push.md index f7cc117b64..cbae5dfabb 100644 --- a/docs/examples/1.8.x/server-dart/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-dart/examples/messaging/update-push.md @@ -16,7 +16,7 @@ Message result = await messaging.updatePush( body: '', // (optional) data: {}, // (optional) action: '', // (optional) - image: '[ID1:ID2]', // (optional) + image: '', // (optional) icon: '', // (optional) sound: '', // (optional) color: '', // (optional) diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..2b5c046b14 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-operations.md @@ -0,0 +1,23 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ], // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-row.md index c2f5dd8293..255bd9615e 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-row.md @@ -19,4 +19,5 @@ Row result = await tablesDB.createRow( "isAdmin": false }, permissions: ["read("any")"], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-rows.md index c22fdba506..6366006c31 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-rows.md @@ -11,4 +11,5 @@ RowList result = await tablesDB.createRows( databaseId: '', tableId: '', rows: [], + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..721ac4cf8b --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/create-transaction.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.createTransaction( + ttl: 60, // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/decrement-row-column.md index 8c376563d8..304e6b0219 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/decrement-row-column.md @@ -14,4 +14,5 @@ Row result = await tablesDB.decrementRowColumn( column: '', value: 0, // (optional) min: 0, // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-row.md index f3966c0930..da1e280cba 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-row.md @@ -11,4 +11,5 @@ await tablesDB.deleteRow( databaseId: '', tableId: '', rowId: '', + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-rows.md index eb2aebb3e6..6738ac78fc 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-rows.md @@ -11,4 +11,5 @@ await tablesDB.deleteRows( databaseId: '', tableId: '', queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..8dc80418a0 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/delete-transaction.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +await tablesDB.deleteTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/get-row.md index 0af17b612a..a0d7a83b39 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/get-row.md @@ -12,4 +12,5 @@ Row result = await tablesDB.getRow( tableId: '', rowId: '', queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..267af59aa9 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/get-transaction.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.getTransaction( + transactionId: '', +); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/increment-row-column.md index bbe262e5c9..049a48a7d0 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/increment-row-column.md @@ -14,4 +14,5 @@ Row result = await tablesDB.incrementRowColumn( column: '', value: 0, // (optional) max: 0, // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/list-rows.md index 83bbe38fca..a429de72f6 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/list-rows.md @@ -11,4 +11,5 @@ RowList result = await tablesDB.listRows( databaseId: '', tableId: '', queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..d4b3a5e2c3 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/list-transactions.md @@ -0,0 +1,12 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +TransactionList result = await tablesDB.listTransactions( + queries: [], // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/update-row.md index d66c24f505..e4f683cae0 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/update-row.md @@ -13,4 +13,5 @@ Row result = await tablesDB.updateRow( rowId: '', data: {}, // (optional) permissions: ["read("any")"], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/update-rows.md index a0973b47d1..a7c0d61b45 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/update-rows.md @@ -12,4 +12,5 @@ RowList result = await tablesDB.updateRows( tableId: '', data: {}, // (optional) queries: [], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..bb3837d4ec --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/update-transaction.md @@ -0,0 +1,14 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +Transaction result = await tablesDB.updateTransaction( + transactionId: '', + commit: false, // (optional) + rollback: false, // (optional) +); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-row.md index fbe4d2928a..f9b8c848ae 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-row.md @@ -13,4 +13,5 @@ Row result = await tablesDB.upsertRow( rowId: '', data: {}, // (optional) permissions: ["read("any")"], // (optional) + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-rows.md index 031aa50bea..d48e9094f4 100644 --- a/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-dart/examples/tablesdb/upsert-rows.md @@ -11,4 +11,5 @@ RowList result = await tablesDB.upsertRows( databaseId: '', tableId: '', rows: [], + transactionId: '', // (optional) ); diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/create-document.md b/docs/examples/1.8.x/server-dotnet/examples/databases/create-document.md index 4a7444db7a..9fcfdd041d 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/create-document.md @@ -20,5 +20,6 @@ Document result = await databases.CreateDocument( age = 30, isAdmin = false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/create-documents.md b/docs/examples/1.8.x/server-dotnet/examples/databases/create-documents.md index dad710f0df..cc6cfb7606 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/create-documents.md @@ -12,5 +12,6 @@ Databases databases = new Databases(client); DocumentList result = await databases.CreateDocuments( databaseId: "", collectionId: "", - documents: new List() + documents: new List(), + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/create-operations.md b/docs/examples/1.8.x/server-dotnet/examples/databases/create-operations.md new file mode 100644 index 0000000000..701c6432b8 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/create-operations.md @@ -0,0 +1,25 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +Databases databases = new Databases(client); + +Transaction result = await databases.CreateOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/databases/create-transaction.md new file mode 100644 index 0000000000..f8d7b34ffd --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/create-transaction.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +Databases databases = new Databases(client); + +Transaction result = await databases.CreateTransaction( + ttl: 60 // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-dotnet/examples/databases/decrement-document-attribute.md index 2e48d4578e..9ff62a08aa 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/decrement-document-attribute.md @@ -15,5 +15,6 @@ Document result = await databases.DecrementDocumentAttribute( documentId: "", attribute: "", value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/delete-document.md b/docs/examples/1.8.x/server-dotnet/examples/databases/delete-document.md index 221b80e254..34bdbdafcd 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/delete-document.md @@ -12,5 +12,6 @@ Databases databases = new Databases(client); await databases.DeleteDocument( databaseId: "", collectionId: "", - documentId: "" + documentId: "", + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-dotnet/examples/databases/delete-documents.md index a9bc9c277b..52f711b842 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/delete-documents.md @@ -12,5 +12,6 @@ Databases databases = new Databases(client); await databases.DeleteDocuments( databaseId: "", collectionId: "", - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..713a75787e --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/delete-transaction.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +Databases databases = new Databases(client); + +await databases.DeleteTransaction( + transactionId: "" +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/get-document.md b/docs/examples/1.8.x/server-dotnet/examples/databases/get-document.md index d7e9b68807..bbafc3c888 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/get-document.md @@ -13,5 +13,6 @@ Document result = await databases.GetDocument( databaseId: "", collectionId: "", documentId: "", - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/databases/get-transaction.md new file mode 100644 index 0000000000..d66ab6e205 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/get-transaction.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +Databases databases = new Databases(client); + +Transaction result = await databases.GetTransaction( + transactionId: "" +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-dotnet/examples/databases/increment-document-attribute.md index 923c8d63e2..37a4ed76eb 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/increment-document-attribute.md @@ -15,5 +15,6 @@ Document result = await databases.IncrementDocumentAttribute( documentId: "", attribute: "", value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/list-documents.md b/docs/examples/1.8.x/server-dotnet/examples/databases/list-documents.md index f59e4576bd..643c42015d 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/list-documents.md @@ -12,5 +12,6 @@ Databases databases = new Databases(client); DocumentList result = await databases.ListDocuments( databaseId: "", collectionId: "", - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-dotnet/examples/databases/list-transactions.md new file mode 100644 index 0000000000..3f2ce0df24 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/list-transactions.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +Databases databases = new Databases(client); + +TransactionList result = await databases.ListTransactions( + queries: new List() // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/update-document.md b/docs/examples/1.8.x/server-dotnet/examples/databases/update-document.md index 3121c15e08..838b2790a9 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/update-document.md @@ -14,5 +14,6 @@ Document result = await databases.UpdateDocument( collectionId: "", documentId: "", data: [object], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/update-documents.md b/docs/examples/1.8.x/server-dotnet/examples/databases/update-documents.md index 63ded21ac9..077ec04da6 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/update-documents.md @@ -13,5 +13,6 @@ DocumentList result = await databases.UpdateDocuments( databaseId: "", collectionId: "", data: [object], // optional - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/databases/update-transaction.md new file mode 100644 index 0000000000..056f0d86f0 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/update-transaction.md @@ -0,0 +1,16 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +Databases databases = new Databases(client); + +Transaction result = await databases.UpdateTransaction( + transactionId: "", + commit: false, // optional + rollback: false // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-document.md index c0876bfa73..e12c5dd0d7 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-document.md @@ -14,5 +14,6 @@ Document result = await databases.UpsertDocument( collectionId: "", documentId: "", data: [object], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-documents.md index 6c124c16e5..4fefbfcc38 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-dotnet/examples/databases/upsert-documents.md @@ -12,5 +12,6 @@ Databases databases = new Databases(client); DocumentList result = await databases.UpsertDocuments( databaseId: "", collectionId: "", - documents: new List() + documents: new List(), + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/messaging/create-push.md b/docs/examples/1.8.x/server-dotnet/examples/messaging/create-push.md index 1d2dbec1f2..ec90fa6d90 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-dotnet/examples/messaging/create-push.md @@ -19,7 +19,7 @@ Message result = await messaging.CreatePush( targets: new List(), // optional data: [object], // optional action: "", // optional - image: "[ID1:ID2]", // optional + image: "", // optional icon: "", // optional sound: "", // optional color: "", // optional diff --git a/docs/examples/1.8.x/server-dotnet/examples/messaging/update-push.md b/docs/examples/1.8.x/server-dotnet/examples/messaging/update-push.md index 37da215e82..b45752c815 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-dotnet/examples/messaging/update-push.md @@ -19,7 +19,7 @@ Message result = await messaging.UpdatePush( body: "", // optional data: [object], // optional action: "", // optional - image: "[ID1:ID2]", // optional + image: "", // optional icon: "", // optional sound: "", // optional color: "", // optional diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..0a8da3e8cc --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-operations.md @@ -0,0 +1,25 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +Transaction result = await tablesDB.CreateOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-row.md index 01a21b0dcd..8d56063f1f 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-row.md @@ -20,5 +20,6 @@ Row result = await tablesDB.CreateRow( age = 30, isAdmin = false }, - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-rows.md index c23e795a84..c73c474f45 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-rows.md @@ -12,5 +12,6 @@ TablesDB tablesDB = new TablesDB(client); RowList result = await tablesDB.CreateRows( databaseId: "", tableId: "", - rows: new List() + rows: new List(), + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..b42b087539 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/create-transaction.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +Transaction result = await tablesDB.CreateTransaction( + ttl: 60 // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/decrement-row-column.md index 66d98b65b9..19498d035f 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/decrement-row-column.md @@ -15,5 +15,6 @@ Row result = await tablesDB.DecrementRowColumn( rowId: "", column: "", value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-row.md index 86d7fbf392..81bd084f62 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-row.md @@ -12,5 +12,6 @@ TablesDB tablesDB = new TablesDB(client); await tablesDB.DeleteRow( databaseId: "", tableId: "", - rowId: "" + rowId: "", + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-rows.md index 13d5758fdb..c0f656cda5 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-rows.md @@ -12,5 +12,6 @@ TablesDB tablesDB = new TablesDB(client); await tablesDB.DeleteRows( databaseId: "", tableId: "", - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..6e41c80c02 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/delete-transaction.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +await tablesDB.DeleteTransaction( + transactionId: "" +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-row.md index 99d79ac3dd..66f6a230e3 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-row.md @@ -13,5 +13,6 @@ Row result = await tablesDB.GetRow( databaseId: "", tableId: "", rowId: "", - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..73e0904982 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/get-transaction.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +Transaction result = await tablesDB.GetTransaction( + transactionId: "" +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/increment-row-column.md index 48eabc34d4..cbac61f159 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/increment-row-column.md @@ -15,5 +15,6 @@ Row result = await tablesDB.IncrementRowColumn( rowId: "", column: "", value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-rows.md index d3f860e869..79f809d35d 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-rows.md @@ -12,5 +12,6 @@ TablesDB tablesDB = new TablesDB(client); RowList result = await tablesDB.ListRows( databaseId: "", tableId: "", - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..b1a00e1a44 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/list-transactions.md @@ -0,0 +1,14 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +TransactionList result = await tablesDB.ListTransactions( + queries: new List() // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-row.md index 5eb5acdbd2..40f4eb4314 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-row.md @@ -14,5 +14,6 @@ Row result = await tablesDB.UpdateRow( tableId: "", rowId: "", data: [object], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-rows.md index 401464d66a..368977a0cc 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-rows.md @@ -13,5 +13,6 @@ RowList result = await tablesDB.UpdateRows( databaseId: "", tableId: "", data: [object], // optional - queries: new List() // optional + queries: new List(), // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..3b44fd5d37 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/update-transaction.md @@ -0,0 +1,16 @@ +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +Transaction result = await tablesDB.UpdateTransaction( + transactionId: "", + commit: false, // optional + rollback: false // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-row.md index e1f68a7dfb..18b8419146 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-row.md @@ -14,5 +14,6 @@ Row result = await tablesDB.UpsertRow( tableId: "", rowId: "", data: [object], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-rows.md index 199dc2ba85..fde5df7149 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-dotnet/examples/tablesdb/upsert-rows.md @@ -12,5 +12,6 @@ TablesDB tablesDB = new TablesDB(client); RowList result = await tablesDB.UpsertRows( databaseId: "", tableId: "", - rows: new List() + rows: new List(), + transactionId: "" // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-go/examples/databases/create-document.md b/docs/examples/1.8.x/server-go/examples/databases/create-document.md index ea6305a06e..1c7a489129 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-go/examples/databases/create-document.md @@ -26,4 +26,5 @@ response, error := service.CreateDocument( "isAdmin": false }, databases.WithCreateDocumentPermissions(interface{}{"read("any")"}), + databases.WithCreateDocumentTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/create-documents.md b/docs/examples/1.8.x/server-go/examples/databases/create-documents.md index 8bd0a5761a..ae08b2e7d8 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-go/examples/databases/create-documents.md @@ -18,4 +18,5 @@ response, error := service.CreateDocuments( "", "", []interface{}{}, + databases.WithCreateDocumentsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/create-operations.md b/docs/examples/1.8.x/server-go/examples/databases/create-operations.md new file mode 100644 index 0000000000..c73ad57738 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/databases/create-operations.md @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/databases" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := databases.New(client) + +response, error := service.CreateOperations( + "", + databases.WithCreateOperationsOperations(interface{}{ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + }), +) diff --git a/docs/examples/1.8.x/server-go/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-go/examples/databases/create-transaction.md new file mode 100644 index 0000000000..f74b9152c1 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/databases/create-transaction.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/databases" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := databases.New(client) + +response, error := service.CreateTransaction( + databases.WithCreateTransactionTtl(60), +) diff --git a/docs/examples/1.8.x/server-go/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-go/examples/databases/decrement-document-attribute.md index af463743b5..7cb2c1c3c3 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-go/examples/databases/decrement-document-attribute.md @@ -21,4 +21,5 @@ response, error := service.DecrementDocumentAttribute( "", databases.WithDecrementDocumentAttributeValue(0), databases.WithDecrementDocumentAttributeMin(0), + databases.WithDecrementDocumentAttributeTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/delete-document.md b/docs/examples/1.8.x/server-go/examples/databases/delete-document.md index 6e9b58a56d..80e44b89eb 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-go/examples/databases/delete-document.md @@ -18,4 +18,5 @@ response, error := service.DeleteDocument( "", "", "", + databases.WithDeleteDocumentTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-go/examples/databases/delete-documents.md index 43e0d4cb36..5c070a0dc4 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-go/examples/databases/delete-documents.md @@ -18,4 +18,5 @@ response, error := service.DeleteDocuments( "", "", databases.WithDeleteDocumentsQueries([]interface{}{}), + databases.WithDeleteDocumentsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-go/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..b06f8bf6f3 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/databases/delete-transaction.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/databases" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := databases.New(client) + +response, error := service.DeleteTransaction( + "", +) diff --git a/docs/examples/1.8.x/server-go/examples/databases/get-document.md b/docs/examples/1.8.x/server-go/examples/databases/get-document.md index 5e63077aac..e075084f61 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-go/examples/databases/get-document.md @@ -19,4 +19,5 @@ response, error := service.GetDocument( "", "", databases.WithGetDocumentQueries([]interface{}{}), + databases.WithGetDocumentTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-go/examples/databases/get-transaction.md new file mode 100644 index 0000000000..4c15d73231 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/databases/get-transaction.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/databases" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := databases.New(client) + +response, error := service.GetTransaction( + "", +) diff --git a/docs/examples/1.8.x/server-go/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-go/examples/databases/increment-document-attribute.md index d2e91ee4c1..510d6486b8 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-go/examples/databases/increment-document-attribute.md @@ -21,4 +21,5 @@ response, error := service.IncrementDocumentAttribute( "", databases.WithIncrementDocumentAttributeValue(0), databases.WithIncrementDocumentAttributeMax(0), + databases.WithIncrementDocumentAttributeTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/list-documents.md b/docs/examples/1.8.x/server-go/examples/databases/list-documents.md index 0aaef36c59..3d83145a61 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-go/examples/databases/list-documents.md @@ -18,4 +18,5 @@ response, error := service.ListDocuments( "", "", databases.WithListDocumentsQueries([]interface{}{}), + databases.WithListDocumentsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-go/examples/databases/list-transactions.md new file mode 100644 index 0000000000..662874bb8c --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/databases/list-transactions.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/databases" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := databases.New(client) + +response, error := service.ListTransactions( + databases.WithListTransactionsQueries([]interface{}{}), +) diff --git a/docs/examples/1.8.x/server-go/examples/databases/update-document.md b/docs/examples/1.8.x/server-go/examples/databases/update-document.md index 90c0947536..314385d6a8 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-go/examples/databases/update-document.md @@ -20,4 +20,5 @@ response, error := service.UpdateDocument( "", databases.WithUpdateDocumentData(map[string]interface{}{}), databases.WithUpdateDocumentPermissions(interface{}{"read("any")"}), + databases.WithUpdateDocumentTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/update-documents.md b/docs/examples/1.8.x/server-go/examples/databases/update-documents.md index 7caee918e4..729656affd 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-go/examples/databases/update-documents.md @@ -19,4 +19,5 @@ response, error := service.UpdateDocuments( "", databases.WithUpdateDocumentsData(map[string]interface{}{}), databases.WithUpdateDocumentsQueries([]interface{}{}), + databases.WithUpdateDocumentsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-go/examples/databases/update-transaction.md new file mode 100644 index 0000000000..76ef4acb72 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/databases/update-transaction.md @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/databases" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := databases.New(client) + +response, error := service.UpdateTransaction( + "", + databases.WithUpdateTransactionCommit(false), + databases.WithUpdateTransactionRollback(false), +) diff --git a/docs/examples/1.8.x/server-go/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-go/examples/databases/upsert-document.md index 00cf8ad408..471c39185b 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-go/examples/databases/upsert-document.md @@ -20,4 +20,5 @@ response, error := service.UpsertDocument( "", map[string]interface{}{}, databases.WithUpsertDocumentPermissions(interface{}{"read("any")"}), + databases.WithUpsertDocumentTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-go/examples/databases/upsert-documents.md index a81ee4446e..9088883b1f 100644 --- a/docs/examples/1.8.x/server-go/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-go/examples/databases/upsert-documents.md @@ -18,4 +18,5 @@ response, error := service.UpsertDocuments( "", "", []interface{}{}, + databases.WithUpsertDocumentsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/messaging/create-push.md b/docs/examples/1.8.x/server-go/examples/messaging/create-push.md index fe2371bacd..a607f4391b 100644 --- a/docs/examples/1.8.x/server-go/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-go/examples/messaging/create-push.md @@ -23,7 +23,7 @@ response, error := service.CreatePush( messaging.WithCreatePushTargets([]interface{}{}), messaging.WithCreatePushData(map[string]interface{}{}), messaging.WithCreatePushAction(""), - messaging.WithCreatePushImage("[ID1:ID2]"), + messaging.WithCreatePushImage(""), messaging.WithCreatePushIcon(""), messaging.WithCreatePushSound(""), messaging.WithCreatePushColor(""), diff --git a/docs/examples/1.8.x/server-go/examples/messaging/update-push.md b/docs/examples/1.8.x/server-go/examples/messaging/update-push.md index 190627fa43..d364159e35 100644 --- a/docs/examples/1.8.x/server-go/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-go/examples/messaging/update-push.md @@ -23,7 +23,7 @@ response, error := service.UpdatePush( messaging.WithUpdatePushBody(""), messaging.WithUpdatePushData(map[string]interface{}{}), messaging.WithUpdatePushAction(""), - messaging.WithUpdatePushImage("[ID1:ID2]"), + messaging.WithUpdatePushImage(""), messaging.WithUpdatePushIcon(""), messaging.WithUpdatePushSound(""), messaging.WithUpdatePushColor(""), diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-go/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..330ece2bb9 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/create-operations.md @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := tablesdb.New(client) + +response, error := service.CreateOperations( + "", + tablesdb.WithCreateOperationsOperations(interface{}{ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + }), +) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-go/examples/tablesdb/create-row.md index 596f11cf75..24054ace1d 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/create-row.md @@ -26,4 +26,5 @@ response, error := service.CreateRow( "isAdmin": false }, tablesdb.WithCreateRowPermissions(interface{}{"read("any")"}), + tablesdb.WithCreateRowTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-go/examples/tablesdb/create-rows.md index 2d83be06df..6ddeb067db 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/create-rows.md @@ -18,4 +18,5 @@ response, error := service.CreateRows( "", "", []interface{}{}, + tablesdb.WithCreateRowsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-go/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..165f897cf8 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/create-transaction.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := tablesdb.New(client) + +response, error := service.CreateTransaction( + tablesdb.WithCreateTransactionTtl(60), +) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-go/examples/tablesdb/decrement-row-column.md index cd8d6c8983..a74bdda219 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/decrement-row-column.md @@ -21,4 +21,5 @@ response, error := service.DecrementRowColumn( "", tablesdb.WithDecrementRowColumnValue(0), tablesdb.WithDecrementRowColumnMin(0), + tablesdb.WithDecrementRowColumnTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-go/examples/tablesdb/delete-row.md index 8571a54c6c..39338452bc 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/delete-row.md @@ -18,4 +18,5 @@ response, error := service.DeleteRow( "", "", "", + tablesdb.WithDeleteRowTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-go/examples/tablesdb/delete-rows.md index 364a4c942f..b9fa49b5fb 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/delete-rows.md @@ -18,4 +18,5 @@ response, error := service.DeleteRows( "", "", tablesdb.WithDeleteRowsQueries([]interface{}{}), + tablesdb.WithDeleteRowsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-go/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..16ee050534 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/delete-transaction.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := tablesdb.New(client) + +response, error := service.DeleteTransaction( + "", +) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-go/examples/tablesdb/get-row.md index 3a555f07c5..025c6b55a1 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/get-row.md @@ -19,4 +19,5 @@ response, error := service.GetRow( "", "", tablesdb.WithGetRowQueries([]interface{}{}), + tablesdb.WithGetRowTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-go/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..d478007b62 --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/get-transaction.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := tablesdb.New(client) + +response, error := service.GetTransaction( + "", +) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-go/examples/tablesdb/increment-row-column.md index d072099cc6..4548f3cbb1 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/increment-row-column.md @@ -21,4 +21,5 @@ response, error := service.IncrementRowColumn( "", tablesdb.WithIncrementRowColumnValue(0), tablesdb.WithIncrementRowColumnMax(0), + tablesdb.WithIncrementRowColumnTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-go/examples/tablesdb/list-rows.md index 34a7b6fb98..784fbc81d9 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/list-rows.md @@ -18,4 +18,5 @@ response, error := service.ListRows( "", "", tablesdb.WithListRowsQueries([]interface{}{}), + tablesdb.WithListRowsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-go/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..7379d8555e --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/list-transactions.md @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := tablesdb.New(client) + +response, error := service.ListTransactions( + tablesdb.WithListTransactionsQueries([]interface{}{}), +) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-go/examples/tablesdb/update-row.md index 1e819bb589..12ea0b10a4 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/update-row.md @@ -20,4 +20,5 @@ response, error := service.UpdateRow( "", tablesdb.WithUpdateRowData(map[string]interface{}{}), tablesdb.WithUpdateRowPermissions(interface{}{"read("any")"}), + tablesdb.WithUpdateRowTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-go/examples/tablesdb/update-rows.md index 3541dce134..ff6a81e57c 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/update-rows.md @@ -19,4 +19,5 @@ response, error := service.UpdateRows( "", tablesdb.WithUpdateRowsData(map[string]interface{}{}), tablesdb.WithUpdateRowsQueries([]interface{}{}), + tablesdb.WithUpdateRowsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-go/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..9842a4555c --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/update-transaction.md @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +client := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1") + client.WithProject("") + client.WithKey("") +) + +service := tablesdb.New(client) + +response, error := service.UpdateTransaction( + "", + tablesdb.WithUpdateTransactionCommit(false), + tablesdb.WithUpdateTransactionRollback(false), +) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-row.md index 9fec778142..4caa5415e9 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-row.md @@ -20,4 +20,5 @@ response, error := service.UpsertRow( "", tablesdb.WithUpsertRowData(map[string]interface{}{}), tablesdb.WithUpsertRowPermissions(interface{}{"read("any")"}), + tablesdb.WithUpsertRowTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-rows.md index 5ded736cd0..a74d6ee9b1 100644 --- a/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-go/examples/tablesdb/upsert-rows.md @@ -18,4 +18,5 @@ response, error := service.UpsertRows( "", "", []interface{}{}, + tablesdb.WithUpsertRowsTransactionId(""), ) diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/create-document.md b/docs/examples/1.8.x/server-graphql/examples/databases/create-document.md index 39e4bba1cb..411615f7a7 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/create-document.md @@ -4,7 +4,8 @@ mutation { collectionId: "", documentId: "", data: "{\"username\":\"walter.obrien\",\"email\":\"walter.obrien@example.com\",\"fullName\":\"Walter O'Brien\",\"age\":30,\"isAdmin\":false}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/create-documents.md b/docs/examples/1.8.x/server-graphql/examples/databases/create-documents.md index 8ce79dcbb5..01a8914d0e 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/create-documents.md @@ -2,7 +2,8 @@ mutation { databasesCreateDocuments( databaseId: "", collectionId: "", - documents: [] + documents: [], + transactionId: "" ) { total documents { diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/create-operations.md b/docs/examples/1.8.x/server-graphql/examples/databases/create-operations.md new file mode 100644 index 0000000000..1be3b39ee1 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/databases/create-operations.md @@ -0,0 +1,23 @@ +mutation { + databasesCreateOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-graphql/examples/databases/create-transaction.md new file mode 100644 index 0000000000..7fea034ab6 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/databases/create-transaction.md @@ -0,0 +1,12 @@ +mutation { + databasesCreateTransaction( + ttl: 60 + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-graphql/examples/databases/decrement-document-attribute.md index 2e7970049d..e6032fd0e7 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/decrement-document-attribute.md @@ -5,7 +5,8 @@ mutation { documentId: "", attribute: "", value: 0, - min: 0 + min: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/delete-document.md b/docs/examples/1.8.x/server-graphql/examples/databases/delete-document.md index 848371bca0..2e172aa5dd 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/delete-document.md @@ -2,7 +2,8 @@ mutation { databasesDeleteDocument( databaseId: "", collectionId: "", - documentId: "" + documentId: "", + transactionId: "" ) { status } diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-graphql/examples/databases/delete-documents.md index 5822d3b950..aed5f6333f 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/delete-documents.md @@ -2,7 +2,8 @@ mutation { databasesDeleteDocuments( databaseId: "", collectionId: "", - queries: [] + queries: [], + transactionId: "" ) { total documents { diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-graphql/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..cd29a0b8a6 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/databases/delete-transaction.md @@ -0,0 +1,7 @@ +mutation { + databasesDeleteTransaction( + transactionId: "" + ) { + status + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-graphql/examples/databases/get-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-graphql/examples/databases/increment-document-attribute.md index 322ed69ced..3518ff1583 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/increment-document-attribute.md @@ -5,7 +5,8 @@ mutation { documentId: "", attribute: "", value: 0, - max: 0 + max: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-graphql/examples/databases/list-transactions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/update-document.md b/docs/examples/1.8.x/server-graphql/examples/databases/update-document.md index aea605d9d7..cf43d9eed0 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/update-document.md @@ -4,7 +4,8 @@ mutation { collectionId: "", documentId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/update-documents.md b/docs/examples/1.8.x/server-graphql/examples/databases/update-documents.md index 83c0c07f84..d6eb18de2a 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/update-documents.md @@ -3,7 +3,8 @@ mutation { databaseId: "", collectionId: "", data: "{}", - queries: [] + queries: [], + transactionId: "" ) { total documents { diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-graphql/examples/databases/update-transaction.md new file mode 100644 index 0000000000..b56c7139ac --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/databases/update-transaction.md @@ -0,0 +1,14 @@ +mutation { + databasesUpdateTransaction( + transactionId: "", + commit: false, + rollback: false + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-graphql/examples/databases/upsert-document.md index 9d1e753081..d487c0d303 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/upsert-document.md @@ -4,7 +4,8 @@ mutation { collectionId: "", documentId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-graphql/examples/databases/upsert-documents.md index 2bfb765915..7852ba93f8 100644 --- a/docs/examples/1.8.x/server-graphql/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-graphql/examples/databases/upsert-documents.md @@ -2,7 +2,8 @@ mutation { databasesUpsertDocuments( databaseId: "", collectionId: "", - documents: [] + documents: [], + transactionId: "" ) { total documents { diff --git a/docs/examples/1.8.x/server-graphql/examples/messaging/create-push.md b/docs/examples/1.8.x/server-graphql/examples/messaging/create-push.md index 92264d1b67..dc924dfd32 100644 --- a/docs/examples/1.8.x/server-graphql/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-graphql/examples/messaging/create-push.md @@ -8,7 +8,7 @@ mutation { targets: [], data: "{}", action: "", - image: "[ID1:ID2]", + image: "", icon: "", sound: "", color: "", diff --git a/docs/examples/1.8.x/server-graphql/examples/messaging/update-push.md b/docs/examples/1.8.x/server-graphql/examples/messaging/update-push.md index 8ee2f57610..3436e0cf2f 100644 --- a/docs/examples/1.8.x/server-graphql/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-graphql/examples/messaging/update-push.md @@ -8,7 +8,7 @@ mutation { body: "", data: "{}", action: "", - image: "[ID1:ID2]", + image: "", icon: "", sound: "", color: "", diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..bb2be8085a --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-operations.md @@ -0,0 +1,23 @@ +mutation { + tablesDBCreateOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-row.md index c7d2ec7d03..109bc008d6 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-row.md @@ -4,7 +4,8 @@ mutation { tableId: "", rowId: "", data: "{\"username\":\"walter.obrien\",\"email\":\"walter.obrien@example.com\",\"fullName\":\"Walter O'Brien\",\"age\":30,\"isAdmin\":false}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-rows.md index 25dc9a367d..9364b5676d 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-rows.md @@ -2,7 +2,8 @@ mutation { tablesDBCreateRows( databaseId: "", tableId: "", - rows: [] + rows: [], + transactionId: "" ) { total rows { diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..0e874f0c78 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/create-transaction.md @@ -0,0 +1,12 @@ +mutation { + tablesDBCreateTransaction( + ttl: 60 + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/decrement-row-column.md index 398ec19901..1d57d79b54 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/decrement-row-column.md @@ -5,7 +5,8 @@ mutation { rowId: "", column: "", value: 0, - min: 0 + min: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-row.md index 1a08b0f60d..3b44913049 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-row.md @@ -2,7 +2,8 @@ mutation { tablesDBDeleteRow( databaseId: "", tableId: "", - rowId: "" + rowId: "", + transactionId: "" ) { status } diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-rows.md index dfa7c13779..9dae8fc679 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-rows.md @@ -2,7 +2,8 @@ mutation { tablesDBDeleteRows( databaseId: "", tableId: "", - queries: [] + queries: [], + transactionId: "" ) { total rows { diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..4a2d6f15a2 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/delete-transaction.md @@ -0,0 +1,7 @@ +mutation { + tablesDBDeleteTransaction( + transactionId: "" + ) { + status + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/increment-row-column.md index b7ff87f387..3ae008e718 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/increment-row-column.md @@ -5,7 +5,8 @@ mutation { rowId: "", column: "", value: 0, - max: 0 + max: 0, + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-row.md index 5a5b288ab8..aa89e6ae01 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-row.md @@ -4,7 +4,8 @@ mutation { tableId: "", rowId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-rows.md index 4816748352..5a6203a4dc 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-rows.md @@ -3,7 +3,8 @@ mutation { databaseId: "", tableId: "", data: "{}", - queries: [] + queries: [], + transactionId: "" ) { total rows { diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..2094877303 --- /dev/null +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/update-transaction.md @@ -0,0 +1,14 @@ +mutation { + tablesDBUpdateTransaction( + transactionId: "", + commit: false, + rollback: false + ) { + _id + _createdAt + _updatedAt + status + operations + expiresAt + } +} diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-row.md index cc3b63de4a..3fe36ee7f1 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-row.md @@ -4,7 +4,8 @@ mutation { tableId: "", rowId: "", data: "{}", - permissions: ["read("any")"] + permissions: ["read("any")"], + transactionId: "" ) { _id _sequence diff --git a/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-rows.md index f4e01c0af7..bbfc09c763 100644 --- a/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-graphql/examples/tablesdb/upsert-rows.md @@ -2,7 +2,8 @@ mutation { tablesDBUpsertRows( databaseId: "", tableId: "", - rows: [] + rows: [], + transactionId: "" ) { total rows { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md index d5e777d157..9c6357dfae 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md @@ -21,6 +21,7 @@ databases.createDocument( "isAdmin" to false ), // data listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md index 0de0c276ed..3a4540974b 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md @@ -13,6 +13,7 @@ databases.createDocuments( "", // databaseId "", // collectionId listOf(), // documents + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md new file mode 100644 index 0000000000..2dad8a15ac --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md @@ -0,0 +1,34 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +Databases databases = new Databases(client); + +databases.createOperations( + "", // transactionId + listOf( + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ), // operations (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-transaction.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-transaction.md new file mode 100644 index 0000000000..5fb7c5936a --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-transaction.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +Databases databases = new Databases(client); + +databases.createTransaction( + 60, // ttl (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/decrement-document-attribute.md index a44cc51260..a852083bce 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/decrement-document-attribute.md @@ -16,6 +16,7 @@ databases.decrementDocumentAttribute( "", // attribute 0, // value (optional) 0, // min (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/delete-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/delete-document.md index f6e6209f36..2f7003b234 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/delete-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/delete-document.md @@ -13,6 +13,7 @@ databases.deleteDocument( "", // databaseId "", // collectionId "", // documentId + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md index e8394b1ff9..958c40c382 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md @@ -13,6 +13,7 @@ databases.deleteDocuments( "", // databaseId "", // collectionId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/delete-transaction.md b/docs/examples/1.8.x/server-kotlin/java/databases/delete-transaction.md new file mode 100644 index 0000000000..200bbbdc26 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/databases/delete-transaction.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +Databases databases = new Databases(client); + +databases.deleteTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md index 2719073a7d..489447f599 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md @@ -14,6 +14,7 @@ databases.getDocument( "", // collectionId "", // documentId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/get-transaction.md b/docs/examples/1.8.x/server-kotlin/java/databases/get-transaction.md new file mode 100644 index 0000000000..d896ca0751 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/databases/get-transaction.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +Databases databases = new Databases(client); + +databases.getTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/increment-document-attribute.md index b5b5054e25..be837d00f8 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/increment-document-attribute.md @@ -16,6 +16,7 @@ databases.incrementDocumentAttribute( "", // attribute 0, // value (optional) 0, // max (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md index 36982c0eb0..1e84348c1a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md @@ -13,6 +13,7 @@ databases.listDocuments( "", // databaseId "", // collectionId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md new file mode 100644 index 0000000000..281fc1205b --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +Databases databases = new Databases(client); + +databases.listTransactions( + listOf(), // queries (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md index f7b05c9601..f3019ab95b 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md @@ -15,6 +15,7 @@ databases.updateDocument( "", // documentId mapOf( "a" to "b" ), // data (optional) listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md index b4138b41d2..a685ac81fc 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md @@ -14,6 +14,7 @@ databases.updateDocuments( "", // collectionId mapOf( "a" to "b" ), // data (optional) listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-transaction.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-transaction.md new file mode 100644 index 0000000000..8479ed31aa --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-transaction.md @@ -0,0 +1,25 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Databases; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +Databases databases = new Databases(client); + +databases.updateTransaction( + "", // transactionId + false, // commit (optional) + false, // rollback (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md index daa44141e2..39864b9ac3 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md @@ -15,6 +15,7 @@ databases.upsertDocument( "", // documentId mapOf( "a" to "b" ), // data listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md index 95e9a33ef2..b8fcd8781a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md @@ -13,6 +13,7 @@ databases.upsertDocuments( "", // databaseId "", // collectionId listOf(), // documents + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md b/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md index 277ab9655c..14e8ca2679 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md @@ -18,7 +18,7 @@ messaging.createPush( listOf(), // targets (optional) mapOf( "a" to "b" ), // data (optional) "", // action (optional) - "[ID1:ID2]", // image (optional) + "", // image (optional) "", // icon (optional) "", // sound (optional) "", // color (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md b/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md index b7038de6a4..ce56683674 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md @@ -18,7 +18,7 @@ messaging.updatePush( "", // body (optional) mapOf( "a" to "b" ), // data (optional) "", // action (optional) - "[ID1:ID2]", // image (optional) + "", // image (optional) "", // icon (optional) "", // sound (optional) "", // color (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md new file mode 100644 index 0000000000..9504f623b3 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md @@ -0,0 +1,34 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.createOperations( + "", // transactionId + listOf( + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ), // operations (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md index 6c7d84702d..d041511c11 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md @@ -21,6 +21,7 @@ tablesDB.createRow( "isAdmin" to false ), // data listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md index 21bdd21879..956d812165 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md @@ -13,6 +13,7 @@ tablesDB.createRows( "", // databaseId "", // tableId listOf(), // rows + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-transaction.md new file mode 100644 index 0000000000..3529956c54 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-transaction.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.createTransaction( + 60, // ttl (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/decrement-row-column.md index b9f250f48f..78a811676d 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/decrement-row-column.md @@ -16,6 +16,7 @@ tablesDB.decrementRowColumn( "", // column 0, // value (optional) 0, // min (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-row.md index fd66525a8d..5da1ba0cf3 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-row.md @@ -13,6 +13,7 @@ tablesDB.deleteRow( "", // databaseId "", // tableId "", // rowId + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md index 43b772e1a3..80ca0bb40f 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md @@ -13,6 +13,7 @@ tablesDB.deleteRows( "", // databaseId "", // tableId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..816b6e5dee --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-transaction.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.deleteTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md index 03cf3fb234..d642ebcaf1 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md @@ -14,6 +14,7 @@ tablesDB.getRow( "", // tableId "", // rowId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-transaction.md new file mode 100644 index 0000000000..dab07dce4e --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-transaction.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.getTransaction( + "", // transactionId + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/increment-row-column.md index db48d05c37..33715721a8 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/increment-row-column.md @@ -16,6 +16,7 @@ tablesDB.incrementRowColumn( "", // column 0, // value (optional) 0, // max (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md index 52cf2a1670..96520e2bc5 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md @@ -13,6 +13,7 @@ tablesDB.listRows( "", // databaseId "", // tableId listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md new file mode 100644 index 0000000000..acc4902da4 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md @@ -0,0 +1,23 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.listTransactions( + listOf(), // queries (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md index bedc816f14..b4f9631222 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md @@ -15,6 +15,7 @@ tablesDB.updateRow( "", // rowId mapOf( "a" to "b" ), // data (optional) listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md index 169b57c3e5..7d39e4422c 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md @@ -14,6 +14,7 @@ tablesDB.updateRows( "", // tableId mapOf( "a" to "b" ), // data (optional) listOf(), // queries (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-transaction.md new file mode 100644 index 0000000000..f043d5dc44 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-transaction.md @@ -0,0 +1,25 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.updateTransaction( + "", // transactionId + false, // commit (optional) + false, // rollback (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md index d6155fcd1b..b6a986ef4d 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md @@ -15,6 +15,7 @@ tablesDB.upsertRow( "", // rowId mapOf( "a" to "b" ), // data (optional) listOf("read("any")"), // permissions (optional) + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md index da15f6a0db..c4b2bf3857 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md @@ -13,6 +13,7 @@ tablesDB.upsertRows( "", // databaseId "", // tableId listOf(), // rows + "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-document.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-document.md index 1c1d628729..46cb711b62 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-document.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-document.md @@ -20,5 +20,6 @@ val response = databases.createDocument( "age" to 30, "isAdmin" to false ), - permissions = listOf("read("any")") // optional + permissions = listOf("read("any")"), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-documents.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-documents.md index 41a98dc016..114d5cc707 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-documents.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-documents.md @@ -12,5 +12,6 @@ val databases = Databases(client) val response = databases.createDocuments( databaseId = "", collectionId = "", - documents = listOf() + documents = listOf(), + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md new file mode 100644 index 0000000000..1c741818b9 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md @@ -0,0 +1,25 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val databases = Databases(client) + +val response = databases.createOperations( + transactionId = "", + operations = listOf( + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ) // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-transaction.md new file mode 100644 index 0000000000..83ff583038 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-transaction.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val databases = Databases(client) + +val response = databases.createTransaction( + ttl = 60 // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/decrement-document-attribute.md index d0226c0bdb..3ccd662d59 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/decrement-document-attribute.md @@ -15,5 +15,6 @@ val response = databases.decrementDocumentAttribute( documentId = "", attribute = "", value = 0, // optional - min = 0 // optional + min = 0, // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-document.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-document.md index a9eea6b648..3be4372987 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-document.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-document.md @@ -12,5 +12,6 @@ val databases = Databases(client) val response = databases.deleteDocument( databaseId = "", collectionId = "", - documentId = "" + documentId = "", + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-documents.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-documents.md index c4caa63aae..9b9ea263c4 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-documents.md @@ -12,5 +12,6 @@ val databases = Databases(client) val response = databases.deleteDocuments( databaseId = "", collectionId = "", - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-transaction.md new file mode 100644 index 0000000000..ef11e9fad8 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/delete-transaction.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val databases = Databases(client) + +val response = databases.deleteTransaction( + transactionId = "" +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-document.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-document.md index d21a19869b..98855d0984 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-document.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-document.md @@ -13,5 +13,6 @@ val response = databases.getDocument( databaseId = "", collectionId = "", documentId = "", - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-transaction.md new file mode 100644 index 0000000000..1e44376ab7 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/get-transaction.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val databases = Databases(client) + +val response = databases.getTransaction( + transactionId = "" +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/increment-document-attribute.md index b56ed91d75..fb358868d2 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/increment-document-attribute.md @@ -15,5 +15,6 @@ val response = databases.incrementDocumentAttribute( documentId = "", attribute = "", value = 0, // optional - max = 0 // optional + max = 0, // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-documents.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-documents.md index ed9cb3165d..ab75493964 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-documents.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-documents.md @@ -12,5 +12,6 @@ val databases = Databases(client) val response = databases.listDocuments( databaseId = "", collectionId = "", - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-transactions.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-transactions.md new file mode 100644 index 0000000000..0d122b108d --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/list-transactions.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val databases = Databases(client) + +val response = databases.listTransactions( + queries = listOf() // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-document.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-document.md index 4dd0349823..c64a705676 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-document.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-document.md @@ -14,5 +14,6 @@ val response = databases.updateDocument( collectionId = "", documentId = "", data = mapOf( "a" to "b" ), // optional - permissions = listOf("read("any")") // optional + permissions = listOf("read("any")"), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-documents.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-documents.md index 9d6c2b5ea8..b5b76fcaee 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-documents.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-documents.md @@ -13,5 +13,6 @@ val response = databases.updateDocuments( databaseId = "", collectionId = "", data = mapOf( "a" to "b" ), // optional - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-transaction.md new file mode 100644 index 0000000000..834d0dc78d --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-transaction.md @@ -0,0 +1,16 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Databases + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val databases = Databases(client) + +val response = databases.updateTransaction( + transactionId = "", + commit = false, // optional + rollback = false // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-document.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-document.md index d8be0e13db..d6d6800864 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-document.md @@ -14,5 +14,6 @@ val response = databases.upsertDocument( collectionId = "", documentId = "", data = mapOf( "a" to "b" ), - permissions = listOf("read("any")") // optional + permissions = listOf("read("any")"), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-documents.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-documents.md index ca861c61b2..db9e2b3e2d 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/upsert-documents.md @@ -12,5 +12,6 @@ val databases = Databases(client) val response = databases.upsertDocuments( databaseId = "", collectionId = "", - documents = listOf() + documents = listOf(), + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/messaging/create-push.md b/docs/examples/1.8.x/server-kotlin/kotlin/messaging/create-push.md index 5b07f5355b..d0d765a32a 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/messaging/create-push.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/messaging/create-push.md @@ -18,7 +18,7 @@ val response = messaging.createPush( targets = listOf(), // optional data = mapOf( "a" to "b" ), // optional action = "", // optional - image = "[ID1:ID2]", // optional + image = "", // optional icon = "", // optional sound = "", // optional color = "", // optional diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/messaging/update-push.md b/docs/examples/1.8.x/server-kotlin/kotlin/messaging/update-push.md index 710a37e518..0b9c4c6535 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/messaging/update-push.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/messaging/update-push.md @@ -18,7 +18,7 @@ val response = messaging.updatePush( body = "", // optional data = mapOf( "a" to "b" ), // optional action = "", // optional - image = "[ID1:ID2]", // optional + image = "", // optional icon = "", // optional sound = "", // optional color = "", // optional diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md new file mode 100644 index 0000000000..40c98d1c81 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md @@ -0,0 +1,25 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.createOperations( + transactionId = "", + operations = listOf( + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ) // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-row.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-row.md index 774800d8f4..b06038964b 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-row.md @@ -20,5 +20,6 @@ val response = tablesDB.createRow( "age" to 30, "isAdmin" to false ), - permissions = listOf("read("any")") // optional + permissions = listOf("read("any")"), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-rows.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-rows.md index 1da47b5c18..8cef6028a2 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-rows.md @@ -12,5 +12,6 @@ val tablesDB = TablesDB(client) val response = tablesDB.createRows( databaseId = "", tableId = "", - rows = listOf() + rows = listOf(), + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-transaction.md new file mode 100644 index 0000000000..31385700c5 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-transaction.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.createTransaction( + ttl = 60 // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/decrement-row-column.md index e284ec3980..30a8d54b1a 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/decrement-row-column.md @@ -15,5 +15,6 @@ val response = tablesDB.decrementRowColumn( rowId = "", column = "", value = 0, // optional - min = 0 // optional + min = 0, // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-row.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-row.md index f24b9353e0..6ba7d6057c 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-row.md @@ -12,5 +12,6 @@ val tablesDB = TablesDB(client) val response = tablesDB.deleteRow( databaseId = "", tableId = "", - rowId = "" + rowId = "", + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-rows.md index c915a5c55a..da2b709f8a 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-rows.md @@ -12,5 +12,6 @@ val tablesDB = TablesDB(client) val response = tablesDB.deleteRows( databaseId = "", tableId = "", - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..2ef1f27a25 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/delete-transaction.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.deleteTransaction( + transactionId = "" +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-row.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-row.md index ec54631646..f92a1ccf27 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-row.md @@ -13,5 +13,6 @@ val response = tablesDB.getRow( databaseId = "", tableId = "", rowId = "", - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-transaction.md new file mode 100644 index 0000000000..f4467dc914 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/get-transaction.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.getTransaction( + transactionId = "" +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/increment-row-column.md index cac151e41b..af3676e75b 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/increment-row-column.md @@ -15,5 +15,6 @@ val response = tablesDB.incrementRowColumn( rowId = "", column = "", value = 0, // optional - max = 0 // optional + max = 0, // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-rows.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-rows.md index b0f5df476b..711e4e1a31 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-rows.md @@ -12,5 +12,6 @@ val tablesDB = TablesDB(client) val response = tablesDB.listRows( databaseId = "", tableId = "", - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-transactions.md new file mode 100644 index 0000000000..a060b9fac3 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/list-transactions.md @@ -0,0 +1,14 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.listTransactions( + queries = listOf() // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-row.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-row.md index 9c5248f4e8..0fefb78e5f 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-row.md @@ -14,5 +14,6 @@ val response = tablesDB.updateRow( tableId = "", rowId = "", data = mapOf( "a" to "b" ), // optional - permissions = listOf("read("any")") // optional + permissions = listOf("read("any")"), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-rows.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-rows.md index c285d5b4fb..61041a7783 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-rows.md @@ -13,5 +13,6 @@ val response = tablesDB.updateRows( databaseId = "", tableId = "", data = mapOf( "a" to "b" ), // optional - queries = listOf() // optional + queries = listOf(), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-transaction.md new file mode 100644 index 0000000000..a3797ca5ca --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-transaction.md @@ -0,0 +1,16 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.updateTransaction( + transactionId = "", + commit = false, // optional + rollback = false // optional +) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-row.md index 3fcbc61617..5bcc73b4b1 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-row.md @@ -14,5 +14,6 @@ val response = tablesDB.upsertRow( tableId = "", rowId = "", data = mapOf( "a" to "b" ), // optional - permissions = listOf("read("any")") // optional + permissions = listOf("read("any")"), // optional + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-rows.md index 7059c6018b..2f08375c4a 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/upsert-rows.md @@ -12,5 +12,6 @@ val tablesDB = TablesDB(client) val response = tablesDB.upsertRows( databaseId = "", tableId = "", - rows = listOf() + rows = listOf(), + transactionId = "" // optional ) diff --git a/docs/examples/1.8.x/server-php/examples/databases/create-document.md b/docs/examples/1.8.x/server-php/examples/databases/create-document.md index 9f2e8f3643..19d3cfb566 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-php/examples/databases/create-document.md @@ -21,5 +21,6 @@ $result = $databases->createDocument( 'age' => 30, 'isAdmin' => false ], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/create-documents.md b/docs/examples/1.8.x/server-php/examples/databases/create-documents.md index bc05f67260..ced7a5a83f 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-php/examples/databases/create-documents.md @@ -13,5 +13,6 @@ $databases = new Databases($client); $result = $databases->createDocuments( databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/create-operations.md b/docs/examples/1.8.x/server-php/examples/databases/create-operations.md new file mode 100644 index 0000000000..05038cb6f6 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/databases/create-operations.md @@ -0,0 +1,26 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$databases = new Databases($client); + +$result = $databases->createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-php/examples/databases/create-transaction.md new file mode 100644 index 0000000000..ea6faf73b1 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/databases/create-transaction.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$databases = new Databases($client); + +$result = $databases->createTransaction( + ttl: 60 // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-php/examples/databases/decrement-document-attribute.md index 6464a26818..dfb1873cdd 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-php/examples/databases/decrement-document-attribute.md @@ -16,5 +16,6 @@ $result = $databases->decrementDocumentAttribute( documentId: '', attribute: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/delete-document.md b/docs/examples/1.8.x/server-php/examples/databases/delete-document.md index def7f24569..6e4d7aa2a6 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-php/examples/databases/delete-document.md @@ -13,5 +13,6 @@ $databases = new Databases($client); $result = $databases->deleteDocument( databaseId: '', collectionId: '', - documentId: '' + documentId: '', + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-php/examples/databases/delete-documents.md index 3552d85317..3b2b0c79c5 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-php/examples/databases/delete-documents.md @@ -13,5 +13,6 @@ $databases = new Databases($client); $result = $databases->deleteDocuments( databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-php/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..0559aace08 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/databases/delete-transaction.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$databases = new Databases($client); + +$result = $databases->deleteTransaction( + transactionId: '' +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/get-document.md b/docs/examples/1.8.x/server-php/examples/databases/get-document.md index a3204c50a7..834602d89f 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-php/examples/databases/get-document.md @@ -14,5 +14,6 @@ $result = $databases->getDocument( databaseId: '', collectionId: '', documentId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-php/examples/databases/get-transaction.md new file mode 100644 index 0000000000..16ca28da1a --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/databases/get-transaction.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$databases = new Databases($client); + +$result = $databases->getTransaction( + transactionId: '' +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-php/examples/databases/increment-document-attribute.md index 9ad4bdfdec..63162d3224 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-php/examples/databases/increment-document-attribute.md @@ -16,5 +16,6 @@ $result = $databases->incrementDocumentAttribute( documentId: '', attribute: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/list-documents.md b/docs/examples/1.8.x/server-php/examples/databases/list-documents.md index 07183ac8bf..10dcc82340 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-php/examples/databases/list-documents.md @@ -13,5 +13,6 @@ $databases = new Databases($client); $result = $databases->listDocuments( databaseId: '', collectionId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-php/examples/databases/list-transactions.md new file mode 100644 index 0000000000..858e905ba5 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/databases/list-transactions.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$databases = new Databases($client); + +$result = $databases->listTransactions( + queries: [] // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/update-document.md b/docs/examples/1.8.x/server-php/examples/databases/update-document.md index f1c8a34680..d903252886 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-php/examples/databases/update-document.md @@ -15,5 +15,6 @@ $result = $databases->updateDocument( collectionId: '', documentId: '', data: [], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/update-documents.md b/docs/examples/1.8.x/server-php/examples/databases/update-documents.md index 51b4e18bc2..72632461a9 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-php/examples/databases/update-documents.md @@ -14,5 +14,6 @@ $result = $databases->updateDocuments( databaseId: '', collectionId: '', data: [], // optional - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-php/examples/databases/update-transaction.md new file mode 100644 index 0000000000..750eb861f9 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/databases/update-transaction.md @@ -0,0 +1,17 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$databases = new Databases($client); + +$result = $databases->updateTransaction( + transactionId: '', + commit: false, // optional + rollback: false // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-php/examples/databases/upsert-document.md index 6cff8296a3..6db7462ac7 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-php/examples/databases/upsert-document.md @@ -15,5 +15,6 @@ $result = $databases->upsertDocument( collectionId: '', documentId: '', data: [], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-php/examples/databases/upsert-documents.md index d9f9efda5c..06d3a319af 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-php/examples/databases/upsert-documents.md @@ -13,5 +13,6 @@ $databases = new Databases($client); $result = $databases->upsertDocuments( databaseId: '', collectionId: '', - documents: [] + documents: [], + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/messaging/create-push.md b/docs/examples/1.8.x/server-php/examples/messaging/create-push.md index 46aeeb3b8b..51fc0d0a92 100644 --- a/docs/examples/1.8.x/server-php/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-php/examples/messaging/create-push.md @@ -19,7 +19,7 @@ $result = $messaging->createPush( targets: [], // optional data: [], // optional action: '', // optional - image: '[ID1:ID2]', // optional + image: '', // optional icon: '', // optional sound: '', // optional color: '', // optional diff --git a/docs/examples/1.8.x/server-php/examples/messaging/update-push.md b/docs/examples/1.8.x/server-php/examples/messaging/update-push.md index e1df0b9132..05a51783c9 100644 --- a/docs/examples/1.8.x/server-php/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-php/examples/messaging/update-push.md @@ -19,7 +19,7 @@ $result = $messaging->updatePush( body: '', // optional data: [], // optional action: '', // optional - image: '[ID1:ID2]', // optional + image: '', // optional icon: '', // optional sound: '', // optional color: '', // optional diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-php/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..429a0bb546 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/create-operations.md @@ -0,0 +1,26 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-php/examples/tablesdb/create-row.md index fa5137b99e..873ecaf448 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/create-row.md @@ -21,5 +21,6 @@ $result = $tablesDB->createRow( 'age' => 30, 'isAdmin' => false ], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-php/examples/tablesdb/create-rows.md index 011443859f..44c9c7d140 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/create-rows.md @@ -13,5 +13,6 @@ $tablesDB = new TablesDB($client); $result = $tablesDB->createRows( databaseId: '', tableId: '', - rows: [] + rows: [], + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-php/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..32488185b1 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/create-transaction.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->createTransaction( + ttl: 60 // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-php/examples/tablesdb/decrement-row-column.md index a58bd71071..ede258e8bd 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/decrement-row-column.md @@ -16,5 +16,6 @@ $result = $tablesDB->decrementRowColumn( rowId: '', column: '', value: null, // optional - min: null // optional + min: null, // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-php/examples/tablesdb/delete-row.md index 4ffc112d66..df87c5077b 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/delete-row.md @@ -13,5 +13,6 @@ $tablesDB = new TablesDB($client); $result = $tablesDB->deleteRow( databaseId: '', tableId: '', - rowId: '' + rowId: '', + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-php/examples/tablesdb/delete-rows.md index 10a3c87ff2..79ed607c47 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/delete-rows.md @@ -13,5 +13,6 @@ $tablesDB = new TablesDB($client); $result = $tablesDB->deleteRows( databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-php/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..d1650158c9 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/delete-transaction.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->deleteTransaction( + transactionId: '' +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-php/examples/tablesdb/get-row.md index 00ba9b65b5..4bbea5594d 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/get-row.md @@ -14,5 +14,6 @@ $result = $tablesDB->getRow( databaseId: '', tableId: '', rowId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-php/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..146e7d191b --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/get-transaction.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->getTransaction( + transactionId: '' +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-php/examples/tablesdb/increment-row-column.md index d72a1e374f..66bf2e8489 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/increment-row-column.md @@ -16,5 +16,6 @@ $result = $tablesDB->incrementRowColumn( rowId: '', column: '', value: null, // optional - max: null // optional + max: null, // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-php/examples/tablesdb/list-rows.md index c3b713703e..5f8c9aa1ef 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/list-rows.md @@ -13,5 +13,6 @@ $tablesDB = new TablesDB($client); $result = $tablesDB->listRows( databaseId: '', tableId: '', - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-php/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..15095d6f0c --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/list-transactions.md @@ -0,0 +1,15 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->listTransactions( + queries: [] // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-php/examples/tablesdb/update-row.md index 70e5d159fd..c01eba8d57 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/update-row.md @@ -15,5 +15,6 @@ $result = $tablesDB->updateRow( tableId: '', rowId: '', data: [], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-php/examples/tablesdb/update-rows.md index 8a676289d2..681a9f0d8b 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/update-rows.md @@ -14,5 +14,6 @@ $result = $tablesDB->updateRows( databaseId: '', tableId: '', data: [], // optional - queries: [] // optional + queries: [], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-php/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..fed3810b5a --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/update-transaction.md @@ -0,0 +1,17 @@ +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->updateTransaction( + transactionId: '', + commit: false, // optional + rollback: false // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-row.md index 235f0e577b..bec3c0af92 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-row.md @@ -15,5 +15,6 @@ $result = $tablesDB->upsertRow( tableId: '', rowId: '', data: [], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-rows.md index c1890f1ea3..fb93df8bcd 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/upsert-rows.md @@ -13,5 +13,6 @@ $tablesDB = new TablesDB($client); $result = $tablesDB->upsertRows( databaseId: '', tableId: '', - rows: [] + rows: [], + transactionId: '' // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-python/examples/databases/create-document.md b/docs/examples/1.8.x/server-python/examples/databases/create-document.md index 3d7dee1a4f..f42a3d82b6 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-python/examples/databases/create-document.md @@ -19,5 +19,6 @@ result = databases.create_document( "age": 30, "isAdmin": False }, - permissions = ["read("any")"] # optional + permissions = ["read("any")"], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/create-documents.md b/docs/examples/1.8.x/server-python/examples/databases/create-documents.md index 1b94e5165a..97fa4c6840 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-python/examples/databases/create-documents.md @@ -11,5 +11,6 @@ databases = Databases(client) result = databases.create_documents( database_id = '', collection_id = '', - documents = [] + documents = [], + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/create-operations.md b/docs/examples/1.8.x/server-python/examples/databases/create-operations.md new file mode 100644 index 0000000000..d8fc1fa772 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/databases/create-operations.md @@ -0,0 +1,24 @@ +from appwrite.client import Client +from appwrite.services.databases import Databases + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +databases = Databases(client) + +result = databases.create_operations( + transaction_id = '', + operations = [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-python/examples/databases/create-transaction.md new file mode 100644 index 0000000000..a733b658f8 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/databases/create-transaction.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.databases import Databases + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +databases = Databases(client) + +result = databases.create_transaction( + ttl = 60 # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-python/examples/databases/decrement-document-attribute.md index 3efedf766e..09ed9fcb47 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-python/examples/databases/decrement-document-attribute.md @@ -14,5 +14,6 @@ result = databases.decrement_document_attribute( document_id = '', attribute = '', value = None, # optional - min = None # optional + min = None, # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/delete-document.md b/docs/examples/1.8.x/server-python/examples/databases/delete-document.md index 57f8b3bd9d..89d85853e4 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-python/examples/databases/delete-document.md @@ -11,5 +11,6 @@ databases = Databases(client) result = databases.delete_document( database_id = '', collection_id = '', - document_id = '' + document_id = '', + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-python/examples/databases/delete-documents.md index a315f0c200..63130fb05e 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-python/examples/databases/delete-documents.md @@ -11,5 +11,6 @@ databases = Databases(client) result = databases.delete_documents( database_id = '', collection_id = '', - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-python/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..fab1f2a586 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/databases/delete-transaction.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.databases import Databases + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +databases = Databases(client) + +result = databases.delete_transaction( + transaction_id = '' +) diff --git a/docs/examples/1.8.x/server-python/examples/databases/get-document.md b/docs/examples/1.8.x/server-python/examples/databases/get-document.md index aff5008fa0..6cd0bc2b95 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-python/examples/databases/get-document.md @@ -12,5 +12,6 @@ result = databases.get_document( database_id = '', collection_id = '', document_id = '', - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-python/examples/databases/get-transaction.md new file mode 100644 index 0000000000..2a89f3d83d --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/databases/get-transaction.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.databases import Databases + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +databases = Databases(client) + +result = databases.get_transaction( + transaction_id = '' +) diff --git a/docs/examples/1.8.x/server-python/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-python/examples/databases/increment-document-attribute.md index 9ae1cedfe4..3e85656c10 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-python/examples/databases/increment-document-attribute.md @@ -14,5 +14,6 @@ result = databases.increment_document_attribute( document_id = '', attribute = '', value = None, # optional - max = None # optional + max = None, # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/list-documents.md b/docs/examples/1.8.x/server-python/examples/databases/list-documents.md index 8b450cd020..cecac30585 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-python/examples/databases/list-documents.md @@ -11,5 +11,6 @@ databases = Databases(client) result = databases.list_documents( database_id = '', collection_id = '', - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-python/examples/databases/list-transactions.md new file mode 100644 index 0000000000..a410c96b05 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/databases/list-transactions.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.databases import Databases + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +databases = Databases(client) + +result = databases.list_transactions( + queries = [] # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/databases/update-document.md b/docs/examples/1.8.x/server-python/examples/databases/update-document.md index 9ef6527934..c9ef02f72e 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-python/examples/databases/update-document.md @@ -13,5 +13,6 @@ result = databases.update_document( collection_id = '', document_id = '', data = {}, # optional - permissions = ["read("any")"] # optional + permissions = ["read("any")"], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/update-documents.md b/docs/examples/1.8.x/server-python/examples/databases/update-documents.md index 5a50d1a912..2aab8c61c4 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-python/examples/databases/update-documents.md @@ -12,5 +12,6 @@ result = databases.update_documents( database_id = '', collection_id = '', data = {}, # optional - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-python/examples/databases/update-transaction.md new file mode 100644 index 0000000000..571f98c7ce --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/databases/update-transaction.md @@ -0,0 +1,15 @@ +from appwrite.client import Client +from appwrite.services.databases import Databases + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +databases = Databases(client) + +result = databases.update_transaction( + transaction_id = '', + commit = False, # optional + rollback = False # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-python/examples/databases/upsert-document.md index c491ea4f44..e1a2f44d8c 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-python/examples/databases/upsert-document.md @@ -13,5 +13,6 @@ result = databases.upsert_document( collection_id = '', document_id = '', data = {}, - permissions = ["read("any")"] # optional + permissions = ["read("any")"], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-python/examples/databases/upsert-documents.md index 5136d5fcb1..f0720e34c0 100644 --- a/docs/examples/1.8.x/server-python/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-python/examples/databases/upsert-documents.md @@ -11,5 +11,6 @@ databases = Databases(client) result = databases.upsert_documents( database_id = '', collection_id = '', - documents = [] + documents = [], + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/messaging/create-push.md b/docs/examples/1.8.x/server-python/examples/messaging/create-push.md index 8671b56a39..b706234227 100644 --- a/docs/examples/1.8.x/server-python/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-python/examples/messaging/create-push.md @@ -17,7 +17,7 @@ result = messaging.create_push( targets = [], # optional data = {}, # optional action = '', # optional - image = '[ID1:ID2]', # optional + image = '', # optional icon = '', # optional sound = '', # optional color = '', # optional diff --git a/docs/examples/1.8.x/server-python/examples/messaging/update-push.md b/docs/examples/1.8.x/server-python/examples/messaging/update-push.md index e3bb02e71f..ce5d39466f 100644 --- a/docs/examples/1.8.x/server-python/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-python/examples/messaging/update-push.md @@ -17,7 +17,7 @@ result = messaging.update_push( body = '', # optional data = {}, # optional action = '', # optional - image = '[ID1:ID2]', # optional + image = '', # optional icon = '', # optional sound = '', # optional color = '', # optional diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-python/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..a4881a9e8f --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/create-operations.md @@ -0,0 +1,24 @@ +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.create_operations( + transaction_id = '', + operations = [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-python/examples/tablesdb/create-row.md index d4c1cdad14..d2de58617f 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/create-row.md @@ -19,5 +19,6 @@ result = tables_db.create_row( "age": 30, "isAdmin": False }, - permissions = ["read("any")"] # optional + permissions = ["read("any")"], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-python/examples/tablesdb/create-rows.md index 656a47aa0b..1527e0b30d 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/create-rows.md @@ -11,5 +11,6 @@ tables_db = TablesDB(client) result = tables_db.create_rows( database_id = '', table_id = '', - rows = [] + rows = [], + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-python/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..05cc80eaa2 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/create-transaction.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.create_transaction( + ttl = 60 # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-python/examples/tablesdb/decrement-row-column.md index 096bc4dbaa..d207bb1b4d 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/decrement-row-column.md @@ -14,5 +14,6 @@ result = tables_db.decrement_row_column( row_id = '', column = '', value = None, # optional - min = None # optional + min = None, # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-python/examples/tablesdb/delete-row.md index 569b607020..3943ab27a5 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/delete-row.md @@ -11,5 +11,6 @@ tables_db = TablesDB(client) result = tables_db.delete_row( database_id = '', table_id = '', - row_id = '' + row_id = '', + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-python/examples/tablesdb/delete-rows.md index c3e836e7c6..290d6d346b 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/delete-rows.md @@ -11,5 +11,6 @@ tables_db = TablesDB(client) result = tables_db.delete_rows( database_id = '', table_id = '', - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-python/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..6d2957f3d6 --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/delete-transaction.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.delete_transaction( + transaction_id = '' +) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-python/examples/tablesdb/get-row.md index c806214297..4398c9a43d 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/get-row.md @@ -12,5 +12,6 @@ result = tables_db.get_row( database_id = '', table_id = '', row_id = '', - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-python/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..e50c63af9d --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/get-transaction.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.get_transaction( + transaction_id = '' +) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-python/examples/tablesdb/increment-row-column.md index bcb88f7a31..8e121f65f6 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/increment-row-column.md @@ -14,5 +14,6 @@ result = tables_db.increment_row_column( row_id = '', column = '', value = None, # optional - max = None # optional + max = None, # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-python/examples/tablesdb/list-rows.md index 9ae7549fb0..eb0a4ed1b3 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/list-rows.md @@ -11,5 +11,6 @@ tables_db = TablesDB(client) result = tables_db.list_rows( database_id = '', table_id = '', - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-python/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..e597c2d6fd --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/list-transactions.md @@ -0,0 +1,13 @@ +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.list_transactions( + queries = [] # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-python/examples/tablesdb/update-row.md index 86d0cf2b8a..89dbfb0587 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/update-row.md @@ -13,5 +13,6 @@ result = tables_db.update_row( table_id = '', row_id = '', data = {}, # optional - permissions = ["read("any")"] # optional + permissions = ["read("any")"], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-python/examples/tablesdb/update-rows.md index 386ddf8b88..4717581276 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/update-rows.md @@ -12,5 +12,6 @@ result = tables_db.update_rows( database_id = '', table_id = '', data = {}, # optional - queries = [] # optional + queries = [], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-python/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..97b518dc6e --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/update-transaction.md @@ -0,0 +1,15 @@ +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.update_transaction( + transaction_id = '', + commit = False, # optional + rollback = False # optional +) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-row.md index 068fded0c3..8539e12e96 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-row.md @@ -13,5 +13,6 @@ result = tables_db.upsert_row( table_id = '', row_id = '', data = {}, # optional - permissions = ["read("any")"] # optional + permissions = ["read("any")"], # optional + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-rows.md index 06436c0fa6..d42e259fb0 100644 --- a/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-python/examples/tablesdb/upsert-rows.md @@ -11,5 +11,6 @@ tables_db = TablesDB(client) result = tables_db.upsert_rows( database_id = '', table_id = '', - rows = [] + rows = [], + transaction_id = '' # optional ) diff --git a/docs/examples/1.8.x/server-rest/examples/databases/create-document.md b/docs/examples/1.8.x/server-rest/examples/databases/create-document.md index 57c38f0ba7..e1ca57e0bf 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/create-document.md @@ -16,5 +16,6 @@ X-Appwrite-JWT: "age": 30, "isAdmin": false }, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/create-documents.md b/docs/examples/1.8.x/server-rest/examples/databases/create-documents.md index cee5405fb2..4e23244620 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/create-documents.md @@ -8,5 +8,6 @@ X-Appwrite-Key: X-Appwrite-JWT: { - "documents": [] + "documents": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/create-operations.md b/docs/examples/1.8.x/server-rest/examples/databases/create-operations.md new file mode 100644 index 0000000000..212d60df29 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/databases/create-operations.md @@ -0,0 +1,22 @@ +POST /v1/databases/transactions/{transactionId}/operations HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "operations": [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] +} diff --git a/docs/examples/1.8.x/server-rest/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-rest/examples/databases/create-transaction.md new file mode 100644 index 0000000000..3647e2d128 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/databases/create-transaction.md @@ -0,0 +1,12 @@ +POST /v1/databases/transactions HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "ttl": 60 +} diff --git a/docs/examples/1.8.x/server-rest/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-rest/examples/databases/decrement-document-attribute.md index 78694a804d..0bd736b0e4 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/decrement-document-attribute.md @@ -9,5 +9,6 @@ X-Appwrite-Key: { "value": 0, - "min": 0 + "min": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/delete-document.md b/docs/examples/1.8.x/server-rest/examples/databases/delete-document.md index b5580b04bf..1031bfe18d 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/delete-document.md @@ -7,3 +7,6 @@ X-Appwrite-Session: X-Appwrite-Key: X-Appwrite-JWT: +{ + "transactionId": "" +} diff --git a/docs/examples/1.8.x/server-rest/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-rest/examples/databases/delete-documents.md index cb27719953..13ad4c6600 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/delete-documents.md @@ -6,5 +6,6 @@ X-Appwrite-Project: X-Appwrite-Key: { - "queries": [] + "queries": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-rest/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..09fc4e21f7 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/databases/delete-transaction.md @@ -0,0 +1,9 @@ +DELETE /v1/databases/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + diff --git a/docs/examples/1.8.x/server-rest/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-rest/examples/databases/get-transaction.md new file mode 100644 index 0000000000..e3d89d72de --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/databases/get-transaction.md @@ -0,0 +1,7 @@ +GET /v1/databases/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/server-rest/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-rest/examples/databases/increment-document-attribute.md index cd6b4122eb..924f0e9b0c 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/increment-document-attribute.md @@ -9,5 +9,6 @@ X-Appwrite-Key: { "value": 0, - "max": 0 + "max": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-rest/examples/databases/list-transactions.md new file mode 100644 index 0000000000..7a6f680a5f --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/databases/list-transactions.md @@ -0,0 +1,7 @@ +GET /v1/databases/transactions HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/server-rest/examples/databases/update-document.md b/docs/examples/1.8.x/server-rest/examples/databases/update-document.md index 9a156375de..dafd249c31 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/update-document.md @@ -9,5 +9,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/update-documents.md b/docs/examples/1.8.x/server-rest/examples/databases/update-documents.md index 69ea7a0d6f..69d2dccd13 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/update-documents.md @@ -7,5 +7,6 @@ X-Appwrite-Key: { "data": {}, - "queries": [] + "queries": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-rest/examples/databases/update-transaction.md new file mode 100644 index 0000000000..21f1921e41 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/databases/update-transaction.md @@ -0,0 +1,13 @@ +PATCH /v1/databases/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "commit": false, + "rollback": false +} diff --git a/docs/examples/1.8.x/server-rest/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-rest/examples/databases/upsert-document.md index 97b61bfc7f..e4a9c7796a 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/upsert-document.md @@ -9,5 +9,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-rest/examples/databases/upsert-documents.md index 4bcb9cb0c0..7b15435e90 100644 --- a/docs/examples/1.8.x/server-rest/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-rest/examples/databases/upsert-documents.md @@ -6,5 +6,6 @@ X-Appwrite-Project: X-Appwrite-Key: { - "documents": [] + "documents": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/messaging/create-push.md b/docs/examples/1.8.x/server-rest/examples/messaging/create-push.md index a70702c014..f873bfe6ee 100644 --- a/docs/examples/1.8.x/server-rest/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-rest/examples/messaging/create-push.md @@ -14,7 +14,7 @@ X-Appwrite-Key: "targets": [], "data": {}, "action": "", - "image": "[ID1:ID2]", + "image": "", "icon": "", "sound": "", "color": "", diff --git a/docs/examples/1.8.x/server-rest/examples/messaging/update-push.md b/docs/examples/1.8.x/server-rest/examples/messaging/update-push.md index b3b953bc31..a3a6f84ae6 100644 --- a/docs/examples/1.8.x/server-rest/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-rest/examples/messaging/update-push.md @@ -13,7 +13,7 @@ X-Appwrite-Key: "body": "", "data": {}, "action": "", - "image": "[ID1:ID2]", + "image": "", "icon": "", "sound": "", "color": "", diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..87ca296db2 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-operations.md @@ -0,0 +1,22 @@ +POST /v1/tablesdb/transactions/{transactionId}/operations HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "operations": [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] +} diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-row.md index 3c42d0f172..cec287f4b3 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-row.md @@ -16,5 +16,6 @@ X-Appwrite-JWT: "age": 30, "isAdmin": false }, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-rows.md index 176b4cdb02..0ff4426b84 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-rows.md @@ -8,5 +8,6 @@ X-Appwrite-Key: X-Appwrite-JWT: { - "rows": [] + "rows": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..a2b8b184bd --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/create-transaction.md @@ -0,0 +1,12 @@ +POST /v1/tablesdb/transactions HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "ttl": 60 +} diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/decrement-row-column.md index 26d8e1118c..74b06974f1 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/decrement-row-column.md @@ -9,5 +9,6 @@ X-Appwrite-Key: { "value": 0, - "min": 0 + "min": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-row.md index 3dbbf45a3c..b1376ee7cd 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-row.md @@ -7,3 +7,6 @@ X-Appwrite-Session: X-Appwrite-Key: X-Appwrite-JWT: +{ + "transactionId": "" +} diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-rows.md index c57d62ede3..22eae7d599 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-rows.md @@ -6,5 +6,6 @@ X-Appwrite-Project: X-Appwrite-Key: { - "queries": [] + "queries": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..ed6e20dcc8 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/delete-transaction.md @@ -0,0 +1,9 @@ +DELETE /v1/tablesdb/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..690351d711 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/get-transaction.md @@ -0,0 +1,7 @@ +GET /v1/tablesdb/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/increment-row-column.md index d687727806..e9047669cd 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/increment-row-column.md @@ -9,5 +9,6 @@ X-Appwrite-Key: { "value": 0, - "max": 0 + "max": 0, + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..8b7f9301e3 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/list-transactions.md @@ -0,0 +1,7 @@ +GET /v1/tablesdb/transactions HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/update-row.md index 51f10f7f97..5c37e3d929 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/update-row.md @@ -9,5 +9,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/update-rows.md index 2f282d8e13..c872907d30 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/update-rows.md @@ -7,5 +7,6 @@ X-Appwrite-Key: { "data": {}, - "queries": [] + "queries": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..118366e4a6 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/update-transaction.md @@ -0,0 +1,13 @@ +PATCH /v1/tablesdb/transactions/{transactionId} HTTP/1.1 +Host: cloud.appwrite.io +Content-Type: application/json +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: +X-Appwrite-Key: +X-Appwrite-Session: +X-Appwrite-JWT: + +{ + "commit": false, + "rollback": false +} diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-row.md index edb74043fb..9f698fb195 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-row.md @@ -9,5 +9,6 @@ X-Appwrite-JWT: { "data": {}, - "permissions": ["read(\"any\")"] + "permissions": ["read(\"any\")"], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-rows.md index 147e4f66c3..822c7aaec2 100644 --- a/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-rest/examples/tablesdb/upsert-rows.md @@ -6,5 +6,6 @@ X-Appwrite-Project: X-Appwrite-Key: { - "rows": [] + "rows": [], + "transactionId": "" } diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/create-document.md b/docs/examples/1.8.x/server-ruby/examples/databases/create-document.md index 22ce5745fd..d12a3dbb8d 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/create-document.md @@ -20,5 +20,6 @@ result = databases.create_document( "age" => 30, "isAdmin" => false }, - permissions: ["read("any")"] # optional + permissions: ["read("any")"], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/create-documents.md b/docs/examples/1.8.x/server-ruby/examples/databases/create-documents.md index 16abc5e465..db45bd78a9 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/create-documents.md @@ -12,5 +12,6 @@ databases = Databases.new(client) result = databases.create_documents( database_id: '', collection_id: '', - documents: [] + documents: [], + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/create-operations.md b/docs/examples/1.8.x/server-ruby/examples/databases/create-operations.md new file mode 100644 index 0000000000..687932bd3e --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/databases/create-operations.md @@ -0,0 +1,25 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +databases = Databases.new(client) + +result = databases.create_operations( + transaction_id: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-ruby/examples/databases/create-transaction.md new file mode 100644 index 0000000000..83d2e4ea4d --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/databases/create-transaction.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +databases = Databases.new(client) + +result = databases.create_transaction( + ttl: 60 # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-ruby/examples/databases/decrement-document-attribute.md index 9fd0191a0f..ecf15864da 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/decrement-document-attribute.md @@ -15,5 +15,6 @@ result = databases.decrement_document_attribute( document_id: '', attribute: '', value: null, # optional - min: null # optional + min: null, # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/delete-document.md b/docs/examples/1.8.x/server-ruby/examples/databases/delete-document.md index 2102d2695b..079247fc05 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/delete-document.md @@ -12,5 +12,6 @@ databases = Databases.new(client) result = databases.delete_document( database_id: '', collection_id: '', - document_id: '' + document_id: '', + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-ruby/examples/databases/delete-documents.md index d0f10d0b41..838660747c 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/delete-documents.md @@ -12,5 +12,6 @@ databases = Databases.new(client) result = databases.delete_documents( database_id: '', collection_id: '', - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-ruby/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..2024818ad4 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/databases/delete-transaction.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +databases = Databases.new(client) + +result = databases.delete_transaction( + transaction_id: '' +) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/get-document.md b/docs/examples/1.8.x/server-ruby/examples/databases/get-document.md index f43a1a2924..47404fee80 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/get-document.md @@ -13,5 +13,6 @@ result = databases.get_document( database_id: '', collection_id: '', document_id: '', - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-ruby/examples/databases/get-transaction.md new file mode 100644 index 0000000000..7d8349dc7d --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/databases/get-transaction.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +databases = Databases.new(client) + +result = databases.get_transaction( + transaction_id: '' +) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-ruby/examples/databases/increment-document-attribute.md index 3e8bfe0b2a..8f78675cdd 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/increment-document-attribute.md @@ -15,5 +15,6 @@ result = databases.increment_document_attribute( document_id: '', attribute: '', value: null, # optional - max: null # optional + max: null, # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/list-documents.md b/docs/examples/1.8.x/server-ruby/examples/databases/list-documents.md index 6617198d3f..666bfbd5ce 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/list-documents.md @@ -12,5 +12,6 @@ databases = Databases.new(client) result = databases.list_documents( database_id: '', collection_id: '', - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-ruby/examples/databases/list-transactions.md new file mode 100644 index 0000000000..c041a05b5e --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/databases/list-transactions.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +databases = Databases.new(client) + +result = databases.list_transactions( + queries: [] # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/update-document.md b/docs/examples/1.8.x/server-ruby/examples/databases/update-document.md index 485eb0485a..5831d68b5d 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/update-document.md @@ -14,5 +14,6 @@ result = databases.update_document( collection_id: '', document_id: '', data: {}, # optional - permissions: ["read("any")"] # optional + permissions: ["read("any")"], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/update-documents.md b/docs/examples/1.8.x/server-ruby/examples/databases/update-documents.md index 2f6907294f..c85f594e55 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/update-documents.md @@ -13,5 +13,6 @@ result = databases.update_documents( database_id: '', collection_id: '', data: {}, # optional - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-ruby/examples/databases/update-transaction.md new file mode 100644 index 0000000000..e53c148b18 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/databases/update-transaction.md @@ -0,0 +1,16 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +databases = Databases.new(client) + +result = databases.update_transaction( + transaction_id: '', + commit: false, # optional + rollback: false # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-ruby/examples/databases/upsert-document.md index 238081864f..e5daa554c4 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/upsert-document.md @@ -14,5 +14,6 @@ result = databases.upsert_document( collection_id: '', document_id: '', data: {}, - permissions: ["read("any")"] # optional + permissions: ["read("any")"], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-ruby/examples/databases/upsert-documents.md index 30c42aa439..b470b8d31f 100644 --- a/docs/examples/1.8.x/server-ruby/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-ruby/examples/databases/upsert-documents.md @@ -12,5 +12,6 @@ databases = Databases.new(client) result = databases.upsert_documents( database_id: '', collection_id: '', - documents: [] + documents: [], + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/messaging/create-push.md b/docs/examples/1.8.x/server-ruby/examples/messaging/create-push.md index 5c58fa542b..f4555aa967 100644 --- a/docs/examples/1.8.x/server-ruby/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-ruby/examples/messaging/create-push.md @@ -18,7 +18,7 @@ result = messaging.create_push( targets: [], # optional data: {}, # optional action: '', # optional - image: '[ID1:ID2]', # optional + image: '', # optional icon: '', # optional sound: '', # optional color: '', # optional diff --git a/docs/examples/1.8.x/server-ruby/examples/messaging/update-push.md b/docs/examples/1.8.x/server-ruby/examples/messaging/update-push.md index 42a5104ccb..19b273bb24 100644 --- a/docs/examples/1.8.x/server-ruby/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-ruby/examples/messaging/update-push.md @@ -18,7 +18,7 @@ result = messaging.update_push( body: '', # optional data: {}, # optional action: '', # optional - image: '[ID1:ID2]', # optional + image: '', # optional icon: '', # optional sound: '', # optional color: '', # optional diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..dfc7180990 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-operations.md @@ -0,0 +1,25 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.create_operations( + transaction_id: '', + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-row.md index 5e19136676..5622711642 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-row.md @@ -20,5 +20,6 @@ result = tables_db.create_row( "age" => 30, "isAdmin" => false }, - permissions: ["read("any")"] # optional + permissions: ["read("any")"], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-rows.md index f258d4d36f..76ee28699a 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-rows.md @@ -12,5 +12,6 @@ tables_db = TablesDB.new(client) result = tables_db.create_rows( database_id: '', table_id: '', - rows: [] + rows: [], + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..e3525afa19 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/create-transaction.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.create_transaction( + ttl: 60 # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/decrement-row-column.md index 21439740ed..62b01977b1 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/decrement-row-column.md @@ -15,5 +15,6 @@ result = tables_db.decrement_row_column( row_id: '', column: '', value: null, # optional - min: null # optional + min: null, # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-row.md index 704f52fc39..9747cb938a 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-row.md @@ -12,5 +12,6 @@ tables_db = TablesDB.new(client) result = tables_db.delete_row( database_id: '', table_id: '', - row_id: '' + row_id: '', + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-rows.md index 5b15c1748a..cf95cfb229 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-rows.md @@ -12,5 +12,6 @@ tables_db = TablesDB.new(client) result = tables_db.delete_rows( database_id: '', table_id: '', - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..8fa7b3b8ac --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/delete-transaction.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.delete_transaction( + transaction_id: '' +) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-row.md index 621c2e12f6..bdc1cf5fe6 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-row.md @@ -13,5 +13,6 @@ result = tables_db.get_row( database_id: '', table_id: '', row_id: '', - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..ce8468ba3f --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/get-transaction.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.get_transaction( + transaction_id: '' +) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/increment-row-column.md index bf9b6cc230..a20d2f5b36 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/increment-row-column.md @@ -15,5 +15,6 @@ result = tables_db.increment_row_column( row_id: '', column: '', value: null, # optional - max: null # optional + max: null, # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-rows.md index af971fbe10..b205cece5d 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-rows.md @@ -12,5 +12,6 @@ tables_db = TablesDB.new(client) result = tables_db.list_rows( database_id: '', table_id: '', - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..e969bc9965 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/list-transactions.md @@ -0,0 +1,14 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.list_transactions( + queries: [] # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-row.md index 7a48c5e3f6..02123051ca 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-row.md @@ -14,5 +14,6 @@ result = tables_db.update_row( table_id: '', row_id: '', data: {}, # optional - permissions: ["read("any")"] # optional + permissions: ["read("any")"], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-rows.md index 7316241139..7c538a137d 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-rows.md @@ -13,5 +13,6 @@ result = tables_db.update_rows( database_id: '', table_id: '', data: {}, # optional - queries: [] # optional + queries: [], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..2b8b3e7999 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/update-transaction.md @@ -0,0 +1,16 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.update_transaction( + transaction_id: '', + commit: false, # optional + rollback: false # optional +) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-row.md index 5eb4281002..9feb685927 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-row.md @@ -14,5 +14,6 @@ result = tables_db.upsert_row( table_id: '', row_id: '', data: {}, # optional - permissions: ["read("any")"] # optional + permissions: ["read("any")"], # optional + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-rows.md index c48211dcc0..e38f534ea9 100644 --- a/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-ruby/examples/tablesdb/upsert-rows.md @@ -12,5 +12,6 @@ tables_db = TablesDB.new(client) result = tables_db.upsert_rows( database_id: '', table_id: '', - rows: [] + rows: [], + transaction_id: '' # optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/create-document.md b/docs/examples/1.8.x/server-swift/examples/databases/create-document.md index cc25fd8df8..604bacdc2a 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/create-document.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/create-document.md @@ -18,6 +18,7 @@ let document = try await databases.createDocument( "age": 30, "isAdmin": false ], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/create-documents.md b/docs/examples/1.8.x/server-swift/examples/databases/create-documents.md index 2e992d9e3a..82a75125ae 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/create-documents.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/create-documents.md @@ -10,6 +10,7 @@ let databases = Databases(client) let documentList = try await databases.createDocuments( databaseId: "", collectionId: "", - documents: [] + documents: [], + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/create-operations.md b/docs/examples/1.8.x/server-swift/examples/databases/create-operations.md new file mode 100644 index 0000000000..7cab190bd3 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/databases/create-operations.md @@ -0,0 +1,24 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let databases = Databases(client) + +let transaction = try await databases.createOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/databases/create-transaction.md b/docs/examples/1.8.x/server-swift/examples/databases/create-transaction.md new file mode 100644 index 0000000000..333632c583 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/databases/create-transaction.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let databases = Databases(client) + +let transaction = try await databases.createTransaction( + ttl: 60 // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/databases/decrement-document-attribute.md b/docs/examples/1.8.x/server-swift/examples/databases/decrement-document-attribute.md index 81516fa26a..8c256ad208 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/decrement-document-attribute.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/decrement-document-attribute.md @@ -13,6 +13,7 @@ let document = try await databases.decrementDocumentAttribute( documentId: "", attribute: "", value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/delete-document.md b/docs/examples/1.8.x/server-swift/examples/databases/delete-document.md index 1db59709ab..9120c3d0d0 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/delete-document.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/delete-document.md @@ -10,6 +10,7 @@ let databases = Databases(client) let result = try await databases.deleteDocument( databaseId: "", collectionId: "", - documentId: "" + documentId: "", + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/delete-documents.md b/docs/examples/1.8.x/server-swift/examples/databases/delete-documents.md index d5321f2b26..79ec772b3b 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/delete-documents.md @@ -10,6 +10,7 @@ let databases = Databases(client) let documentList = try await databases.deleteDocuments( databaseId: "", collectionId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/delete-transaction.md b/docs/examples/1.8.x/server-swift/examples/databases/delete-transaction.md new file mode 100644 index 0000000000..8ac62ef945 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/databases/delete-transaction.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let databases = Databases(client) + +let result = try await databases.deleteTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/server-swift/examples/databases/get-document.md b/docs/examples/1.8.x/server-swift/examples/databases/get-document.md index c92856a731..319a7ec3fb 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/get-document.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/get-document.md @@ -11,6 +11,7 @@ let document = try await databases.getDocument( databaseId: "", collectionId: "", documentId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/get-transaction.md b/docs/examples/1.8.x/server-swift/examples/databases/get-transaction.md new file mode 100644 index 0000000000..bfabd08b78 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/databases/get-transaction.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let databases = Databases(client) + +let transaction = try await databases.getTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/server-swift/examples/databases/increment-document-attribute.md b/docs/examples/1.8.x/server-swift/examples/databases/increment-document-attribute.md index 64ba46b413..7dd8805dc0 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/increment-document-attribute.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/increment-document-attribute.md @@ -13,6 +13,7 @@ let document = try await databases.incrementDocumentAttribute( documentId: "", attribute: "", value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/list-documents.md b/docs/examples/1.8.x/server-swift/examples/databases/list-documents.md index 2cac9330b3..1147530d00 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/list-documents.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/list-documents.md @@ -10,6 +10,7 @@ let databases = Databases(client) let documentList = try await databases.listDocuments( databaseId: "", collectionId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/list-transactions.md b/docs/examples/1.8.x/server-swift/examples/databases/list-transactions.md new file mode 100644 index 0000000000..bb0d852d0a --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/databases/list-transactions.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let databases = Databases(client) + +let transactionList = try await databases.listTransactions( + queries: [] // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/databases/update-document.md b/docs/examples/1.8.x/server-swift/examples/databases/update-document.md index 7d452db284..b6260d754d 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/update-document.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/update-document.md @@ -12,6 +12,7 @@ let document = try await databases.updateDocument( collectionId: "", documentId: "", data: [:], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/update-documents.md b/docs/examples/1.8.x/server-swift/examples/databases/update-documents.md index 0e934b1424..f1fb34aa3c 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/update-documents.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/update-documents.md @@ -11,6 +11,7 @@ let documentList = try await databases.updateDocuments( databaseId: "", collectionId: "", data: [:], // optional - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/update-transaction.md b/docs/examples/1.8.x/server-swift/examples/databases/update-transaction.md new file mode 100644 index 0000000000..79f4939b5d --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/databases/update-transaction.md @@ -0,0 +1,15 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let databases = Databases(client) + +let transaction = try await databases.updateTransaction( + transactionId: "", + commit: false, // optional + rollback: false // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/databases/upsert-document.md b/docs/examples/1.8.x/server-swift/examples/databases/upsert-document.md index e78bd458a0..26897f4ca5 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/upsert-document.md @@ -12,6 +12,7 @@ let document = try await databases.upsertDocument( collectionId: "", documentId: "", data: [:], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/databases/upsert-documents.md b/docs/examples/1.8.x/server-swift/examples/databases/upsert-documents.md index 544f02f9c0..92c5fd9810 100644 --- a/docs/examples/1.8.x/server-swift/examples/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-swift/examples/databases/upsert-documents.md @@ -10,6 +10,7 @@ let databases = Databases(client) let documentList = try await databases.upsertDocuments( databaseId: "", collectionId: "", - documents: [] + documents: [], + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/messaging/create-push.md b/docs/examples/1.8.x/server-swift/examples/messaging/create-push.md index 498eccb51a..ba03b3330d 100644 --- a/docs/examples/1.8.x/server-swift/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-swift/examples/messaging/create-push.md @@ -17,7 +17,7 @@ let message = try await messaging.createPush( targets: [], // optional data: [:], // optional action: "", // optional - image: "[ID1:ID2]", // optional + image: "", // optional icon: "", // optional sound: "", // optional color: "", // optional diff --git a/docs/examples/1.8.x/server-swift/examples/messaging/update-push.md b/docs/examples/1.8.x/server-swift/examples/messaging/update-push.md index e443161aa9..b7b5bb0b38 100644 --- a/docs/examples/1.8.x/server-swift/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-swift/examples/messaging/update-push.md @@ -17,7 +17,7 @@ let message = try await messaging.updatePush( body: "", // optional data: [:], // optional action: "", // optional - image: "[ID1:ID2]", // optional + image: "", // optional icon: "", // optional sound: "", // optional color: "", // optional diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/create-operations.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-operations.md new file mode 100644 index 0000000000..5ee356e55b --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-operations.md @@ -0,0 +1,24 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.createOperations( + transactionId: "", + operations: [ + { + "action": "create", + "databaseId": "", + "tableId": "", + "rowId": "", + "data": { + "name": "Walter O'Brien" + } + } + ] // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/create-row.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-row.md index 0c59a65755..049ef2da3d 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-row.md @@ -18,6 +18,7 @@ let row = try await tablesDB.createRow( "age": 30, "isAdmin": false ], - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/create-rows.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-rows.md index 25ea512590..63fafbd9e5 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-rows.md @@ -10,6 +10,7 @@ let tablesDB = TablesDB(client) let rowList = try await tablesDB.createRows( databaseId: "", tableId: "", - rows: [] + rows: [], + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/create-transaction.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-transaction.md new file mode 100644 index 0000000000..d826446ea2 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/create-transaction.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.createTransaction( + ttl: 60 // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/decrement-row-column.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/decrement-row-column.md index 196289e994..9c33055202 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/decrement-row-column.md @@ -13,6 +13,7 @@ let row = try await tablesDB.decrementRowColumn( rowId: "", column: "", value: 0, // optional - min: 0 // optional + min: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-row.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-row.md index 5449c74edb..a0a96eea4e 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-row.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-row.md @@ -10,6 +10,7 @@ let tablesDB = TablesDB(client) let result = try await tablesDB.deleteRow( databaseId: "", tableId: "", - rowId: "" + rowId: "", + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-rows.md index 85d8957f78..7235112eca 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-rows.md @@ -10,6 +10,7 @@ let tablesDB = TablesDB(client) let rowList = try await tablesDB.deleteRows( databaseId: "", tableId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-transaction.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-transaction.md new file mode 100644 index 0000000000..9a5d58bf42 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/delete-transaction.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let result = try await tablesDB.deleteTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/get-row.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/get-row.md index 17e6ccbb52..ecadab16aa 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/get-row.md @@ -11,6 +11,7 @@ let row = try await tablesDB.getRow( databaseId: "", tableId: "", rowId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/get-transaction.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/get-transaction.md new file mode 100644 index 0000000000..af0bd03b12 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/get-transaction.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.getTransaction( + transactionId: "" +) + diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/increment-row-column.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/increment-row-column.md index 38aa7581c6..6c35ba8d4a 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/increment-row-column.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/increment-row-column.md @@ -13,6 +13,7 @@ let row = try await tablesDB.incrementRowColumn( rowId: "", column: "", value: 0, // optional - max: 0 // optional + max: 0, // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/list-rows.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/list-rows.md index 7320147685..92a2813f74 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/list-rows.md @@ -10,6 +10,7 @@ let tablesDB = TablesDB(client) let rowList = try await tablesDB.listRows( databaseId: "", tableId: "", - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/list-transactions.md new file mode 100644 index 0000000000..c6acb295d6 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/list-transactions.md @@ -0,0 +1,13 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let transactionList = try await tablesDB.listTransactions( + queries: [] // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/update-row.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/update-row.md index e06338b3d6..3ebd9e0970 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/update-row.md @@ -12,6 +12,7 @@ let row = try await tablesDB.updateRow( tableId: "", rowId: "", data: [:], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/update-rows.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/update-rows.md index 58894f5b85..f18a2a306c 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/update-rows.md @@ -11,6 +11,7 @@ let rowList = try await tablesDB.updateRows( databaseId: "", tableId: "", data: [:], // optional - queries: [] // optional + queries: [], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/update-transaction.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/update-transaction.md new file mode 100644 index 0000000000..faa7d07d63 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/update-transaction.md @@ -0,0 +1,15 @@ +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let transaction = try await tablesDB.updateTransaction( + transactionId: "", + commit: false, // optional + rollback: false // optional +) + diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-row.md index dc133f6cd1..1b076266a3 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-row.md @@ -12,6 +12,7 @@ let row = try await tablesDB.upsertRow( tableId: "", rowId: "", data: [:], // optional - permissions: ["read("any")"] // optional + permissions: ["read("any")"], // optional + transactionId: "" // optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-rows.md index fe35f0da91..027087b252 100644 --- a/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-swift/examples/tablesdb/upsert-rows.md @@ -10,6 +10,7 @@ let tablesDB = TablesDB(client) let rowList = try await tablesDB.upsertRows( databaseId: "", tableId: "", - rows: [] + rows: [], + transactionId: "" // optional ) diff --git a/docs/sdks/android/CHANGELOG.md b/docs/sdks/android/CHANGELOG.md index b6b9e4072b..559b61fbe9 100644 --- a/docs/sdks/android/CHANGELOG.md +++ b/docs/sdks/android/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 11.2.0 + +* Add transaction support for Databases and TablesDB + ## 11.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/apple/CHANGELOG.md b/docs/sdks/apple/CHANGELOG.md index dce2df1804..fed05027ce 100644 --- a/docs/sdks/apple/CHANGELOG.md +++ b/docs/sdks/apple/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 13.2.0 + +* Add transaction support for Databases and TablesDB + ## 13.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 289c1ef0cb..202340eb76 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 10.2.0 + +* Add transaction support for Databases and TablesDB + ## 10.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index c25e66708b..3770468a34 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 19.2.0 + +* Add transaction support for Databases and TablesDB + ## 19.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/dotnet/CHANGELOG.md b/docs/sdks/dotnet/CHANGELOG.md index 7bd7d1d267..d2613ed142 100644 --- a/docs/sdks/dotnet/CHANGELOG.md +++ b/docs/sdks/dotnet/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 0.21.0 + +* Add transaction support for Databases and TablesDB + ## 0.20.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 176efe4a5d..4fcd2778d8 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 20.2.0 + +* Add transaction support for Databases and TablesDB + ## 20.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/go/CHANGELOG.md b/docs/sdks/go/CHANGELOG.md index 9191f77aa3..88e81854f1 100644 --- a/docs/sdks/go/CHANGELOG.md +++ b/docs/sdks/go/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## v0.13.0 + +* Add transaction support for Databases and TablesDB + ## v0.12.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/kotlin/CHANGELOG.md b/docs/sdks/kotlin/CHANGELOG.md index 40c32751d7..422b08c0ef 100644 --- a/docs/sdks/kotlin/CHANGELOG.md +++ b/docs/sdks/kotlin/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 12.2.0 + +* Add transaction support for Databases and TablesDB + ## 12.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/nodejs/CHANGELOG.md b/docs/sdks/nodejs/CHANGELOG.md index 7225d3d92f..a646e5694a 100644 --- a/docs/sdks/nodejs/CHANGELOG.md +++ b/docs/sdks/nodejs/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 20.2.0 + +* Add transaction support for Databases and TablesDB + ## 20.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/php/CHANGELOG.md b/docs/sdks/php/CHANGELOG.md index b1221264b2..d375d10c09 100644 --- a/docs/sdks/php/CHANGELOG.md +++ b/docs/sdks/php/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 17.4.0 + +* Add transaction support for Databases and TablesDB + ## 17.3.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/python/CHANGELOG.md b/docs/sdks/python/CHANGELOG.md index 584aa5cf5c..1c246243d3 100644 --- a/docs/sdks/python/CHANGELOG.md +++ b/docs/sdks/python/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 13.4.0 + +* Add transaction support for Databases and TablesDB + ## 13.3.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index 79ade8cc6c..ca732deec3 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1,5 +1,9 @@ # Change log +## 0.17.0 + +* Add transaction support for Databases and TablesDB + ## 0.16.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/ruby/CHANGELOG.md b/docs/sdks/ruby/CHANGELOG.md index 0498d7cfb5..e39eedb436 100644 --- a/docs/sdks/ruby/CHANGELOG.md +++ b/docs/sdks/ruby/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 19.2.0 + +* Add transaction support for Databases and TablesDB + ## 19.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/swift/CHANGELOG.md b/docs/sdks/swift/CHANGELOG.md index 3d73a868f3..0d384b23f8 100644 --- a/docs/sdks/swift/CHANGELOG.md +++ b/docs/sdks/swift/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 13.2.0 + +* Add transaction support for Databases and TablesDB + ## 13.1.0 * Deprecate `createVerification` method in `Account` service diff --git a/docs/sdks/web/CHANGELOG.md b/docs/sdks/web/CHANGELOG.md index a28d868075..b88265dd01 100644 --- a/docs/sdks/web/CHANGELOG.md +++ b/docs/sdks/web/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 21.2.0 + +* Add transaction support for Databases and TablesDB + ## 21.1.0 * Deprecate `createVerification` method in `Account` service From a3103bf4d46d87fe16a5d6eaf7065e73e1149f94 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 23:37:28 +1300 Subject: [PATCH 262/274] Fix release --- app/config/platforms.php | 32 ++++++++++++++--------------- docs/sdks/android/CHANGELOG.md | 2 +- docs/sdks/apple/CHANGELOG.md | 2 +- docs/sdks/cli/CHANGELOG.md | 2 +- docs/sdks/dart/CHANGELOG.md | 2 +- docs/sdks/dotnet/CHANGELOG.md | 2 +- docs/sdks/flutter/CHANGELOG.md | 2 +- docs/sdks/go/CHANGELOG.md | 2 +- docs/sdks/kotlin/CHANGELOG.md | 2 +- docs/sdks/nodejs/CHANGELOG.md | 2 +- docs/sdks/php/CHANGELOG.md | 2 +- docs/sdks/python/CHANGELOG.md | 2 +- docs/sdks/react-native/CHANGELOG.md | 2 +- docs/sdks/ruby/CHANGELOG.md | 2 +- docs/sdks/swift/CHANGELOG.md | 2 +- docs/sdks/web/CHANGELOG.md | 2 +- 16 files changed, 31 insertions(+), 31 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index 0f32c9f45c..22606d803c 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '21.2.0', + 'version' => '21.2.1', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '20.2.0', + 'version' => '20.2.1', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '13.2.0', + 'version' => '13.2.1', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '11.2.0', + 'version' => '11.2.1', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -139,7 +139,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.17.0', + 'version' => '0.17.1', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -207,7 +207,7 @@ return [ [ 'key' => 'web', 'name' => 'Console', - 'version' => '0.1.0', + 'version' => '0.1.1', 'url' => '', 'package' => '', 'enabled' => true, @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '10.2.0', + 'version' => '10.2.1', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '20.2.0', + 'version' => '20.2.1', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -281,7 +281,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '17.4.0', + 'version' => '17.4.1', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -300,7 +300,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '13.4.0', + 'version' => '13.4.1', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '19.2.0', + 'version' => '19.2.1', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => 'v0.13.0', + 'version' => 'v0.13.1', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, @@ -357,7 +357,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.21.0', + 'version' => '0.21.1', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '19.2.0', + 'version' => '19.2.1', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '12.2.0', + 'version' => '12.2.1', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '13.2.0', + 'version' => '13.2.1', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/docs/sdks/android/CHANGELOG.md b/docs/sdks/android/CHANGELOG.md index 559b61fbe9..a05da45c4e 100644 --- a/docs/sdks/android/CHANGELOG.md +++ b/docs/sdks/android/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 11.2.0 +## 11.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/apple/CHANGELOG.md b/docs/sdks/apple/CHANGELOG.md index fed05027ce..6d67c4943f 100644 --- a/docs/sdks/apple/CHANGELOG.md +++ b/docs/sdks/apple/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 13.2.0 +## 13.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 202340eb76..db3898dd00 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 10.2.0 +## 10.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 3770468a34..c5ea578d20 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 19.2.0 +## 19.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/dotnet/CHANGELOG.md b/docs/sdks/dotnet/CHANGELOG.md index d2613ed142..deb467ce3d 100644 --- a/docs/sdks/dotnet/CHANGELOG.md +++ b/docs/sdks/dotnet/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 0.21.0 +## 0.21.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 4fcd2778d8..7ac74d0c05 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 20.2.0 +## 20.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/go/CHANGELOG.md b/docs/sdks/go/CHANGELOG.md index 88e81854f1..243fdc14a1 100644 --- a/docs/sdks/go/CHANGELOG.md +++ b/docs/sdks/go/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## v0.13.0 +## v0.13.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/kotlin/CHANGELOG.md b/docs/sdks/kotlin/CHANGELOG.md index 422b08c0ef..fe8c8bf46a 100644 --- a/docs/sdks/kotlin/CHANGELOG.md +++ b/docs/sdks/kotlin/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 12.2.0 +## 12.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/nodejs/CHANGELOG.md b/docs/sdks/nodejs/CHANGELOG.md index a646e5694a..bd21b954f7 100644 --- a/docs/sdks/nodejs/CHANGELOG.md +++ b/docs/sdks/nodejs/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 20.2.0 +## 20.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/php/CHANGELOG.md b/docs/sdks/php/CHANGELOG.md index d375d10c09..3e5e810e84 100644 --- a/docs/sdks/php/CHANGELOG.md +++ b/docs/sdks/php/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 17.4.0 +## 17.4.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/python/CHANGELOG.md b/docs/sdks/python/CHANGELOG.md index 1c246243d3..7d8327b919 100644 --- a/docs/sdks/python/CHANGELOG.md +++ b/docs/sdks/python/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 13.4.0 +## 13.4.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index ca732deec3..f1f15907bc 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## 0.17.0 +## 0.17.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/ruby/CHANGELOG.md b/docs/sdks/ruby/CHANGELOG.md index e39eedb436..22f70c4c3a 100644 --- a/docs/sdks/ruby/CHANGELOG.md +++ b/docs/sdks/ruby/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 19.2.0 +## 19.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/swift/CHANGELOG.md b/docs/sdks/swift/CHANGELOG.md index 0d384b23f8..10119c524b 100644 --- a/docs/sdks/swift/CHANGELOG.md +++ b/docs/sdks/swift/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 13.2.0 +## 13.2.1 * Add transaction support for Databases and TablesDB diff --git a/docs/sdks/web/CHANGELOG.md b/docs/sdks/web/CHANGELOG.md index b88265dd01..338ec9095c 100644 --- a/docs/sdks/web/CHANGELOG.md +++ b/docs/sdks/web/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 21.2.0 +## 21.2.1 * Add transaction support for Databases and TablesDB From 32d6eaa21c078e703efe0b74f6416653511e6d5e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 9 Oct 2025 23:37:44 +1300 Subject: [PATCH 263/274] Check expiry on stage --- .../Databases/Collections/Documents/Attribute/Decrement.php | 6 ++++++ .../Databases/Collections/Documents/Attribute/Increment.php | 6 ++++++ .../Http/Databases/Collections/Documents/Create.php | 6 ++++++ .../Http/Databases/Collections/Documents/Delete.php | 6 ++++++ .../Http/Databases/Collections/Documents/Update.php | 6 ++++++ .../Http/Databases/Collections/Documents/Upsert.php | 6 ++++++ .../Http/Databases/Transactions/Operations/Create.php | 6 ++++++ 7 files changed, 42 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index cbe0ddceaf..da202b2f40 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -105,6 +105,12 @@ class Decrement extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 22e19c69a5..543597612c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -105,6 +105,12 @@ class Increment extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 902a3585ba..41c50775a3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -375,6 +375,12 @@ class Create extends Action throw new Exception(Exception::TRANSACTION_NOT_READY); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index be73068c06..200bd4db72 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -137,6 +137,12 @@ class Delete extends Action throw new Exception(Exception::TRANSACTION_NOT_READY); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index a7d03de812..37adc1db70 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -252,6 +252,12 @@ class Update extends Action throw new Exception(Exception::TRANSACTION_NOT_READY); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 5d2f755212..92dd1c03b7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -261,6 +261,12 @@ class Upsert extends Action throw new Exception(Exception::TRANSACTION_NOT_READY); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + // Enforce max operations per transaction $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index c3ba45bdce..460da671b0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -77,6 +77,12 @@ class Create extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } + $now = new \DateTime(); + $expiresAt = new \DateTime($transaction->getAttribute('expiresAt', 'now')); + if ($now > $expiresAt) { + throw new Exception(Exception::TRANSACTION_EXPIRED); + } + $maxBatch = $plan['databasesTransactionSize'] ?? APP_LIMIT_DATABASE_TRANSACTION; $existing = $transaction->getAttribute('operations', 0); From bf589f74857d7d26d906c08ebc635cf4b7faa5c6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 02:16:58 +1300 Subject: [PATCH 264/274] Fix client side --- .../Documents/Attribute/Decrement.php | 8 +- .../Documents/Attribute/Increment.php | 8 +- .../Collections/Documents/Create.php | 4 +- .../Collections/Documents/Delete.php | 4 +- .../Collections/Documents/Update.php | 4 +- .../Collections/Documents/Upsert.php | 4 +- .../Http/Databases/Transactions/Create.php | 18 +- .../Transactions/Operations/Create.php | 106 +++-- .../Http/Databases/Transactions/Update.php | 8 +- .../Http/TablesDB/Transactions/Create.php | 1 + .../TablesDB/Transactions/PermissionsBase.php | 433 ++++++++++++++++++ 11 files changed, 542 insertions(+), 56 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index da202b2f40..316d0f1edb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute; +use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -88,6 +89,9 @@ class Decrement extends Action public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -100,7 +104,9 @@ class Decrement extends Action // Handle transaction staging if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 543597612c..22fc1a8152 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute; +use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -88,6 +89,9 @@ class Increment extends Action public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void { + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -100,7 +104,9 @@ class Increment extends Action // Handle transaction staging if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 41c50775a3..4254283432 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -367,7 +367,9 @@ class Create extends Action // Handle transaction staging if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 200bd4db72..a4cef59e5f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -129,7 +129,9 @@ class Delete extends Action // Handle transaction staging if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 37adc1db70..a3a976c04a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -244,7 +244,9 @@ class Update extends Action // Handle transaction staging if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 92dd1c03b7..91a35d74c2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -253,7 +253,9 @@ class Upsert extends Action // Handle transaction staging if ($transactionId !== null) { - $transaction = $dbForProject->getDocument('transactions', $transactionId); + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php index 744ad33540..c4c5bf8b51 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Create.php @@ -11,6 +11,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Authorization; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Range; @@ -53,13 +54,28 @@ class Create extends Action ->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') + ->inject('user') ->callback($this->action(...)); } - public function action(int $ttl, UtopiaResponse $response, Database $dbForProject): void + public function action(int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user): void { + $permissions = []; + if (!empty($user->getId())) { + $allowedPermissions = [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]; + + foreach ($allowedPermissions as $permission) { + $permissions[] = (new Permission($permission, 'user', $user->getId()))->toString(); + } + } + $transaction = Authorization::skip(fn () => $dbForProject->createDocument('transactions', new Document([ '$id' => ID::unique(), + '$permissions' => $permissions, 'status' => 'pending', 'operations' => 0, 'expiresAt' => DateTime::addSeconds(new \DateTime(), $ttl), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 460da671b0..8aa132d15d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -72,8 +72,17 @@ class Create extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); } - $transaction = Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)); - if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') { + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + // API keys and admins can read any transaction, regular users need permissions + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); + if ($transaction->isEmpty()) { + throw new Exception(Exception::TRANSACTION_NOT_FOUND); + } + if ($transaction->getAttribute('status', '') !== 'pending') { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or non‑pending transaction'); } @@ -93,9 +102,6 @@ class Create extends Action ); } - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $databases = $collections = $staged = $dependants = []; foreach ($operations as $operation) { if (!$isAPIKey && !$isPrivilegedUser && \in_array($operation['action'], [ @@ -146,54 +152,58 @@ class Create extends Action } } - $permissionType = match ($operation['action']) { - 'create', 'bulkCreate' => Database::PERMISSION_CREATE, - 'update', 'bulkUpdate', 'increment', 'decrement' => Database::PERMISSION_UPDATE, - 'delete', 'bulkDelete' => Database::PERMISSION_DELETE, - 'upsert', 'bulkUpsert' => ($document && !$document->isEmpty()) ? Database::PERMISSION_UPDATE : Database::PERMISSION_CREATE, - default => throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid action: ' . $operation['action']) - }; + // Bulk operations skip permission validation entirely (API key/admin only, already checked above) + if (!\in_array($operation['action'], ['bulkCreate', 'bulkUpdate', 'bulkUpsert', 'bulkDelete'])) { + $permissionType = match ($operation['action']) { + 'create' => Database::PERMISSION_CREATE, + 'update', 'increment', 'decrement' => Database::PERMISSION_UPDATE, + 'delete' => Database::PERMISSION_DELETE, + 'upsert' => ($document && !$document->isEmpty()) ? Database::PERMISSION_UPDATE : Database::PERMISSION_CREATE, + default => throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid action: ' . $operation['action']) + }; - if (!$isAPIKey && !$isPrivilegedUser) { - $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validator = new Authorization($permissionType); - $collectionValid = $validator->isValid($collection->getPermissionsByType($permissionType)); - $documentValid = false; - if ($document !== null && !$document->isEmpty() && $documentSecurity) { - if ($permissionType === Database::PERMISSION_UPDATE) { - $documentValid = $validator->isValid($document->getUpdate()); - } elseif ($permissionType === Database::PERMISSION_DELETE) { - $documentValid = $validator->isValid($document->getDelete()); + // For individual operations, enforce permissions unless using API key/admin + if (!$isAPIKey && !$isPrivilegedUser) { + $documentSecurity = $collection->getAttribute('documentSecurity', false); + $validator = new Authorization($permissionType); + $collectionValid = $validator->isValid($collection->getPermissionsByType($permissionType)); + $documentValid = false; + if ($document !== null && !$document->isEmpty() && $documentSecurity) { + if ($permissionType === Database::PERMISSION_UPDATE) { + $documentValid = $validator->isValid($document->getUpdate()); + } elseif ($permissionType === Database::PERMISSION_DELETE) { + $documentValid = $validator->isValid($document->getDelete()); + } } - } - if ($permissionType === Database::PERMISSION_CREATE || !$documentSecurity) { - if (!$collectionValid) { - throw new Exception(Exception::USER_UNAUTHORIZED); + if ($permissionType === Database::PERMISSION_CREATE || !$documentSecurity) { + if (!$collectionValid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + } else { + if (!$collectionValid && !$documentValid) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } } - } else { - if (!$collectionValid && !$documentValid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - } - // Users can only set permissions for roles they have - if (isset($operation['data']['$permissions'])) { - $permissions = $operation['data']['$permissions']; - $roles = Authorization::getRoles(); - foreach (Database::PERMISSIONS as $type) { - foreach ($permissions as $permission) { - $permission = Permission::parse($permission); - if ($permission->getPermission() != $type) { - continue; - } - $role = (new Role( - $permission->getRole(), - $permission->getIdentifier(), - $permission->getDimension() - ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + // Users can only set permissions for roles they have + if (isset($operation['data']['$permissions'])) { + $permissions = $operation['data']['$permissions']; + $roles = Authorization::getRoles(); + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + } } } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 5d29bba34b..927adb9bc7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; +use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -110,7 +111,12 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); } - $transaction = Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + $transaction = ($isAPIKey || $isPrivilegedUser) + ? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) + : $dbForProject->getDocument('transactions', $transactionId); if ($transaction->isEmpty()) { throw new Exception(Exception::TRANSACTION_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php index e6c24b3341..a375d47c3c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Create.php @@ -49,6 +49,7 @@ class Create extends TransactionsCreate ->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') + ->inject('user') ->callback($this->action(...)); } } diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php index 9dac59e2ec..a1082256ee 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/PermissionsBase.php @@ -780,4 +780,437 @@ trait PermissionsBase $this->assertEquals(200, $rollback['headers']['status-code']); } + + /** + * Test that one user cannot read another user's transaction + */ + public function testUserCannotReadAnotherUsersTransaction(): void + { + // Create user 1 (fresh) and their transaction + $user1 = $this->getUser(true); + $user1Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user1['session'], + ]; + + $transaction1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(201, $transaction1['headers']['status-code']); + $transactionId1 = $transaction1['body']['$id']; + + // Create user 2 (fresh) + $user2 = $this->getUser(true); // Fresh user + $user2Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user2['session'], + ]; + + // User 2 tries to read User 1's transaction - should fail + $readAttempt = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers)); + + // This should fail with 404 Not Found (transaction doesn't exist for this user) + $this->assertEquals(404, $readAttempt['headers']['status-code']); + + // Verify User 1 can still read their own transaction + $readOwn = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(200, $readOwn['headers']['status-code']); + $this->assertEquals($transactionId1, $readOwn['body']['$id']); + } + + /** + * Test that one user cannot list another user's transactions + */ + public function testUserCannotListAnotherUsersTransactions(): void + { + // Create user 1 (fresh) with transactions + $user1 = $this->getUser(true); + $user1Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user1['session'], + ]; + + $transaction1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(201, $transaction1['headers']['status-code']); + + $transaction2 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(201, $transaction2['headers']['status-code']); + + // Create user 2 (fresh) with their own transaction + $user2 = $this->getUser(true); // Fresh user + $user2Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user2['session'], + ]; + + $transaction3 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers)); + + $this->assertEquals(201, $transaction3['headers']['status-code']); + + // User 2 lists transactions - should only see their own + $listUser2 = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers)); + + $this->assertEquals(200, $listUser2['headers']['status-code']); + $this->assertEquals(1, $listUser2['body']['total']); + $this->assertEquals($transaction3['body']['$id'], $listUser2['body']['transactions'][0]['$id']); + + // User 1 lists transactions - should only see their own (2 transactions) + $listUser1 = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(200, $listUser1['headers']['status-code']); + $this->assertEquals(2, $listUser1['body']['total']); + + // Verify neither of user1's transactions appear in user2's list + $user2TransactionIds = array_column($listUser2['body']['transactions'], '$id'); + $this->assertNotContains($transaction1['body']['$id'], $user2TransactionIds); + $this->assertNotContains($transaction2['body']['$id'], $user2TransactionIds); + } + + /** + * Test that one user cannot update another user's transaction + */ + public function testUserCannotUpdateAnotherUsersTransaction(): void + { + // Create user 1 (fresh) and their transaction + $user1 = $this->getUser(true); + $user1Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user1['session'], + ]; + + $transaction1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(201, $transaction1['headers']['status-code']); + $transactionId1 = $transaction1['body']['$id']; + + // Create user 2 (fresh) + $user2 = $this->getUser(true); // Fresh user + $user2Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user2['session'], + ]; + + // User 2 tries to commit User 1's transaction - should fail + $commitAttempt = $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers), [ + 'commit' => true, + ]); + + // This should fail with 404 Not Found + $this->assertEquals(404, $commitAttempt['headers']['status-code']); + + // User 2 tries to rollback User 1's transaction - should also fail + $rollbackAttempt = $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers), [ + 'rollback' => true, + ]); + + // This should also fail with 404 Not Found + $this->assertEquals(404, $rollbackAttempt['headers']['status-code']); + + // Verify User 1 can still commit their own transaction + $commitOwn = $this->client->call(Client::METHOD_PATCH, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers), [ + 'commit' => true, + ]); + + $this->assertEquals(200, $commitOwn['headers']['status-code']); + } + + /** + * Test that one user cannot delete another user's transaction + */ + public function testUserCannotDeleteAnotherUsersTransaction(): void + { + // Create user 1 (fresh) and their transaction + $user1 = $this->getUser(true); + $user1Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user1['session'], + ]; + + $transaction1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(201, $transaction1['headers']['status-code']); + $transactionId1 = $transaction1['body']['$id']; + + // Create user 2 (fresh) + $user2 = $this->getUser(true); // Fresh user + $user2Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user2['session'], + ]; + + // User 2 tries to delete User 1's transaction - should fail + $deleteAttempt = $this->client->call(Client::METHOD_DELETE, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers)); + + // This should fail with 404 Not Found + $this->assertEquals(404, $deleteAttempt['headers']['status-code']); + + // Verify User 1 can still access their transaction + $readOwn = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(200, $readOwn['headers']['status-code']); + + // User 1 can delete their own transaction + $deleteOwn = $this->client->call(Client::METHOD_DELETE, '/tablesdb/transactions/' . $transactionId1, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(204, $deleteOwn['headers']['status-code']); + } + + /** + * Test that one user cannot add operations to another user's transaction + */ + public function testUserCannotAddOperationsToAnotherUsersTransaction(): void + { + // Create a collection for testing + $collection = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => 'permTest11', + 'name' => 'Permission Test 11', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'rowSecurity' => false, + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + $attribute = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $this->permissionsDatabase . '/tables/' . $collection['body']['$id'] . '/columns/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'size' => 255, + 'required' => true, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + sleep(2); + + // Create user 1 (fresh) and their transaction + $user1 = $this->getUser(true); + $user1Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user1['session'], + ]; + + $transaction1 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers)); + + $this->assertEquals(201, $transaction1['headers']['status-code']); + $transactionId1 = $transaction1['body']['$id']; + + // Create user 2 (fresh) + $user2 = $this->getUser(true); // Fresh user + $user2Headers = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user2['session'], + ]; + + // User 2 tries to add operations to User 1's transaction - should fail + $operationAttempt = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId1 . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user2Headers), [ + 'operations' => [[ + 'action' => 'create', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'maliciousDoc', + 'data' => ['title' => 'Malicious Document'], + ]] + ]); + + // This should fail with 404 Not Found + $this->assertEquals(404, $operationAttempt['headers']['status-code']); + + // Verify User 1 can still add operations to their own transaction + $operationOwn = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions/' . $transactionId1 . '/operations', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $user1Headers), [ + 'operations' => [[ + 'action' => 'create', + 'databaseId' => $this->permissionsDatabase, + 'tableId' => $collection['body']['$id'], + 'rowId' => 'legitimateDoc', + 'data' => ['title' => 'Legitimate Document'], + ]] + ]); + + $this->assertEquals(201, $operationOwn['headers']['status-code']); + $this->assertEquals(1, $operationOwn['body']['operations']); + } + + /** + * Test that an authenticated user can successfully list their own transactions + */ + public function testAuthenticatedUserCanListTheirOwnTransactions(): void + { + // Create an authenticated user + $user = $this->getUser(); + $userHeaders = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user['session'], + ]; + + // Create multiple transactions for this user + $transactionIds = []; + for ($i = 0; $i < 3; $i++) { + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $this->assertNotEmpty($transaction['body']['$id']); + $transactionIds[] = $transaction['body']['$id']; + } + + // List transactions + $list = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(200, $list['headers']['status-code']); + $this->assertGreaterThanOrEqual(3, $list['body']['total']); + $this->assertIsArray($list['body']['transactions']); + $this->assertGreaterThanOrEqual(3, count($list['body']['transactions'])); + + // Verify all created transactions are in the list + $listedIds = array_column($list['body']['transactions'], '$id'); + foreach ($transactionIds as $transactionId) { + $this->assertContains($transactionId, $listedIds); + } + + // Verify transaction structure + foreach ($list['body']['transactions'] as $transaction) { + $this->assertArrayHasKey('$id', $transaction); + $this->assertArrayHasKey('$createdAt', $transaction); + $this->assertArrayHasKey('$updatedAt', $transaction); + $this->assertArrayHasKey('status', $transaction); + $this->assertArrayHasKey('operations', $transaction); + } + } + + /** + * Test that an authenticated user can successfully delete their own transaction + */ + public function testAuthenticatedUserCanDeleteTheirOwnTransaction(): void + { + // Create an authenticated user + $user = $this->getUser(); + $userHeaders = [ + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $user['session'], + ]; + + // Create a transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Verify transaction exists by reading it + $read = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(200, $read['headers']['status-code']); + $this->assertEquals($transactionId, $read['body']['$id']); + + // Delete the transaction + $delete = $this->client->call(Client::METHOD_DELETE, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(204, $delete['headers']['status-code']); + + // Verify transaction is deleted by trying to read it again + $readAfterDelete = $this->client->call(Client::METHOD_GET, '/tablesdb/transactions/' . $transactionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(404, $readAfterDelete['headers']['status-code']); + + // Create another transaction and verify it can also be deleted + $transaction2 = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(201, $transaction2['headers']['status-code']); + $transactionId2 = $transaction2['body']['$id']; + + $delete2 = $this->client->call(Client::METHOD_DELETE, '/tablesdb/transactions/' . $transactionId2, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $userHeaders)); + + $this->assertEquals(204, $delete2['headers']['status-code']); + } } From 276b1357997d47f3363a5a96bc4505c00215feb8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 02:17:42 +1300 Subject: [PATCH 265/274] Trigger CI From cdac840071b500dc9c2498f24285bd849610a00a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 15:51:26 +1300 Subject: [PATCH 266/274] Use return value for write ops count --- .../Http/Databases/Transactions/Update.php | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 927adb9bc7..26fd631f3a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -167,8 +167,10 @@ class Update extends Action } } - $totalOperations++; - $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + 1; + if (!\in_array($action, ['bulkCreate', 'bulkUpdate', 'bulkUpsert', 'bulkDelete'])) { + $totalOperations++; + $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + 1; + } if ($data instanceof Document) { $data = $data->getArrayCopy(); @@ -194,16 +196,24 @@ class Update extends Action $this->handleDecrementOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); break; case 'bulkCreate': - $this->handleBulkCreateOperation($dbForProject, $collectionId, $data, $createdAt, $state); + $count = $this->handleBulkCreateOperation($dbForProject, $collectionId, $data, $createdAt, $state); + $totalOperations += $count; + $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + $count; break; case 'bulkUpdate': - $this->handleBulkUpdateOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); + $count = $this->handleBulkUpdateOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); + $totalOperations += $count; + $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + $count; break; case 'bulkUpsert': - $this->handleBulkUpsertOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); + $count = $this->handleBulkUpsertOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); + $totalOperations += $count; + $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + $count; break; case 'bulkDelete': - $this->handleBulkDeleteOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); + $count = $this->handleBulkDeleteOperation($dbForProject, $transactionState, $collectionId, $data, $createdAt, $state); + $totalOperations += $count; + $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + $count; break; } } @@ -645,7 +655,7 @@ class Update extends Action * @param array $data * @param \DateTime $createdAt * @param array &$state - * @return void + * @return int Number of documents created * @throws \Utopia\Database\Exception */ private function handleBulkCreateOperation( @@ -654,13 +664,14 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state) { + ): int { + $count = 0; + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $data, &$state, &$count) { $documents = \array_map(function ($doc) { return $doc instanceof Document ? $doc : new Document($doc); }, $data); - $dbForProject->createDocuments( + $count = $dbForProject->createDocuments( $collectionId, $documents, onNext: function (Document $document) use (&$state, $collectionId) { @@ -668,6 +679,7 @@ class Update extends Action } ); }); + return $count; } /** @@ -679,7 +691,7 @@ class Update extends Action * @param array $data * @param \DateTime $createdAt * @param array &$state - * @return void + * @return int Number of documents updated * @throws \Utopia\Database\Exception * @throws \Utopia\Database\Exception\Query * @throws ConflictException @@ -691,7 +703,7 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): int { $queries = Query::parseQueries($data['queries'] ?? []); $updateData = new Document($data['data']); @@ -701,7 +713,7 @@ class Update extends Action // Clone the document before passing to updateDocuments to prevent mutation // The database layer mutates the input document, which would corrupt transaction state - $dbForProject->updateDocuments( + $count = $dbForProject->updateDocuments( $collectionId, clone $updateData, $queries, @@ -739,6 +751,8 @@ class Update extends Action ); } } + + return $count; } /** @@ -750,7 +764,7 @@ class Update extends Action * @param array $data * @param \DateTime $createdAt * @param array &$state - * @return void + * @return int Number of documents upserted * @throws ConflictException * @throws \Utopia\Database\Exception */ @@ -761,14 +775,14 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): int { $documents = \array_map(function ($doc) { return $doc instanceof Document ? $doc : new Document($doc); }, $data); $mergedDocuments = $transactionState->applyBulkUpsertToState($collectionId, $documents, $state); - $dbForProject->upsertDocuments( + $count = $dbForProject->upsertDocuments( $collectionId, $mergedDocuments, onNext: function (Document $upserted, ?Document $old) use (&$state, $collectionId, $createdAt) { @@ -786,6 +800,8 @@ class Update extends Action $state[$collectionId][$upserted->getId()] = $upserted; } ); + + return $count; } /** @@ -797,7 +813,7 @@ class Update extends Action * @param array $data * @param \DateTime $createdAt * @param array &$state - * @return void + * @return int Number of documents deleted * @throws \Utopia\Database\Exception\Query * @throws ConflictException * @throws \Utopia\Database\Exception @@ -809,10 +825,10 @@ class Update extends Action array $data, \DateTime $createdAt, array &$state - ): void { + ): int { $queries = Query::parseQueries($data['queries'] ?? []); - $dbForProject->deleteDocuments( + $count = $dbForProject->deleteDocuments( $collectionId, $queries, onNext: function (Document $deleted, Document $old) use (&$state, $collectionId, $createdAt) { @@ -832,5 +848,7 @@ class Update extends Action ); $transactionState->applyBulkDeleteToState($collectionId, $queries, $state); + + return $count; } } From 7512a0db6b8b9bb68b1c28ea268c1e4ad73462e6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 17:27:09 +1300 Subject: [PATCH 267/274] Fix cross-API compat --- .../Collections/Documents/Action.php | 25 ++ .../Documents/Attribute/Decrement.php | 5 +- .../Documents/Attribute/Increment.php | 5 +- .../Collections/Documents/Create.php | 3 +- .../Collections/Documents/Update.php | 3 +- .../Collections/Documents/Upsert.php | 3 +- .../Http/Databases/Transactions/Update.php | 36 ++- .../Legacy/Transactions/TransactionsBase.php | 143 ++++++++++ .../Transactions/TransactionsBase.php | 246 ++++++++++++++++++ 9 files changed, 456 insertions(+), 13 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index 3d95c67a26..3da89f352c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -205,6 +205,31 @@ abstract class Action extends AppwriteAction return $this->isCollectionsAPI() ? 'collection' : 'table'; } + /** + * Get the correct attribute/column key for increment/decrement operations. + */ + protected function getAttributeKey(): string + { + return $this->isCollectionsAPI() ? 'attribute' : 'column'; + } + + /** + * Get the key used in ID parameters (e.g., 'collectionId' or 'tableId'). + */ + protected function getGroupId(): string + { + return $this->getCollectionsEventsContext() . 'Id'; + } + + /** + * Get the resource ID key for the current action. + */ + protected function getResourceId(): string + { + $resource = $this->isCollectionsAPI() ? 'document' : 'row'; + return $resource . 'Id'; + } + /** * Remove configured removable attributes from a document. * Used for relationship path handling to remove API-specific attributes. diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 316d0f1edb..0b3d0e9fb4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -136,7 +136,7 @@ class Decrement extends Action 'documentId' => $documentId, 'action' => 'decrement', 'data' => [ - 'attribute' => $attribute, + $this->getAttributeKey() => $attribute, 'value' => $value, 'min' => $min, ], @@ -153,9 +153,10 @@ class Decrement extends Action }); // Return successful response without actually decrementing + $groupId = $this->getGroupId(); $mockDocument = new Document([ '$id' => $documentId, - '$collectionId' => $collectionId, + '$' . $groupId => $collectionId, '$databaseId' => $databaseId, $attribute => $value, ]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 22fc1a8152..ef64099fa8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -136,7 +136,7 @@ class Increment extends Action 'documentId' => $documentId, 'action' => 'increment', 'data' => [ - 'attribute' => $attribute, + $this->getAttributeKey() => $attribute, 'value' => $value, 'max' => $max, ], @@ -153,9 +153,10 @@ class Increment extends Action }); // Return successful response without actually incrementing + $groupId = $this->getGroupId(); $mockDocument = new Document([ '$id' => $documentId, - '$collectionId' => $collectionId, + '$' . $groupId => $collectionId, '$databaseId' => $databaseId, $attribute => $value, ]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 4254283432..521190d3dc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -419,9 +419,10 @@ class Create extends Action 'total' => \count($documents), ]), $this->getBulkResponseModel()); } else { + $groupId = $this->getGroupId(); $mockDocument = new Document([ '$id' => $documents[0]['$id'] ?? $documentId, - '$collectionId' => $collectionId, + '$' . $groupId => $collectionId, '$databaseId' => $databaseId, ...$documents[0] ]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index a3a976c04a..552c51a5fb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -292,9 +292,10 @@ class Update extends Action }); // Return successful response without actually updating document + $groupId = $this->getGroupId(); $mockDocument = new Document([ '$id' => $documentId, - '$collectionId' => $collectionId, + '$' . $groupId => $collectionId, '$databaseId' => $databaseId, ...$document->getArrayCopy(), ...$data diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 91a35d74c2..f113a99c7a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -301,9 +301,10 @@ class Upsert extends Action }); // Return successful response without actually upserting document + $groupId = $this->getGroupId(); $mockDocument = new Document([ '$id' => $documentId, - '$collectionId' => $collectionId, + '$' . $groupId => $collectionId, '$databaseId' => $databaseId, ...$data ]); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 26fd631f3a..6e2bd63827 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -557,6 +557,28 @@ class Update extends Action }); } + /** + * Get the attribute/column name from data, with fallback for cross-API compatibility + * + * @param array $data The operation data + * @return string The attribute/column name + */ + private function getAttributeNameFromData(array $data): string + { + $expectedKey = $this->getAttributeKey(); + if (isset($data[$expectedKey])) { + return $data[$expectedKey]; + } + + // Try the opposite key for cross-API compatibility + $fallbackKey = $expectedKey === 'attribute' ? 'column' : 'attribute'; + if (isset($data[$fallbackKey])) { + return $data[$fallbackKey]; + } + + return ''; + } + /** * Handle increment operation * @@ -579,23 +601,24 @@ class Update extends Action array &$state ): void { $dependent = isset($state[$collectionId][$documentId]); + $attribute = $this->getAttributeNameFromData($data); if ($dependent) { $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data[$this->getAttributeKey()], + attribute: $attribute, value: $data['value'] ?? 1, max: $data['max'] ?? null ); return; } - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state, $attribute) { $state[$collectionId][$documentId] = $dbForProject->increaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data[$this->getAttributeKey()], + attribute: $attribute, value: $data['value'] ?? 1, max: $data['max'] ?? null ); @@ -624,23 +647,24 @@ class Update extends Action array &$state ): void { $dependent = isset($state[$collectionId][$documentId]); + $attribute = $this->getAttributeNameFromData($data); if ($dependent) { $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data[$this->getAttributeKey()], + attribute: $attribute, value: $data['value'] ?? 1, min: $data['min'] ?? null ); return; } - $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state) { + $dbForProject->withRequestTimestamp($createdAt, function () use ($dbForProject, $collectionId, $documentId, $data, &$state, $attribute) { $state[$collectionId][$documentId] = $dbForProject->decreaseDocumentAttribute( collection: $collectionId, id: $documentId, - attribute: $data[$this->getAttributeKey()], + attribute: $attribute, value: $data['value'] ?? 1, min: $data['min'] ?? null ); diff --git a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php index 836f1ca966..0f85de0ff5 100644 --- a/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/Legacy/Transactions/TransactionsBase.php @@ -3732,6 +3732,149 @@ trait TransactionsBase $this->assertEquals(0, $doc['body']['score']); } + /** + * Test individual increment/decrement endpoints with transactions for Legacy Collections API + * This test ensures that: + * 1. Transaction logs store the correct attribute key ('attribute' for Collections API) + * 2. Mock responses return the correct ID keys ('$collectionId' not '$tableId') + */ + public function testIncrementDecrementEndpointsWithTransaction(): void + { + // Create database and collection + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'IncrDecrEndpointTestDB' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'AccountsCollection', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add balance attribute + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/attributes/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'balance', + 'required' => false, + 'default' => 0, + ]); + + sleep(2); + + // Create initial documents + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'joe', + 'data' => ['balance' => 100] + ]); + + $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/documents", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'jane', + 'data' => ['balance' => 50] + ]); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/databases/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Test: Decrement using individual endpoint - should store 'attribute' not 'column' in transaction log + $decrementResponse = $this->client->call( + Client::METHOD_PATCH, + "/databases/{$databaseId}/collections/{$collectionId}/documents/joe/balance/decrement", + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'transactionId' => $transactionId, + 'value' => 50, + 'min' => 0, + ] + ); + + // Test: Response should return '$collectionId' not '$tableId' for Collections API + $this->assertEquals(200, $decrementResponse['headers']['status-code']); + $this->assertArrayHasKey('$collectionId', $decrementResponse['body'], 'Response should contain $collectionId for Collections API'); + $this->assertArrayNotHasKey('$tableId', $decrementResponse['body'], 'Response should not contain $tableId for Collections API'); + $this->assertEquals($collectionId, $decrementResponse['body']['$collectionId']); + + // Test increment endpoint + $incrementResponse = $this->client->call( + Client::METHOD_PATCH, + "/databases/{$databaseId}/collections/{$collectionId}/documents/jane/balance/increment", + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'transactionId' => $transactionId, + 'value' => 50, + ] + ); + + $this->assertEquals(200, $incrementResponse['headers']['status-code']); + $this->assertArrayHasKey('$collectionId', $incrementResponse['body'], 'Response should contain $collectionId for Collections API'); + $this->assertArrayNotHasKey('$tableId', $incrementResponse['body'], 'Response should not contain $tableId for Collections API'); + $this->assertEquals($collectionId, $incrementResponse['body']['$collectionId']); + + // Commit transaction - this will fail if transaction log has 'column' instead of 'attribute' + $commitResponse = $this->client->call(Client::METHOD_PATCH, "/databases/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $commitResponse['headers']['status-code'], 'Transaction commit should succeed'); + + // Verify final values + $joe = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/joe", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $jane = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/documents/jane", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $joe['headers']['status-code']); + $this->assertEquals(50, $joe['body']['balance'], 'Joe should have 100 - 50 = 50'); + + $this->assertEquals(200, $jane['headers']['status-code']); + $this->assertEquals(100, $jane['body']['balance'], 'Jane should have 50 + 50 = 100'); + } + /** * Test bulk update operations in transaction */ diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index b64d249e97..efa3b52cef 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -3867,6 +3867,252 @@ trait TransactionsBase $this->assertEquals('updated', $row['body']['status'], 'Status should be updated'); } + /** + * Test individual increment/decrement endpoints with transactions + * This test ensures that: + * 1. Transaction logs store the correct attribute key ('column' for TablesDB) + * 2. Mock responses return the correct ID keys ('$tableId' not '$collectionId') + */ + public function testIncrementDecrementEndpointsWithTransaction(): void + { + // Create database and table + $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' => 'IncrDecrEndpointTestDB' + ]); + + $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' => 'AccountsTable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add balance column + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'balance', + 'required' => false, + 'default' => 0, + ]); + + sleep(2); + + // Create initial rows + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'joe', + 'data' => ['balance' => 100] + ]); + + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'jane', + 'data' => ['balance' => 50] + ]); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Test Bug 1: Decrement using individual endpoint - should store 'column' not 'attribute' in transaction log + $decrementResponse = $this->client->call( + Client::METHOD_PATCH, + "/tablesdb/{$databaseId}/tables/{$tableId}/rows/joe/balance/decrement", + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'transactionId' => $transactionId, + 'value' => 50, + 'min' => 0, + ] + ); + + // Test Bug 2: Response should return '$tableId' not '$collectionId' + $this->assertEquals(200, $decrementResponse['headers']['status-code']); + $this->assertArrayHasKey('$tableId', $decrementResponse['body'], 'Response should contain $tableId for TablesDB API'); + $this->assertArrayNotHasKey('$collectionId', $decrementResponse['body'], 'Response should not contain $collectionId for TablesDB API'); + $this->assertEquals($tableId, $decrementResponse['body']['$tableId']); + + // Test increment endpoint + $incrementResponse = $this->client->call( + Client::METHOD_PATCH, + "/tablesdb/{$databaseId}/tables/{$tableId}/rows/jane/balance/increment", + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'transactionId' => $transactionId, + 'value' => 50, + ] + ); + + $this->assertEquals(200, $incrementResponse['headers']['status-code']); + $this->assertArrayHasKey('$tableId', $incrementResponse['body'], 'Response should contain $tableId for TablesDB API'); + $this->assertArrayNotHasKey('$collectionId', $incrementResponse['body'], 'Response should not contain $collectionId for TablesDB API'); + $this->assertEquals($tableId, $incrementResponse['body']['$tableId']); + + // Commit transaction - this will fail if transaction log has 'attribute' instead of 'column' + $commitResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $commitResponse['headers']['status-code'], 'Transaction commit should succeed'); + + // Verify final values + $joe = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/joe", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $jane = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/jane", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $joe['headers']['status-code']); + $this->assertEquals(50, $joe['body']['balance'], 'Joe should have 100 - 50 = 50'); + + $this->assertEquals(200, $jane['headers']['status-code']); + $this->assertEquals(100, $jane['body']['balance'], 'Jane should have 50 + 50 = 100'); + } + + /** + * Test cross-API compatibility: stage operations via TablesDB, commit via Collections API + * This ensures fallback logic works when APIs are mixed + */ + public function testCrossAPIIncrementDecrement(): void + { + // Create database and table + $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' => 'CrossAPITestDB' + ]); + + $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' => 'CrossAPITable', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $tableId = $table['body']['$id']; + + // Add balance column + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/integer", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'balance', + 'required' => false, + 'default' => 0, + ]); + + sleep(2); + + // Create initial row + $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'test', + 'data' => ['balance' => 100] + ]); + + // Create transaction using TablesDB API + $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']; + + // Stage operations using TablesDB API (will store 'column' key) + $this->client->call( + Client::METHOD_PATCH, + "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test/balance/decrement", + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'transactionId' => $transactionId, + 'value' => 30, + ] + ); + + // Commit using Collections API (expects 'attribute' key but should fallback to 'column') + $commitResponse = $this->client->call( + Client::METHOD_PATCH, + "/databases/transactions/{$transactionId}", + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'commit' => true + ] + ); + + $this->assertEquals(200, $commitResponse['headers']['status-code'], 'Cross-API commit should succeed'); + + // Verify final value + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals(70, $row['body']['balance'], 'Balance should be 100 - 30 = 70'); + } + public function testBulkUpdateWithDependentDocuments(): void { // Create database and table From f95e8a965a337caec26d0b547734ea82c89202f1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 17:27:57 +1300 Subject: [PATCH 268/274] Only set sequence on not empty --- src/Appwrite/Utopia/Response/Model/Document.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index 5bad504a63..0faebf2d76 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -82,7 +82,10 @@ class Document extends Any { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); - $document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0)); + + if (!$document->isEmpty()) { + $document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0)); + } foreach ($document->getAttributes() as $attribute) { if (\is_array($attribute)) { From 2830ab55f05ddfc19970989dcdaeb387cb209192 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 17:42:42 +1300 Subject: [PATCH 269/274] Lint --- src/Appwrite/Utopia/Response/Model/Document.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index 0faebf2d76..f9766c895c 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -82,7 +82,7 @@ class Document extends Any { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); - + if (!$document->isEmpty()) { $document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0)); } From 12a1653ae00c7876f781ef6d402734273cdc2435 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 18:44:48 +1300 Subject: [PATCH 270/274] Update doc desc --- .../Databases/Http/Databases/Transactions/Operations/Create.php | 2 +- .../Databases/Http/TablesDB/Transactions/Operations/Create.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 8aa132d15d..bd94c1c7eb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -39,7 +39,7 @@ class Create extends Action $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/databases/transactions/:transactionId/operations') - ->desc('Create operations scoped to a transaction') + ->desc('Create operations') ->groups(['api', 'database', 'transactions']) ->label('scope', 'documents.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php index d85020b2bc..469eb1b792 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Operations/Create.php @@ -30,7 +30,7 @@ class Create extends OperationsCreate $this ->setHttpMethod(self::HTTP_REQUEST_METHOD_POST) ->setHttpPath('/v1/tablesdb/transactions/:transactionId/operations') - ->desc('Create operations scoped to a transaction') + ->desc('Create operations') ->groups(['api', 'database', 'transactions']) ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) From ab5556919e6c8bc01e4d395d8c0412b8dc119579 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 10 Oct 2025 19:21:33 +1300 Subject: [PATCH 271/274] Update specs --- app/config/specs/open-api3-1.8.x-client.json | 4 ++-- app/config/specs/open-api3-1.8.x-console.json | 4 ++-- app/config/specs/open-api3-1.8.x-server.json | 4 ++-- app/config/specs/open-api3-latest-client.json | 4 ++-- app/config/specs/open-api3-latest-console.json | 4 ++-- app/config/specs/open-api3-latest-server.json | 4 ++-- app/config/specs/swagger2-1.8.x-client.json | 4 ++-- app/config/specs/swagger2-1.8.x-console.json | 4 ++-- app/config/specs/swagger2-1.8.x-server.json | 4 ++-- app/config/specs/swagger2-latest-client.json | 4 ++-- app/config/specs/swagger2-latest-console.json | 4 ++-- app/config/specs/swagger2-latest-server.json | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index bf9f8bbece..b6a6a1afe3 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -5247,7 +5247,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -8389,7 +8389,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 73b8ebb1de..c086fe03d0 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -5646,7 +5646,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -33781,7 +33781,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index b345a36501..49adaeeefa 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -5198,7 +5198,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -24311,7 +24311,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index bf9f8bbece..b6a6a1afe3 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -5247,7 +5247,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -8389,7 +8389,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 73b8ebb1de..c086fe03d0 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -5646,7 +5646,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -33781,7 +33781,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index b345a36501..49adaeeefa 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -5198,7 +5198,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "tags": [ "databases" @@ -24311,7 +24311,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "tags": [ "tablesDB" diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 756d19fd53..89fc5e7e5c 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -5386,7 +5386,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -8463,7 +8463,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index df5d64cb8d..098f138b30 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -5805,7 +5805,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -33897,7 +33897,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 345b787f79..9c8ef73243 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -5345,7 +5345,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -24483,7 +24483,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 756d19fd53..89fc5e7e5c 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -5386,7 +5386,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -8463,7 +8463,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index df5d64cb8d..098f138b30 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -5805,7 +5805,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -33897,7 +33897,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 345b787f79..9c8ef73243 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -5345,7 +5345,7 @@ }, "\/databases\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "databasesCreateOperations", "consumes": [ "application\/json" @@ -24483,7 +24483,7 @@ }, "\/tablesdb\/transactions\/{transactionId}\/operations": { "post": { - "summary": "Create operations scoped to a transaction", + "summary": "Create operations", "operationId": "tablesDBCreateOperations", "consumes": [ "application\/json" From d6fc2806dcada52335eec9b7efbd42624e57c485 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 10 Oct 2025 13:26:55 +0530 Subject: [PATCH 272/274] fix: prevent empty releases in sdk release script --- src/Appwrite/Platform/Tasks/SDKs.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 87c70d214b..7bd3deabac 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -280,6 +280,20 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND continue; } + // Check if the latest commit on the target branch already has a release + $latestCommitCommand = 'gh api repos/' . $repoName . '/commits/' . $releaseTarget . ' --jq ".sha" 2>/dev/null'; + $latestCommitSha = trim(\shell_exec($latestCommitCommand) ?? ''); + + if (!empty($latestCommitSha)) { + $commitReleasesCommand = 'gh api repos/' . $repoName . '/releases --jq ".[] | select(.target_commitish == \"' . $releaseTarget . '\") | .tag_name" 2>/dev/null | head -n 1'; + $existingCommitRelease = trim(\shell_exec($commitReleasesCommand) ?? ''); + + if (!empty($existingCommitRelease)) { + Console::warning("Latest commit on {$releaseTarget} already has a release ({$existingCommitRelease}) for {$language['name']} SDK, skipping to avoid empty release..."); + continue; + } + } + $previousVersion = ''; $tagListCommand = 'gh release list --repo "' . $repoName . '" --limit 1 --json tagName --jq ".[0].tagName" 2>&1'; $previousVersion = trim(\shell_exec($tagListCommand) ?? ''); From 751d51ea2ebf9576fad0c3c8c984663f571c104d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 10 Oct 2025 15:37:29 +0530 Subject: [PATCH 273/274] update domains lib to 0.8.2 --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 1afa3f5109..908a4d9d99 100644 --- a/composer.lock +++ b/composer.lock @@ -3792,16 +3792,16 @@ }, { "name": "utopia-php/domains", - "version": "0.8.1", + "version": "0.8.2", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "d5f903e93c105407da6374e411c4805b7decd8a8" + "reference": "caa294dcebd05c8af876c8afef3e992faccdf645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/d5f903e93c105407da6374e411c4805b7decd8a8", - "reference": "d5f903e93c105407da6374e411c4805b7decd8a8", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/caa294dcebd05c8af876c8afef3e992faccdf645", + "reference": "caa294dcebd05c8af876c8afef3e992faccdf645", "shasum": "" }, "require": { @@ -3847,9 +3847,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.8.1" + "source": "https://github.com/utopia-php/domains/tree/0.8.2" }, - "time": "2025-10-03T11:58:53+00:00" + "time": "2025-10-06T09:56:54+00:00" }, { "name": "utopia-php/dsn", From 1c6b56385c2a6a70a0c9b919af15db8562bede51 Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 10 Oct 2025 18:26:00 +0530 Subject: [PATCH 274/274] fix: code 0 from databases. --- app/realtime.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index fccf5c9a20..e18ab8e10d 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -606,8 +606,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $message = $th->getMessage(); - // sanitize 5xx errors - if ($code >= 500 && !App::isDevelopment()) { + // sanitize 0 && 5xx errors + if (($code === 0 || $code >= 500) && !App::isDevelopment()) { $message = 'Error: Server Error'; } @@ -719,8 +719,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re $message = $th->getMessage(); - // sanitize 5xx errors - if ($code >= 500 && !App::isDevelopment()) { + // sanitize 0 && 5xx errors + if (($code === 0 || $code >= 500) && !App::isDevelopment()) { $message = 'Error: Server Error'; }