Merge branch '1.7.x' into update-tokens

This commit is contained in:
Darshan
2025-05-13 16:16:46 +05:30
committed by GitHub
130 changed files with 6504 additions and 151513 deletions
+1 -1
View File
@@ -87,7 +87,7 @@ _APP_EXECUTOR_HOST=http://exc1/v1
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_DELAY=
_APP_MAINTENANCE_START_TIME=12:00
_APP_MAINTENANCE_RETENTION_CACHE=2592000
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
+12 -2
View File
@@ -10,6 +10,7 @@ use Appwrite\Event\StatsUsage;
use Appwrite\Platform\Appwrite;
use Appwrite\Runtimes\Runtimes;
use Executor\Executor;
use Swoole\Timer;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\CLI;
@@ -25,6 +26,9 @@ use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\System\System;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use function Swoole\Coroutine\run;
// overwriting runtimes to be architecture agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
@@ -199,7 +203,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
};
}, ['pools', 'cache']);
CLI::setResource('queueForStatsUsage', function (Connection $publisher) {
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
@@ -263,6 +267,8 @@ CLI::setResource('logError', function (Registry $register) {
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
CLI::setResource('telemetry', fn () => new NoTelemetry());
$platform = new Appwrite();
$args = $platform->getEnv('argv');
@@ -286,6 +292,10 @@ $cli
'Task',
$taskName,
]);
Timer::clearAll();
});
$cli->run();
$cli->shutdown()->action(fn () => Timer::clearAll());
run($cli->run(...));
+14
View File
@@ -2359,6 +2359,20 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_expired'),
'type' => Database::INDEX_KEY,
'attributes' => ['expired'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_session_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['sessionInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
+51 -2
View File
@@ -367,7 +367,21 @@ return [
'attributes' => ['pingedAt'],
'lengths' => [],
'orders' => [],
]
],
[
'$id' => ID::custom('_key_database'),
'type' => Database::INDEX_KEY,
'attributes' => ['database'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_region_accessed_at'),
'type' => Database::INDEX_KEY,
'attributes' => ['region', 'accessedAt'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -491,6 +505,20 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_project_id_region'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectId', 'region'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_region_rt_active'),
'type' => Database::INDEX_KEY,
'attributes' => ['region', 'resourceType', 'active'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1389,6 +1417,13 @@ return [
'lengths' => [16],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_piid_riid_rt'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1684,7 +1719,14 @@ return [
'attributes' => ['resourceType'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
]
],
[
'$id' => ID::custom('_key_piid_riid_rt'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -1832,6 +1874,13 @@ return [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_piid_prid_rt'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId', 'providerRepositoryId'],
'lengths' => [],
'orders' => [],
],
],
],
+21
View File
@@ -1809,6 +1809,13 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_resource_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['resourceInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -2073,6 +2080,13 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_function_internal_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['functionInternalId'],
'lengths' => [],
'orders' => [],
],
],
],
@@ -2202,6 +2216,13 @@ return [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_resource_internal_id_resource_type'),
'type' => Database::INDEX_KEY,
'attributes' => ['resourceInternalId', 'resourceType'],
'lengths' => [],
'orders' => [],
],
],
],
+11 -11
View File
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '15.0.1',
'version' => '15.0.2',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -245,7 +245,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '16.0.0',
'version' => '16.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -263,7 +263,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '14.0.0',
'version' => '14.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -281,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '14.0.0',
'version' => '14.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -299,7 +299,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '10.0.0',
'version' => '10.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -317,7 +317,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '15.0.0',
'version' => '15.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -335,7 +335,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '0.5.0',
'version' => '0.6.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -353,7 +353,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.12.0',
'version' => '0.13.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '15.0.0',
'version' => '15.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -389,7 +389,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '8.0.0',
'version' => '8.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -411,7 +411,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '9.0.0',
'version' => '9.1.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
-3
View File
@@ -65,9 +65,6 @@ return [
'tests' => false,
'optional' => true,
'icon' => '/images/services/databases.png',
'globalAttributes' => [
'databaseId'
]
],
'locale' => [
'key' => 'locale',
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+112 -78
View File
@@ -2646,7 +2646,10 @@
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-token-email.md",
"rate-limit": 10,
"rate-time": 3600,
"rate-key": "url:{url},email:{param-email}",
"rate-key": [
"url:{url},email:{param-email}",
"url:{url},ip:{ip}"
],
"scope": "sessions.write",
"platforms": [
"server",
@@ -4427,7 +4430,7 @@
"tags": [
"databases"
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n",
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
"responses": {
"201": {
"description": "Document",
@@ -4459,6 +4462,51 @@
"server"
],
"packaging": false,
"methods": [
{
"name": "createDocument",
"parameters": [
"databaseId",
"collectionId",
"documentId",
"data",
"permissions"
],
"required": [
"databaseId",
"collectionId",
"documentId",
"data"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/document"
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/components\/schemas\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n"
}
],
"auth": {
"Project": []
}
@@ -4515,12 +4563,16 @@
"items": {
"type": "string"
}
},
"documents": {
"type": "array",
"description": "Array of documents data as JSON objects.",
"x-example": null,
"items": {
"type": "object"
}
}
},
"required": [
"documentId",
"data"
]
}
}
}
}
@@ -4744,7 +4796,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 113,
"weight": 115,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4828,7 +4880,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 305,
"weight": 308,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4914,7 +4966,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 304,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5029,7 +5081,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 306,
"weight": 309,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5103,7 +5155,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 330,
"weight": 333,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5155,7 +5207,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 329,
"weight": 332,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5207,7 +5259,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 117,
"weight": 120,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5259,7 +5311,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 118,
"weight": 121,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5311,7 +5363,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 122,
"weight": 125,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5363,7 +5415,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 119,
"weight": 122,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5415,7 +5467,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 120,
"weight": 123,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5467,7 +5519,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 121,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5519,7 +5571,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 123,
"weight": 126,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5571,7 +5623,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 124,
"weight": 127,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5623,7 +5675,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 375,
"weight": 378,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5706,7 +5758,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 379,
"weight": 382,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5781,7 +5833,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 207,
"weight": 210,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5867,7 +5919,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 206,
"weight": 209,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -5965,7 +6017,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 208,
"weight": 211,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6037,7 +6089,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 213,
"weight": 216,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6126,7 +6178,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 214,
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6193,7 +6245,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 210,
"weight": 213,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6260,7 +6312,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 209,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6477,7 +6529,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 211,
"weight": 214,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6551,7 +6603,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 218,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6627,7 +6679,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 217,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6712,7 +6764,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 219,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6774,7 +6826,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 221,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6848,7 +6900,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 223,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6912,7 +6964,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 225,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6998,7 +7050,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 224,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7109,7 +7161,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 226,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7181,7 +7233,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 227,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7268,7 +7320,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 229,
"weight": 232,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7342,7 +7394,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 228,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7440,7 +7492,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 220,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7501,7 +7553,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 222,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7564,85 +7616,67 @@
"tags": [
{
"name": "account",
"description": "The Account service allows you to authenticate and manage a user account.",
"x-globalAttributes": []
"description": "The Account service allows you to authenticate and manage a user account."
},
{
"name": "avatars",
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.",
"x-globalAttributes": []
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars."
},
{
"name": "databases",
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents",
"x-globalAttributes": [
"databaseId"
]
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents"
},
{
"name": "locale",
"description": "The Locale service allows you to customize your app based on your users' location.",
"x-globalAttributes": []
"description": "The Locale service allows you to customize your app based on your users' location."
},
{
"name": "health",
"description": "The Health service allows you to both validate and monitor your Appwrite server's health.",
"x-globalAttributes": []
"description": "The Health service allows you to both validate and monitor your Appwrite server's health."
},
{
"name": "projects",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "project",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "storage",
"description": "The Storage service allows you to manage your project files.",
"x-globalAttributes": []
"description": "The Storage service allows you to manage your project files."
},
{
"name": "teams",
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources",
"x-globalAttributes": []
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources"
},
{
"name": "users",
"description": "The Users service allows you to manage your project users.",
"x-globalAttributes": []
"description": "The Users service allows you to manage your project users."
},
{
"name": "functions",
"description": "The Functions Service allows you view, create and manage your Cloud Functions.",
"x-globalAttributes": []
"description": "The Functions Service allows you view, create and manage your Cloud Functions."
},
{
"name": "proxy",
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration.",
"x-globalAttributes": []
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration."
},
{
"name": "graphql",
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL.",
"x-globalAttributes": []
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL."
},
{
"name": "console",
"description": "The Console service allows you to interact with console relevant informations.",
"x-globalAttributes": []
"description": "The Console service allows you to interact with console relevant informations."
},
{
"name": "migrations",
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project.",
"x-globalAttributes": []
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project."
},
{
"name": "messaging",
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.).",
"x-globalAttributes": []
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.)."
}
],
"components": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+115 -80
View File
@@ -2765,7 +2765,10 @@
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/account\/create-token-email.md",
"rate-limit": 10,
"rate-time": 3600,
"rate-key": "url:{url},email:{param-email}",
"rate-key": [
"url:{url},email:{param-email}",
"url:{url},ip:{ip}"
],
"scope": "sessions.write",
"platforms": [
"server",
@@ -4571,7 +4574,7 @@
"tags": [
"databases"
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n",
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
"responses": {
"201": {
"description": "Document",
@@ -4599,6 +4602,51 @@
"server"
],
"packaging": false,
"methods": [
{
"name": "createDocument",
"parameters": [
"databaseId",
"collectionId",
"documentId",
"data",
"permissions"
],
"required": [
"databaseId",
"collectionId",
"documentId",
"data"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/document"
}
],
"description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console."
},
{
"name": "createDocuments",
"parameters": [
"databaseId",
"collectionId",
"documents"
],
"required": [
"databaseId",
"collectionId",
"documents"
],
"responses": [
{
"code": 201,
"model": "#\/definitions\/documentList"
}
],
"description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n"
}
],
"auth": {
"Project": []
}
@@ -4636,13 +4684,13 @@
"documentId": {
"type": "string",
"description": "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.",
"default": null,
"default": "",
"x-example": "<DOCUMENT_ID>"
},
"data": {
"type": "object",
"description": "Document data as JSON object.",
"default": {},
"default": [],
"x-example": "{}"
},
"permissions": {
@@ -4653,12 +4701,17 @@
"items": {
"type": "string"
}
},
"documents": {
"type": "array",
"description": "Array of documents data as JSON objects.",
"default": [],
"x-example": null,
"items": {
"type": "object"
}
}
},
"required": [
"documentId",
"data"
]
}
}
}
]
@@ -4874,7 +4927,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 113,
"weight": 115,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4952,7 +5005,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 305,
"weight": 308,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5035,7 +5088,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 304,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5152,7 +5205,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 306,
"weight": 309,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5224,7 +5277,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 330,
"weight": 333,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5298,7 +5351,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 329,
"weight": 332,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5370,7 +5423,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 117,
"weight": 120,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5422,7 +5475,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 118,
"weight": 121,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5474,7 +5527,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 122,
"weight": 125,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5526,7 +5579,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 119,
"weight": 122,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5578,7 +5631,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 120,
"weight": 123,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5630,7 +5683,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 121,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5682,7 +5735,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 123,
"weight": 126,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5734,7 +5787,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 124,
"weight": 127,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5788,7 +5841,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 375,
"weight": 378,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5873,7 +5926,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 379,
"weight": 382,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5944,7 +5997,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 207,
"weight": 210,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6027,7 +6080,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 206,
"weight": 209,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6117,7 +6170,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 208,
"weight": 211,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6187,7 +6240,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 213,
"weight": 216,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6276,7 +6329,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 214,
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6346,7 +6399,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 210,
"weight": 213,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6416,7 +6469,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 209,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6614,7 +6667,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 211,
"weight": 214,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6684,7 +6737,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 218,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6759,7 +6812,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 217,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6849,7 +6902,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 219,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6911,7 +6964,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 221,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6986,7 +7039,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 223,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7048,7 +7101,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 225,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7131,7 +7184,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 224,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7244,7 +7297,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 226,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7314,7 +7367,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 227,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7400,7 +7453,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 229,
"weight": 232,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7472,7 +7525,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 228,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7566,7 +7619,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 220,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7627,7 +7680,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 222,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7687,85 +7740,67 @@
"tags": [
{
"name": "account",
"description": "The Account service allows you to authenticate and manage a user account.",
"x-globalAttributes": []
"description": "The Account service allows you to authenticate and manage a user account."
},
{
"name": "avatars",
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.",
"x-globalAttributes": []
"description": "The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars."
},
{
"name": "databases",
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents",
"x-globalAttributes": [
"databaseId"
]
"description": "The Databases service allows you to create structured collections of documents, query and filter lists of documents"
},
{
"name": "locale",
"description": "The Locale service allows you to customize your app based on your users' location.",
"x-globalAttributes": []
"description": "The Locale service allows you to customize your app based on your users' location."
},
{
"name": "health",
"description": "The Health service allows you to both validate and monitor your Appwrite server's health.",
"x-globalAttributes": []
"description": "The Health service allows you to both validate and monitor your Appwrite server's health."
},
{
"name": "projects",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "project",
"description": "The Project service allows you to manage all the projects in your Appwrite server.",
"x-globalAttributes": []
"description": "The Project service allows you to manage all the projects in your Appwrite server."
},
{
"name": "storage",
"description": "The Storage service allows you to manage your project files.",
"x-globalAttributes": []
"description": "The Storage service allows you to manage your project files."
},
{
"name": "teams",
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources",
"x-globalAttributes": []
"description": "The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources"
},
{
"name": "users",
"description": "The Users service allows you to manage your project users.",
"x-globalAttributes": []
"description": "The Users service allows you to manage your project users."
},
{
"name": "functions",
"description": "The Functions Service allows you view, create and manage your Cloud Functions.",
"x-globalAttributes": []
"description": "The Functions Service allows you view, create and manage your Cloud Functions."
},
{
"name": "proxy",
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration.",
"x-globalAttributes": []
"description": "The Proxy Service allows you to configure actions for your domains beyond DNS configuration."
},
{
"name": "graphql",
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL.",
"x-globalAttributes": []
"description": "The GraphQL API allows you to query and mutate your Appwrite server using GraphQL."
},
{
"name": "console",
"description": "The Console service allows you to interact with console relevant informations.",
"x-globalAttributes": []
"description": "The Console service allows you to interact with console relevant informations."
},
{
"name": "migrations",
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project.",
"x-globalAttributes": []
"description": "The Migrations service allows you to migrate third-party data to your Appwrite project."
},
{
"name": "messaging",
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.).",
"x-globalAttributes": []
"description": "The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.)."
}
],
"definitions": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+12 -3
View File
@@ -854,7 +854,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_CPUS',
'description' => 'Deprecated since 1.7.0. The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
'description' => 'Deprecated since 1.7.0. The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty or 0, CPU limit will be disabled',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
@@ -872,7 +872,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
'description' => 'Deprecated since 1.7.0. The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
'description' => 'Deprecated since 1.7.0. The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty or 0, memory limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
@@ -1154,13 +1154,22 @@ return [
],
[
'name' => '_APP_MAINTENANCE_DELAY',
'description' => 'Delay value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 0 seconds.',
'description' => 'Deprecated with 1.6.2 use _APP_MAINTENANCE_START_TIME instead to run the maintenance at a specific time per day.',
'introduction' => '1.5.0',
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_START_TIME',
'description' => 'The time of day (in 24-hour format) when the maintenance process should start. The default value is 00:00.',
'introduction' => '1.6.2',
'default' => '00:00',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_CACHE',
'description' => 'The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days).',
+10 -11
View File
@@ -719,9 +719,7 @@ App::delete('/v1/account/sessions/:sessionId')
continue;
}
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
return $dbForProject->deleteDocument('sessions', $session->getId());
});
$dbForProject->deleteDocument('sessions', $session->getId());
unset($sessions[$key]);
@@ -2001,6 +1999,7 @@ App::post('/v1/account/tokens/magic-url')
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
if ($smtpEnabled) {
@@ -2100,7 +2099,7 @@ App::post('/v1/account/tokens/email')
contentType: ContentType::JSON,
))
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->param('userId', '', new CustomId(), 'User 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.')
->param('email', '', new Email(), 'User email.')
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
@@ -2769,7 +2768,7 @@ App::patch('/v1/account/name')
$user->setAttribute('name', $name);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents->setParam('userId', $user->getId());
@@ -2844,7 +2843,7 @@ App::patch('/v1/account/password')
->setAttribute('hash', Auth::DEFAULT_ALGO)
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents->setParam('userId', $user->getId());
@@ -2929,7 +2928,7 @@ App::patch('/v1/account/email')
}
try {
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
/**
* @var Document $oldTarget
*/
@@ -3015,7 +3014,7 @@ App::patch('/v1/account/phone')
}
try {
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
/**
* @var Document $oldTarget
*/
@@ -3065,7 +3064,7 @@ App::patch('/v1/account/prefs')
$user->setAttribute('prefs', $prefs);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents->setParam('userId', $user->getId());
@@ -3103,7 +3102,7 @@ App::patch('/v1/account/status')
$user->setAttribute('status', false);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$queueForEvents
->setParam('userId', $user->getId())
@@ -3867,7 +3866,7 @@ App::patch('/v1/account/mfa')
$user->setAttribute('mfa', $mfa);
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
if ($mfa) {
$factors = $session->getAttribute('factors', []);
+491 -110
View File
@@ -10,6 +10,7 @@ use Appwrite\Network\Validator\Email;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
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\Queries\Attributes;
@@ -35,6 +36,7 @@ use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -3170,7 +3172,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default'])
->desc('Create document')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create')
->label('scope', 'documents.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'document.create')
@@ -3180,6 +3181,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label(
'sdk',
// Using multiple methods to abstract the complexity for SDK users
[
new Method(
namespace: 'databases',
@@ -3193,90 +3195,158 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
model: Response::MODEL_DOCUMENT,
)
],
contentType: ContentType::JSON
contentType: ContentType::JSON,
parameters: [
new Parameter('databaseId', optional: false),
new Parameter('collectionId', optional: false),
new Parameter('documentId', optional: false),
new Parameter('data', optional: false),
new Parameter('permissions', optional: true),
]
),
new Method(
namespace: 'databases',
group: 'documents',
name: 'createDocuments',
description: '/docs/references/databases/create-documents.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_DOCUMENT_LIST,
)
],
contentType: ContentType::JSON,
parameters: [
new Parameter('databaseId', optional: false),
new Parameter('collectionId', optional: false),
new Parameter('documents', optional: false),
]
)
]
)
->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.')
->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('data', [], new JSON(), 'Document data as JSON object.')
->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'])
->inject('response')
->inject('dbForProject')
->inject('user')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $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) {
$data = \is_string($data)
? \json_decode($data, true)
: $data;
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
/**
* Determine which internal path to call, single or bulk
*/
if (empty($data) && empty($documents)) {
// No single or bulk documents provided
throw new Exception(Exception::DOCUMENT_MISSING_DATA);
}
if (isset($data['$id'])) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead');
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, documents');
}
if (!empty($data) && empty($documentId)) {
// Single document provided without document ID
throw new Exception(Exception::DOCUMENT_MISSING_DATA, 'Document ID is required when creating a single document');
}
if (!empty($documents) && !empty($documentId)) {
// Bulk documents provided with document ID
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Param "documentId" is disallowed when creating multiple documents, set "$id" in each document 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 documents, set "$permissions" in each document instead');
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isBulk = true;
if (!empty($data)) {
// Single document provided, convert to single item array
// But remember that it was single to respond with a single document
$isBulk = false;
$documents = [$data];
}
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$allowedPermissions = [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
];
$hasRelationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
// Map aggregate permissions to into the set of individual permissions they represent.
$permissions = Permission::aggregate($permissions, $allowedPermissions);
// Add permissions for current the user if none were provided.
if (\is_null($permissions)) {
$permissions = [];
if (!empty($user->getId())) {
foreach ($allowedPermissions as $permission) {
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
}
}
if ($isBulk && $hasRelationships) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for collections with relationship attributes');
}
// Users can only manage their own roles, API keys and Admin users can manage any
if (!$isAPIKey && !$isPrivilegedUser) {
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(', ', Authorization::getRoles()) . ')');
$setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk) {
$allowedPermissions = [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
];
// If bulk, we need to validate permissions explicitly per document
if ($isBulk) {
$permissions = $document['$permissions'] ?? null;
if (!empty($permissions)) {
$validator = new Permissions();
if (!$validator->isValid($permissions)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription());
}
}
}
}
$data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers
$data['$id'] = $documentId == 'unique()' ? ID::unique() : $documentId;
$data['$permissions'] = $permissions;
$document = new Document($data);
$permissions = Permission::aggregate($permissions, $allowedPermissions);
// Add permissions for current the user if none were provided.
if (\is_null($permissions)) {
$permissions = [];
if (!empty($user->getId())) {
foreach ($allowedPermissions as $permission) {
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
}
}
}
// Users can only manage their own roles, API keys and Admin users can manage any
if (!$isAPIKey && !$isPrivilegedUser) {
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(', ', Authorization::getRoles()) . ')');
}
}
}
}
$document->setAttribute('$permissions', $permissions);
};
$operations = 0;
@@ -3362,21 +3432,57 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
}
};
$checkPermissions($collection, $document, Database::PERMISSION_CREATE);
$documents = \array_map(function ($document) use ($collection, $permissions, $checkPermissions, $isBulk, $documentId, $setPermissions) {
$document['$collection'] = $collection->getId();
// Determine the source ID depending on whether it's a bulk operation.
$sourceId = $isBulk
? ($document['$id'] ?? ID::unique())
: $documentId;
// If bulk, we need to validate ID explicitly
if ($isBulk) {
$validator = new CustomId();
if (!$validator->isValid($sourceId)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, $validator->getDescription());
}
}
// Assign a unique ID if needed, otherwise use the provided ID.
$document['$id'] = $sourceId === 'unique()' ? ID::unique() : $sourceId;
$document = new Document($document);
$setPermissions($document, $permissions);
$checkPermissions($collection, $document, Database::PERMISSION_CREATE);
return $document;
}, $documents);
try {
$document = $dbForProject->createDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document);
$dbForProject->createDocuments(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$documents
);
} catch (StructureException $e) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage());
} catch (DuplicateException $e) {
} catch (DuplicateException) {
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
} catch (NotFoundException $e) {
} catch (NotFoundException) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (TimeoutException) {
throw new Exception(Exception::DATABASE_TIMEOUT);
}
$queueForEvents
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setContext('collection', $collection)
->setContext('database', $database);
// Add $collectionId and $databaseId for all documents
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) {
$document->removeAttribute('$collection');
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
@@ -3408,33 +3514,33 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
}
};
$processDocument($collection, $document);
foreach ($documents as $document) {
$processDocument($collection, $document);
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); // per collection
$response->addHeader('X-Debug-Operations', $operations);
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($document, Response::MODEL_DOCUMENT);
if ($isBulk) {
$response->dynamic(new Document([
'total' => count($documents),
'documents' => $documents
]), Response::MODEL_DOCUMENT_LIST);
$relationships = \array_map(
fn ($document) => $document->getAttribute('key'),
\array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
)
);
return;
}
$queueForEvents
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setParam('documentId', $document->getId())
->setContext('collection', $collection)
->setContext('database', $database)
->setPayload($response->getPayload(), sensitive: $relationships);
->setParam('documentId', $documents[0]->getId())
->setEvent('databases.[databaseId].collections.[collectionId].documents.[documentId].create');
$response->dynamic(
$documents[0],
Response::MODEL_DOCUMENT
);
});
App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
@@ -3526,7 +3632,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$operations++;
$document->removeAttribute('$collection');
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
@@ -3579,10 +3684,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
$response->addHeader('X-Debug-Operations', $operations);
->addMetric(METRIC_DATABASES_OPERATIONS_READS, \max(1, $operations))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), \max(1, $operations));
$select = \array_reduce($queries, function ($result, $query) {
return $result || ($query->getMethod() === Query::TYPE_SELECT);
@@ -3721,10 +3824,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
$processDocument($collection, $document);
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
$response->addHeader('X-Debug-Operations', $operations);
->addMetric(METRIC_DATABASES_OPERATIONS_READS, \max(1, $operations))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), \max(1, $operations));
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
@@ -3881,7 +3982,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->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) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data) && \is_null($permissions)) {
@@ -3898,7 +3998,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
@@ -4021,19 +4120,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$setCollection($collection, $newDocument);
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations);
$response->addHeader('X-Debug-Operations', $operations);
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations));
try {
$document = $dbForProject->withRequestTimestamp(
$requestTimestamp,
fn () => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
)
$document = $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
@@ -4080,8 +4174,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$processDocument($collection, $document);
$response->dynamic($document, Response::MODEL_DOCUMENT);
$relationships = \array_map(
fn ($document) => $document->getAttribute('key'),
\array_filter(
@@ -4097,6 +4189,202 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->setContext('collection', $collection)
->setContext('database', $database)
->setPayload($response->getPayload(), sensitive: $relationships);
$response->dynamic($document, Response::MODEL_DOCUMENT);
});
App::patch('/v1/databases/:databaseId/collections/:collectionId/documents')
->desc('Update documents')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'documents.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'updateDocuments',
description: '/docs/references/databases/update-documents.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT_LIST,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->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)
->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) {
$data = \is_string($data)
? \json_decode($data, true)
: $data;
if (empty($data)) {
throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD);
}
$database = $dbForProject->getDocument('databases', $databaseId);
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$hasRelationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
if ($hasRelationships) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk update is not supported for collections with relationship attributes');
}
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
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());
}
}
$documents = [];
try {
$modified = $dbForProject->updateDocuments(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
new Document($data),
$queries,
onNext: function (Document $document) use ($plan, &$documents) {
if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) {
$documents[] = $document;
}
},
);
} catch (StructureException $e) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage());
} catch (NotFoundException) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (TimeoutException) {
throw new Exception(Exception::DATABASE_TIMEOUT);
}
foreach ($documents as $document) {
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified));
$response->dynamic(new Document([
'total' => $modified,
'documents' => $documents
]), Response::MODEL_DOCUMENT_LIST);
});
App::put('/v1/databases/:databaseId/collections/:collectionId/documents')
->desc('Create or update documents')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'documents.upsert')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'upsertDocuments',
description: '/docs/references/databases/upsert-documents.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT_LIST,
)
],
contentType: ContentType::JSON
))
->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.', true, ['plan'])
->inject('response')
->inject('dbForProject')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (string $databaseId, string $collectionId, array $documents, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan) {
$database = $dbForProject->getDocument('databases', $databaseId);
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$hasRelationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
if ($hasRelationships) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for collections with relationship attributes');
}
foreach ($documents as $key => $document) {
$documents[$key] = new Document($document);
}
$upserted = [];
$modified = $dbForProject->createOrUpdateDocuments(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$documents,
onNext: function (Document $document) use ($plan, &$upserted) {
if (\count($upserted) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) {
$upserted[] = $document;
}
},
);
foreach ($upserted as $document) {
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified));
$response->dynamic(new Document([
'total' => $modified,
'documents' => $upserted
]), Response::MODEL_DOCUMENT_LIST);
});
App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId')
@@ -4157,18 +4445,20 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
}
try {
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) {
$dbForProject->deleteDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$documentId
);
});
$dbForProject->deleteDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$documentId
);
} catch (NotFoundException $e) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$operations = 0;
// Add $collectionId and $databaseId for all documents
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) {
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, &$operations) {
$operations++;
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
@@ -4203,10 +4493,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$processDocument($collection, $document);
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection
$response->addHeader('X-Debug-Operations', 1);
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations));
$relationships = \array_map(
fn ($document) => $document->getAttribute('key'),
@@ -4227,6 +4515,99 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$response->noContent();
});
App::delete('/v1/databases/:databaseId/collections/:collectionId/documents')
->desc('Delete documents')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'documents.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'deleteDocuments',
description: '/docs/references/databases/delete-documents.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DOCUMENT_LIST,
)
],
contentType: ContentType::JSON
))
->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)
->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) {
$database = $dbForProject->getDocument('databases', $databaseId);
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$hasRelationships = \array_filter(
$collection->getAttribute('attributes', []),
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
);
if ($hasRelationships) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk delete is not supported for collections with relationship attributes');
}
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$documents = [];
try {
$modified = $dbForProject->deleteDocuments(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$queries,
onNext: function (Document $document) use ($plan, &$documents) {
if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) {
$documents[] = $document;
}
},
);
} catch (NotFoundException) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (TimeoutException) {
throw new Exception(Exception::DATABASE_TIMEOUT);
}
foreach ($documents as $document) {
$document->setAttribute('$databaseId', $database->getId());
$document->setAttribute('$collectionId', $collection->getId());
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified));
$response->dynamic(new Document([
'total' => $modified,
'documents' => $documents,
]), Response::MODEL_DOCUMENT_LIST);
});
App::get('/v1/databases/usage')
->desc('Get databases usage stats')
->groups(['api', 'database', 'usage'])
@@ -4512,7 +4893,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
$response->dynamic(new Document([
'range' => $range,
'documentsTotal' => $usage[$metrics[0]]['total'],
'documents' => $usage[$metrics[0]]['data'],
'documentsTotal' => $usage[$metrics[0]]['total'],
'documents' => $usage[$metrics[0]]['data'],
]), Response::MODEL_USAGE_COLLECTION);
});
+3 -3
View File
@@ -401,15 +401,15 @@ App::post('/v1/storage/buckets/:bucketId/files')
group: 'files',
name: 'createFile',
description: '/docs/references/storage/create-file.md',
type: MethodType::UPLOAD,
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
requestType: 'multipart/form-data',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_FILE,
)
]
],
type: MethodType::UPLOAD,
requestType: ContentType::MULTIPART
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new CustomId(), 'File 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.')
+1 -3
View File
@@ -323,9 +323,7 @@ App::put('/v1/teams/:teamId')
->setAttribute('name', $name)
->setAttribute('search', implode(' ', [$teamId, $name]));
$team = $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $team) {
return $dbForProject->updateDocument('teams', $team->getId(), $team);
});
$team = $dbForProject->updateDocument('teams', $team->getId(), $team);
$queueForEvents->setParam('teamId', $team->getId());
+4
View File
@@ -9,6 +9,8 @@ use Appwrite\Auth\Validator\PasswordDictionary;
use Appwrite\Auth\Validator\PasswordHistory;
use Appwrite\Auth\Validator\PersonalData;
use Appwrite\Auth\Validator\Phone;
use Appwrite\Deletes\Identities as DeleteIdentities;
use Appwrite\Deletes\Targets as DeleteTargets;
use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
@@ -2291,6 +2293,8 @@ App::delete('/v1/users/:userId')
$clone = clone $user;
$dbForProject->deleteDocument('users', $userId);
DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
+19 -2
View File
@@ -92,8 +92,20 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
$value = 1;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$value = -1;
switch ($event) {
case Database::EVENT_DOCUMENT_DELETE:
$value = -1;
break;
case Database::EVENT_DOCUMENTS_DELETE:
$value = -1 * $document->getAttribute('modified', 0);
break;
case Database::EVENT_DOCUMENTS_CREATE:
$value = $document->getAttribute('modified', 0);
break;
case Database::EVENT_DOCUMENTS_UPSERT:
$value = $document->getAttribute('created', 0);
break;
}
switch (true) {
@@ -339,6 +351,8 @@ App::init()
*/
$method = $route->getLabel('sdk', false);
// Take the first method if there's more than one,
// namespace can not differ between methods on the same route
if (\is_array($method)) {
$method = $method[0];
}
@@ -525,6 +539,9 @@ App::init()
$dbForProject
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
$project,
$document,
+90 -84
View File
@@ -46,29 +46,6 @@ $http = new Server(
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
$http
->set([
'worker_num' => $totalWorkers,
'dispatch_func' => 'dispatch',
'open_http2_protocol' => true,
'http_compression' => false,
'package_max_length' => $payloadSize,
'buffer_output_size' => $payloadSize,
'task_worker_num' => 1, // required for the task to fetch domains background
]);
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
Console::success('Starting reload...');
});
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
/**
* Assigns HTTP requests to worker threads by analyzing its payload/content.
*
@@ -86,76 +63,105 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
*/
function dispatch(Server $server, int $fd, int $type, $data = null): int
{
global $totalWorkers, $domains;
$resolveWorkerId = function (Server $server, $data = null) {
global $totalWorkers, $domains;
// If data is not set we can send request to any worker
// first we try to pick idle worker, if not we randomly pick a worker
if ($data === null) {
// If data is not set we can send request to any worker
// first we try to pick idle worker, if not we randomly pick a worker
if ($data === null) {
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
return rand(0, $totalWorkers - 1);
}
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
// Each worker has numeric ID, starting from 0 and incrementing
// From 0 to riskyWorkers, we consider safe workers
// From riskyWorkers to totalWorkers, we consider risky workers
$riskyWorkers = (int)floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
$domain = '';
// max up to 3 as first line has request details and second line has host
$lines = explode("\n", $data, 3);
$request = $lines[0];
if (count($lines) > 1) {
$domain = trim(explode('Host: ', $lines[1])[1]);
}
// Sync executions are considered risky
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
}
if ($risky) {
// If risky request, only consider risky workers
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
// If idle worker found, give to him
return $j;
}
}
// If no idle workers, give to random risky worker
$worker = rand($riskyWorkers, $totalWorkers - 1);
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
}
// If safe request, give to any idle worker
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
return rand(0, $totalWorkers - 1);
}
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
// Each worker has numeric ID, starting from 0 and incrementing
// From 0 to riskyWorkers, we consider safe workers
// From riskyWorkers to totalWorkers, we consider risky workers
$riskyWorkers = (int) floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
$domain = '';
// max up to 3 as first line has request details and second line has host
$lines = explode("\n", $data, 3);
$request = $lines[0];
if (count($lines) > 1) {
$domain = trim(explode('Host: ', $lines[1])[1]);
}
// Sync executions are considered risky
$risky = false;
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
$risky = true;
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
$risky = true;
} elseif ($domains->get(md5($domain), 'value') === 1) {
// executions request coming from custom domain
$risky = true;
}
if ($risky) {
// If risky request, only consider risky workers
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
// If idle worker found, give to him
return $j;
}
}
// If no idle workers, give to random risky worker
$worker = rand($riskyWorkers, $totalWorkers - 1);
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
// If no idle worker found, give to random safe worker
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
$worker = rand(0, $riskyWorkers - 1);
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
}
// If safe request, give to any idle worker
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
for ($i = 0; $i < $totalWorkers; $i++) {
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
return $i;
}
}
// If no idle worker found, give to random safe worker
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
$worker = rand(0, $riskyWorkers - 1);
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
return $worker;
};
$workerId = $resolveWorkerId($server, $data);
$server->bind($fd, $workerId);
return $workerId;
}
$http
->set([
Constant::OPTION_WORKER_NUM => $totalWorkers,
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
Constant::OPTION_HTTP_COMPRESSION => false,
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
]);
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
Console::success('Starting reload...');
});
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
include __DIR__ . '/controllers/general.php';
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
@@ -540,7 +546,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
});
// Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory
$http->on('Task', function () use ($register, $domains) {
$http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
$lastSyncUpdate = null;
$pools = $register->get('pools');
App::setResource('pools', fn () => $pools);
+1
View File
@@ -26,6 +26,7 @@ const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
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_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
+6 -5
View File
@@ -15,6 +15,7 @@ use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Adapter\SQL;
use Utopia\Database\PDO;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Logger\Adapter\AppSignal;
@@ -216,11 +217,11 @@ $register->set('pools', function () {
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true
\PDO::ATTR_TIMEOUT => 3, // Seconds
\PDO::ATTR_PERSISTENT => false,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => true,
\PDO::ATTR_STRINGIFY_FETCHES => true
));
});
},
+1
View File
@@ -151,6 +151,7 @@ $image = $this->getParam('image', '');
- _APP_LOGGING_CONFIG
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_DELAY
- _APP_MAINTENANCE_START_TIME
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_MAINTENANCE_RETENTION_CACHE
- _APP_MAINTENANCE_RETENTION_ABUSE
+4 -4
View File
@@ -31,7 +31,7 @@
}
},
"require": {
"php": ">=8.0.0",
"php": ">=8.3.0",
"ext-curl": "*",
"ext-imagick": "*",
"ext-mbstring": "*",
@@ -49,11 +49,11 @@
"utopia-php/abuse": "0.52.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.55.*",
"utopia-php/cache": "0.12.*",
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/detector": "0.1.*",
"utopia-php/database": "0.66.*",
"utopia-php/database": "0.68.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
@@ -62,7 +62,7 @@
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.9.1",
"utopia-php/migration": "0.9.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
+1 -1
View File
@@ -753,7 +753,7 @@ services:
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_MAINTENANCE_DELAY
- _APP_MAINTENANCE_START_TIME
- _APP_DATABASE_SHARED_TABLES
appwrite-task-stats-resources:
@@ -0,0 +1,14 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
Databases databases = Databases(client);
DocumentList result = await databases.createDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documents: [],
);
@@ -0,0 +1,14 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
Databases databases = Databases(client);
await databases.deleteDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
queries: [], // (optional)
);
@@ -0,0 +1,15 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
Databases databases = Databases(client);
DocumentList result = await databases.updateDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
data: {}, // (optional)
queries: [], // (optional)
);
@@ -0,0 +1,14 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
Databases databases = Databases(client);
DocumentList result = await databases.upsertDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documents: [], // (optional)
);
@@ -0,0 +1,14 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
const databases = new Databases(client);
const response = await databases.createDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents
);
@@ -0,0 +1,14 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
const response = await databases.deleteDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // queries (optional)
);
@@ -0,0 +1,15 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
const response = await databases.updateDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
{}, // data (optional)
[] // queries (optional)
);
@@ -0,0 +1,14 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
const response = await databases.upsertDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents (optional)
);
@@ -0,0 +1,16 @@
using Appwrite;
using Appwrite.Models;
using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetSession(""); // The user session to authenticate with
Databases databases = new Databases(client);
DocumentList result = await databases.CreateDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: new List<object>()
);
@@ -0,0 +1,16 @@
using Appwrite;
using Appwrite.Models;
using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);
await databases.DeleteDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
queries: new List<string>() // optional
);
@@ -0,0 +1,17 @@
using Appwrite;
using Appwrite.Models;
using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);
DocumentList result = await databases.UpdateDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
data: [object], // optional
queries: new List<string>() // optional
);
@@ -0,0 +1,16 @@
using Appwrite;
using Appwrite.Models;
using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);
DocumentList result = await databases.UpsertDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: new List<object>() // optional
);
@@ -0,0 +1,28 @@
package main
import (
"fmt"
"github.com/appwrite/sdk-for-go/client"
"github.com/appwrite/sdk-for-go/databases"
)
func main() {
client := client.NewClient()
client.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
client.SetProject("<YOUR_PROJECT_ID>") // Your project ID
client.SetSession("") // The user session to authenticate with
service := databases.NewDatabases(client)
response, error := service.CreateDocuments(
"<DATABASE_ID>",
"<COLLECTION_ID>",
[]interface{}{},
)
if error != nil {
panic(error)
}
fmt.Println(response)
}
@@ -0,0 +1,28 @@
package main
import (
"fmt"
"github.com/appwrite/sdk-for-go/client"
"github.com/appwrite/sdk-for-go/databases"
)
func main() {
client := client.NewClient()
client.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
client.SetProject("<YOUR_PROJECT_ID>") // Your project ID
client.SetKey("<YOUR_API_KEY>") // Your secret API key
service := databases.NewDatabases(client)
response, error := service.DeleteDocuments(
"<DATABASE_ID>",
"<COLLECTION_ID>",
databases.WithDeleteDocumentsQueries([]interface{}{}),
)
if error != nil {
panic(error)
}
fmt.Println(response)
}
@@ -0,0 +1,29 @@
package main
import (
"fmt"
"github.com/appwrite/sdk-for-go/client"
"github.com/appwrite/sdk-for-go/databases"
)
func main() {
client := client.NewClient()
client.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
client.SetProject("<YOUR_PROJECT_ID>") // Your project ID
client.SetKey("<YOUR_API_KEY>") // Your secret API key
service := databases.NewDatabases(client)
response, error := service.UpdateDocuments(
"<DATABASE_ID>",
"<COLLECTION_ID>",
databases.WithUpdateDocumentsData(map[string]interface{}{}),
databases.WithUpdateDocumentsQueries([]interface{}{}),
)
if error != nil {
panic(error)
}
fmt.Println(response)
}
@@ -0,0 +1,28 @@
package main
import (
"fmt"
"github.com/appwrite/sdk-for-go/client"
"github.com/appwrite/sdk-for-go/databases"
)
func main() {
client := client.NewClient()
client.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
client.SetProject("<YOUR_PROJECT_ID>") // Your project ID
client.SetKey("<YOUR_API_KEY>") // Your secret API key
service := databases.NewDatabases(client)
response, error := service.UpsertDocuments(
"<DATABASE_ID>",
"<COLLECTION_ID>",
databases.WithUpsertDocumentsDocuments([]interface{}{}),
)
if error != nil {
panic(error)
}
fmt.Println(response)
}
@@ -0,0 +1,18 @@
mutation {
databasesCreateDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: []
) {
total
documents {
_id
_collectionId
_databaseId
_createdAt
_updatedAt
_permissions
data
}
}
}
@@ -0,0 +1,18 @@
mutation {
databasesDeleteDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
queries: []
) {
total
documents {
_id
_collectionId
_databaseId
_createdAt
_updatedAt
_permissions
data
}
}
}
@@ -0,0 +1,19 @@
mutation {
databasesUpdateDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
data: "{}",
queries: []
) {
total
documents {
_id
_collectionId
_databaseId
_createdAt
_updatedAt
_permissions
data
}
}
}
@@ -0,0 +1,18 @@
mutation {
databasesUpsertDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: []
) {
total
documents {
_id
_collectionId
_databaseId
_createdAt
_updatedAt
_permissions
data
}
}
}
@@ -0,0 +1,25 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setSession(""); // The user session to authenticate with
Databases databases = new Databases(client);
databases.createDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // documents
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
System.out.println(result);
})
);
@@ -0,0 +1,25 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);
databases.deleteDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
System.out.println(result);
})
);
@@ -0,0 +1,26 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);
databases.updateDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
mapOf( "a" to "b" ), // data (optional)
listOf(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
System.out.println(result);
})
);
@@ -0,0 +1,25 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);
databases.upsertDocuments(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
listOf(), // documents (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
System.out.println(result);
})
);
@@ -0,0 +1,16 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setSession("") // The user session to authenticate with
val databases = Databases(client)
val response = databases.createDocuments(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
documents = listOf()
)
@@ -0,0 +1,16 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>") // Your secret API key
val databases = Databases(client)
val response = databases.deleteDocuments(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
queries = listOf() // optional
)
@@ -0,0 +1,17 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>") // Your secret API key
val databases = Databases(client)
val response = databases.updateDocuments(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
data = mapOf( "a" to "b" ), // optional
queries = listOf() // optional
)
@@ -0,0 +1,16 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>") // Your secret API key
val databases = Databases(client)
val response = databases.upsertDocuments(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
documents = listOf() // optional
)
@@ -0,0 +1,14 @@
const sdk = require('node-appwrite');
const client = new sdk.Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
const databases = new sdk.Databases(client);
const result = await databases.createDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents
);
@@ -0,0 +1,14 @@
const sdk = require('node-appwrite');
const client = new sdk.Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new sdk.Databases(client);
const result = await databases.deleteDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // queries (optional)
);
@@ -0,0 +1,15 @@
const sdk = require('node-appwrite');
const client = new sdk.Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new sdk.Databases(client);
const result = await databases.updateDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
{}, // data (optional)
[] // queries (optional)
);
@@ -0,0 +1,14 @@
const sdk = require('node-appwrite');
const client = new sdk.Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new sdk.Databases(client);
const result = await databases.upsertDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents (optional)
);
@@ -0,0 +1,17 @@
<?php
use Appwrite\Client;
use Appwrite\Services\Databases;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
->setProject('<YOUR_PROJECT_ID>') // Your project ID
->setSession(''); // The user session to authenticate with
$databases = new Databases($client);
$result = $databases->createDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documents: []
);
@@ -0,0 +1,17 @@
<?php
use Appwrite\Client;
use Appwrite\Services\Databases;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
->setProject('<YOUR_PROJECT_ID>') // Your project ID
->setKey('<YOUR_API_KEY>'); // Your secret API key
$databases = new Databases($client);
$result = $databases->deleteDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
queries: [] // optional
);
@@ -0,0 +1,18 @@
<?php
use Appwrite\Client;
use Appwrite\Services\Databases;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
->setProject('<YOUR_PROJECT_ID>') // Your project ID
->setKey('<YOUR_API_KEY>'); // Your secret API key
$databases = new Databases($client);
$result = $databases->updateDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
data: [], // optional
queries: [] // optional
);
@@ -0,0 +1,17 @@
<?php
use Appwrite\Client;
use Appwrite\Services\Databases;
$client = (new Client())
->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
->setProject('<YOUR_PROJECT_ID>') // Your project ID
->setKey('<YOUR_API_KEY>'); // Your secret API key
$databases = new Databases($client);
$result = $databases->upsertDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documents: [] // optional
);
@@ -0,0 +1,15 @@
from appwrite.client import Client
from appwrite.services.databases import Databases
client = Client()
client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
client.set_project('<YOUR_PROJECT_ID>') # Your project ID
client.set_session('') # The user session to authenticate with
databases = Databases(client)
result = databases.create_documents(
database_id = '<DATABASE_ID>',
collection_id = '<COLLECTION_ID>',
documents = []
)
@@ -0,0 +1,15 @@
from appwrite.client import Client
from appwrite.services.databases import Databases
client = Client()
client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
client.set_project('<YOUR_PROJECT_ID>') # Your project ID
client.set_key('<YOUR_API_KEY>') # Your secret API key
databases = Databases(client)
result = databases.delete_documents(
database_id = '<DATABASE_ID>',
collection_id = '<COLLECTION_ID>',
queries = [] # optional
)
@@ -0,0 +1,16 @@
from appwrite.client import Client
from appwrite.services.databases import Databases
client = Client()
client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
client.set_project('<YOUR_PROJECT_ID>') # Your project ID
client.set_key('<YOUR_API_KEY>') # Your secret API key
databases = Databases(client)
result = databases.update_documents(
database_id = '<DATABASE_ID>',
collection_id = '<COLLECTION_ID>',
data = {}, # optional
queries = [] # optional
)
@@ -0,0 +1,15 @@
from appwrite.client import Client
from appwrite.services.databases import Databases
client = Client()
client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
client.set_project('<YOUR_PROJECT_ID>') # Your project ID
client.set_key('<YOUR_API_KEY>') # Your secret API key
databases = Databases(client)
result = databases.upsert_documents(
database_id = '<DATABASE_ID>',
collection_id = '<COLLECTION_ID>',
documents = [] # optional
)
@@ -0,0 +1,12 @@
POST /v1/databases/{databaseId}/collections/{collectionId}/documents HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Session:
X-Appwrite-Key: <YOUR_API_KEY>
X-Appwrite-JWT: <YOUR_JWT>
{
"documents": []
}
@@ -0,0 +1,10 @@
DELETE /v1/databases/{databaseId}/collections/{collectionId}/documents HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Key: <YOUR_API_KEY>
{
"queries": []
}
@@ -0,0 +1,11 @@
PATCH /v1/databases/{databaseId}/collections/{collectionId}/documents HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Key: <YOUR_API_KEY>
{
"data": {},
"queries": []
}
@@ -0,0 +1,10 @@
PUT /v1/databases/{databaseId}/collections/{collectionId}/documents HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Key: <YOUR_API_KEY>
{
"documents": []
}
@@ -0,0 +1,16 @@
require 'appwrite'
include Appwrite
client = Client.new
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
.set_project('<YOUR_PROJECT_ID>') # Your project ID
.set_session('') # The user session to authenticate with
databases = Databases.new(client)
result = databases.create_documents(
database_id: '<DATABASE_ID>',
collection_id: '<COLLECTION_ID>',
documents: []
)
@@ -0,0 +1,16 @@
require 'appwrite'
include Appwrite
client = Client.new
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
.set_project('<YOUR_PROJECT_ID>') # Your project ID
.set_key('<YOUR_API_KEY>') # Your secret API key
databases = Databases.new(client)
result = databases.delete_documents(
database_id: '<DATABASE_ID>',
collection_id: '<COLLECTION_ID>',
queries: [] # optional
)
@@ -0,0 +1,17 @@
require 'appwrite'
include Appwrite
client = Client.new
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
.set_project('<YOUR_PROJECT_ID>') # Your project ID
.set_key('<YOUR_API_KEY>') # Your secret API key
databases = Databases.new(client)
result = databases.update_documents(
database_id: '<DATABASE_ID>',
collection_id: '<COLLECTION_ID>',
data: {}, # optional
queries: [] # optional
)
@@ -0,0 +1,16 @@
require 'appwrite'
include Appwrite
client = Client.new
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
.set_project('<YOUR_PROJECT_ID>') # Your project ID
.set_key('<YOUR_API_KEY>') # Your secret API key
databases = Databases.new(client)
result = databases.upsert_documents(
database_id: '<DATABASE_ID>',
collection_id: '<COLLECTION_ID>',
documents: [] # optional
)
@@ -0,0 +1,15 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setSession("") // The user session to authenticate with
let databases = Databases(client)
let documentList = try await databases.createDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: []
)
@@ -0,0 +1,15 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>") // Your secret API key
let databases = Databases(client)
let documentList = try await databases.deleteDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
queries: [] // optional
)
@@ -0,0 +1,16 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>") // Your secret API key
let databases = Databases(client)
let documentList = try await databases.updateDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
data: [:], // optional
queries: [] // optional
)
@@ -0,0 +1,15 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setKey("<YOUR_API_KEY>") // Your secret API key
let databases = Databases(client)
let documentList = try await databases.upsertDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: [] // optional
)
+1 -1
View File
@@ -1 +1 @@
Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console.
Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console.
@@ -0,0 +1 @@
Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console.
@@ -0,0 +1 @@
Bulk delete documents using queries, if no queries are passed then all documents are deleted.
@@ -0,0 +1 @@
Update all documents that match your queries, if no queries are submitted then all documents are updated. You can pass only specific fields to be updated.
@@ -0,0 +1 @@
Create or update Documents. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console.
+12
View File
@@ -1,5 +1,17 @@
## 15.0.2
* Avoid setting empty `User-Agent` header and only encode it when present.
* Update doc examples to use new multi-region endpoint: `https://<REGION>.cloud.appwrite.io/v1`.
## 15.0.1
* Removed `Content-Type` header from GET and HEAD requests.
* Add validation for setting endpoint in `setEndpoint` and `setEndPointRealtime` methods.
* Include Figma in list of available OAuth providers.
## 15.0.0
* Encode `User-Agent` header to fix invalid HTTP header field value error.
* Breaking changes:
* Changed the typing of `AppwriteException`'s response parameter from a `dynamic` object to an optional string (`?String`).
+13 -7
View File
@@ -85,13 +85,19 @@ App::post('/v1/account/create')
```php
App::post('/v1/account/jwt')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createJWT')
->label('sdk.description', '/docs/references/account/create-jwt.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_JWT)
->label('sdk', new Method(
namespace: 'account',
name: 'createJWT',
description: '/docs/references/account/create-jwt.md',
auth: [],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_JWT,
)
],
responseType: ResponseType::JSON,
))
```
#### Cache
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace Appwrite\Deletes;
use Utopia\Database\Database;
use Utopia\Database\Query;
class Identities
{
public static function delete(Database $database, Query $query): void
{
$database->deleteDocuments(
'identities',
[
$query,
Query::orderAsc()
],
Database::DELETE_BATCH_SIZE
);
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace Appwrite\Deletes;
use Appwrite\Extend\Exception;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
class Targets
{
public static function delete(Database $database, Query $query): void
{
$database->deleteDocuments(
'targets',
[
$query,
Query::orderAsc()
],
Database::DELETE_BATCH_SIZE,
fn (Document $target) => self::deleteSubscribers($database, $target)
);
}
public static function deleteSubscribers(Database $database, Document $target): void
{
$database->deleteDocuments(
'subscribers',
[
Query::equal('targetInternalId', [$target->getInternalId()]),
Query::orderAsc(),
],
Database::DELETE_BATCH_SIZE,
function (Document $subscriber) use ($database, $target) {
$topicId = $subscriber->getAttribute('topicId');
$topicInternalId = $subscriber->getAttribute('topicInternalId');
$topic = $database->getDocument('topics', $topicId);
if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) {
$totalAttribute = match ($target->getAttribute('providerType')) {
MESSAGE_TYPE_EMAIL => 'emailTotal',
MESSAGE_TYPE_SMS => 'smsTotal',
MESSAGE_TYPE_PUSH => 'pushTotal',
default => throw new Exception('Invalid target provider type'),
};
$database->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
min: 0
);
}
}
);
}
}
+10 -4
View File
@@ -117,13 +117,19 @@ class Mapper
$list = false;
foreach ($route->getParams() as $name => $parameter) {
$methodParameters = $method->getParameters();
$sdkParameters = $method->getParameters();
if (!empty($methodParameters)) {
if (!array_key_exists($name, $methodParameters)) {
if (!empty($sdkParameters)) {
$sdkMethodParameters = [];
foreach ($sdkParameters as $sdkParameter) {
$sdkMethodParameters[$sdkParameter->getName()] = $sdkParameter;
}
if (!\array_key_exists($name, $sdkMethodParameters)) {
continue;
}
$optional = $methodParameters[$name]['optional'];
$optional = $sdkMethodParameters[$name]->getOptional();
} else {
$optional = $parameter['optional'];
}
+5 -4
View File
@@ -9,6 +9,7 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\PDO;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\System\System;
@@ -38,9 +39,9 @@ abstract class Migration
protected Database $consoleDB;
/**
* @var \PDO
* @var PDO
*/
protected \PDO $pdo;
protected PDO $pdo;
/**
* @var array
@@ -147,10 +148,10 @@ abstract class Migration
/**
* Set PDO for Migration.
*
* @param \PDO $pdo
* @param PDO $pdo
* @return \Appwrite\Migration\Migration
*/
public function setPDO(\PDO $pdo): self
public function setPDO(PDO $pdo): self
{
$this->pdo = $pdo;
@@ -34,9 +34,9 @@ class Specification extends Validator
$allowedSpecifications = [];
foreach ($this->specifications as $size => $values) {
if ($values['cpus'] <= $this->maxCpus && $values['memory'] <= $this->maxMemory) {
if (!empty($this->plan) && array_key_exists('specifications', $this->plan)) {
if (!\in_array($size, $this->plan['specifications'])) {
if ((empty($this->maxCpus) || $values['cpus'] <= $this->maxCpus) && (empty($this->maxMemory) || $values['memory'] <= $this->maxMemory)) {
if (!empty($this->plan) && array_key_exists('runtimeSpecifications', $this->plan)) {
if (!\in_array($size, $this->plan['runtimeSpecifications'])) {
continue;
}
}
@@ -6,6 +6,7 @@ use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\MethodType;
use Appwrite\SDK\Response as SDKResponse;
@@ -68,7 +69,7 @@ class Create extends Action
model: Response::MODEL_DEPLOYMENT,
)
],
requestType: 'multipart/form-data',
requestType: ContentType::MULTIPART,
type: MethodType::UPLOAD,
packaging: true,
))
@@ -75,7 +75,6 @@ class Create extends Base
)
],
contentType: ContentType::MULTIPART,
requestType: 'application/json',
))
->param('functionId', '', new UID(), 'Function ID.')
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
@@ -14,7 +14,6 @@ use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -89,8 +88,8 @@ class Create extends Base
->param('specification', APP_COMPUTE_SPECIFICATION_DEFAULT, fn (array $plan) => new Specification(
$plan,
Config::getParam('specifications', []),
App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT),
App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT)
System::getEnv('_APP_COMPUTE_CPUS', 0),
System::getEnv('_APP_COMPUTE_MEMORY', 0)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('response')
->inject('dbForProject')
@@ -131,7 +130,7 @@ class Create extends Base
// Temporary abuse check
$abuseCheck = function () use ($project, $timelimit, $response) {
$abuseKey = "projectId:{projectId},url:{url}";
$abuseLimit = App::getEnv('_APP_FUNCTIONS_CREATION_ABUSE_LIMIT', 50);
$abuseLimit = System::getEnv('_APP_FUNCTIONS_CREATION_ABUSE_LIMIT', 50);
$abuseTime = 86400; // 1 day
$timeLimit = $timelimit($abuseKey, $abuseLimit, $abuseTime);
@@ -14,7 +14,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Response;
use Executor\Executor;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -93,8 +92,8 @@ class Update extends Base
->param('specification', APP_COMPUTE_SPECIFICATION_DEFAULT, fn (array $plan) => new Specification(
$plan,
Config::getParam('specifications', []),
App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT),
App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT)
System::getEnv('_APP_COMPUTE_CPUS', 0),
System::getEnv('_APP_COMPUTE_MEMORY', 0)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('request')
->inject('response')

Some files were not shown because too many files have changed in this diff Show More