Implement Renaming Attributes in the API

This commit is contained in:
Bradley Schofield
2024-08-16 15:18:52 +09:00
parent 0d2fbaca45
commit 8770638d71
10 changed files with 711 additions and 67 deletions
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
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
+61 -22
View File
@@ -234,7 +234,8 @@ function updateAttribute(
int|float $min = null,
int|float $max = null,
array $elements = null,
array $options = []
array $options = [],
string $newKey = null
): Document {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -346,15 +347,22 @@ function updateAttribute(
collection: $collectionId,
id: $key,
onDelete: $primaryDocumentOptions['onDelete'],
newKey: $newKey,
);
if ($primaryDocumentOptions['twoWay']) {
$relatedCollection = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryDocumentOptions['relatedCollection']);
$relatedAttribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']);
if (!empty($newKey) && $newKey !== $key) {
$options['twoWayKey'] = $newKey;
}
$relatedOptions = \array_merge($relatedAttribute->getAttribute('options'), $options);
$relatedAttribute->setAttribute('options', $relatedOptions);
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute);
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedCollection->getId());
}
} else {
@@ -363,11 +371,21 @@ function updateAttribute(
id: $key,
required: $required,
default: $default,
formatOptions: $options ?? null
formatOptions: $options ?? null,
newKey: $newKey,
);
}
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
if (!empty($newKey) && $key !== $newKey) {
// Delete attribute and recreate since we can't modify IDs
$dbForProject->deleteDocument('attributes', $attribute->getId());
$attribute->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey));
$attribute->setAttribute('key', $newKey);
$attribute = $dbForProject->createDocument('attributes', $attribute);
} else {
$attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute);
}
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $collection->getId());
$queueForEvents
@@ -1859,10 +1877,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
@@ -1872,7 +1891,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
queueForEvents: $queueForEvents,
type: Database::VAR_STRING,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -1898,10 +1918,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -1911,7 +1932,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_EMAIL,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -1938,10 +1960,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
->param('elements', null, new ArrayList(new Text(DATABASE::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE::LENGTH_KEY . ' characters long.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -1952,7 +1975,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
filter: APP_DATABASE_ATTRIBUTE_ENUM,
default: $default,
required: $required,
elements: $elements
elements: $elements,
newKey: $newKey
);
$response
@@ -1978,10 +2002,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new IP()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -1991,7 +2016,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_IP,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2017,10 +2043,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2030,7 +2057,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_URL,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2058,10 +2086,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
->param('min', null, new Integer(), 'Minimum value to enforce on new documents')
->param('max', null, new Integer(), 'Maximum value to enforce on new documents')
->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2072,7 +2101,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
default: $default,
required: $required,
min: $min,
max: $max
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
@@ -2107,10 +2137,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents')
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents')
->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2121,7 +2152,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
default: $default,
required: $required,
min: $min,
max: $max
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
@@ -2154,10 +2186,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2166,7 +2199,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
queueForEvents: $queueForEvents,
type: Database::VAR_BOOLEAN,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2192,10 +2226,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new DatetimeValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) {
$attribute = updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
@@ -2204,7 +2239,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
queueForEvents: $queueForEvents,
type: Database::VAR_DATETIME,
default: $default,
required: $required
required: $required,
newKey: $newKey
);
$response
@@ -2229,6 +2265,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
->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('key', '', new Key(), 'Attribute Key.')
->param('onDelete', null, new WhiteList([Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL], true), 'Constraints option', true)
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2237,6 +2274,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
string $collectionId,
string $key,
?string $onDelete,
?string $newKey,
Response $response,
Database $dbForProject,
Event $queueForEvents
@@ -2251,7 +2289,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
required: false,
options: [
'onDelete' => $onDelete
]
],
newKey: $newKey
);
$options = $attribute->getAttribute('options', []);
+3 -3
View File
@@ -44,13 +44,13 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.14.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.38.*",
"utopia-php/abuse": "0.40.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.40.*",
"utopia-php/audit": "0.41.*",
"utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.50.*",
"utopia-php/database": "0.51.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
Generated
+35 -33
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "340aae0879435fc71eac688d47033eb4",
"content-hash": "cd0323e7303780f34b2f775a526de516",
"packages": [
{
"name": "adhocore/jwt",
@@ -1429,26 +1429,28 @@
},
{
"name": "utopia-php/abuse",
"version": "0.38.0",
"version": "0.40.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c"
"reference": "69748a6741a9e44f0c9e430f3c2bd2b8a677c048"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/b7be9086c9d9b4561d810cbd42fdda798742f56c",
"reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/69748a6741a9e44f0c9e430f3c2bd2b8a677c048",
"reference": "69748a6741a9e44f0c9e430f3c2bd2b8a677c048",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/database": "0.50.*"
"utopia-php/database": "0.51.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.4"
},
@@ -1472,9 +1474,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.38.0"
"source": "https://github.com/utopia-php/abuse/tree/0.40.0"
},
"time": "2024-06-24T00:52:02+00:00"
"time": "2024-08-16T06:08:24+00:00"
},
{
"name": "utopia-php/analytics",
@@ -1524,21 +1526,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.40.0",
"version": "0.41.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc"
"reference": "77f1d0a95ea791e38a38a8bc1b7728ffcedcd2d1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/735ae211ce5fee5b52b736731571b4030b1d7cdc",
"reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/77f1d0a95ea791e38a38a8bc1b7728ffcedcd2d1",
"reference": "77f1d0a95ea791e38a38a8bc1b7728ffcedcd2d1",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.50.*"
"utopia-php/database": "0.51.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1565,9 +1567,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.40.0"
"source": "https://github.com/utopia-php/audit/tree/0.41.0"
},
"time": "2024-06-24T00:52:17+00:00"
"time": "2024-08-16T06:08:00+00:00"
},
{
"name": "utopia-php/cache",
@@ -1721,16 +1723,16 @@
},
{
"name": "utopia-php/database",
"version": "0.50.4",
"version": "0.51.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "fd3b856be77bd643bc8a9e3572ee11e4185b9230"
"reference": "cf723c720e5aa938709e74f1ccfc3b46ecdc6de3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/fd3b856be77bd643bc8a9e3572ee11e4185b9230",
"reference": "fd3b856be77bd643bc8a9e3572ee11e4185b9230",
"url": "https://api.github.com/repos/utopia-php/database/zipball/cf723c720e5aa938709e74f1ccfc3b46ecdc6de3",
"reference": "cf723c720e5aa938709e74f1ccfc3b46ecdc6de3",
"shasum": ""
},
"require": {
@@ -1771,9 +1773,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.50.4"
"source": "https://github.com/utopia-php/database/tree/0.51.0"
},
"time": "2024-08-13T03:18:26+00:00"
"time": "2024-08-16T03:28:04+00:00"
},
{
"name": "utopia-php/domains",
@@ -1923,16 +1925,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.7",
"version": "0.33.8",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "78d293d99a262bd63ece750bbf989c7e0643b825"
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/78d293d99a262bd63ece750bbf989c7e0643b825",
"reference": "78d293d99a262bd63ece750bbf989c7e0643b825",
"url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5",
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5",
"shasum": ""
},
"require": {
@@ -1962,9 +1964,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.7"
"source": "https://github.com/utopia-php/http/tree/0.33.8"
},
"time": "2024-08-01T14:01:04+00:00"
"time": "2024-08-15T14:10:09+00:00"
},
{
"name": "utopia-php/image",
@@ -2759,16 +2761,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.8.1",
"version": "0.8.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "3084aa93d24ed1e70f01e75f4318fc0d07f12596"
"reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/3084aa93d24ed1e70f01e75f4318fc0d07f12596",
"reference": "3084aa93d24ed1e70f01e75f4318fc0d07f12596",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18",
"reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18",
"shasum": ""
},
"require": {
@@ -2802,9 +2804,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.8.1"
"source": "https://github.com/utopia-php/vcs/tree/0.8.2"
},
"time": "2024-07-29T20:49:09+00:00"
"time": "2024-08-13T14:36:30+00:00"
},
{
"name": "utopia-php/websocket",
@@ -640,7 +640,7 @@ class DatabasesCustomServerTest extends Scope
}
/**
* @depends testListCollections
* @depends testListCollections
*/
public function testCreateEncryptedAttribute(array $data): void
{
@@ -866,7 +866,7 @@ class DatabasesCustomServerTest extends Scope
$this->assertEquals($collection['body']['indexes'][0]['key'], $index['body']['key']);
// Delete attribute
$attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actors ['body']['$id'] . '/attributes/' . $unneededId, array_merge([
$attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/attributes/' . $unneededId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
@@ -1646,7 +1646,6 @@ class DatabasesCustomServerTest extends Scope
];
}
/**
* @depends testAttributeUpdate
*/
@@ -3328,4 +3327,608 @@ class DatabasesCustomServerTest extends Scope
$this->assertEquals(AppwriteException::ATTRIBUTE_NOT_FOUND, $update['body']['type']);
}
}
/**
* @depends testAttributeUpdate
*/
public function testAttributeRename(array $data)
{
$key = 'string';
$databaseId = $data['databaseId'];
$collectionId = $data['collectionId'];
// Create document to test against
$document = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/documents',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]),
[
'documentId' => 'unique()',
'data' => [
'string' => 'string'
],
"permissions" => ["read(\"any\")"]
]
);
$this->assertEquals(201, $document['headers']['status-code']);
$update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string/' . $key, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'required' => false,
'default' => 'lorum',
'newKey' => 'new_string',
]);
$this->assertEquals(200, $update['headers']['status-code']);
$key = 'new_string';
$new = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/' . $key, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertEquals('new_string', $new['body']['key']);
$doc1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('new_string', $doc1['body']);
$this->assertEquals('string', $doc1['body']['new_string']);
$this->assertArrayNotHasKey('string', $doc1['body']);
// Try and create a new document with the new attribute
$doc2 = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/documents',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]),
[
'documentId' => 'unique()',
'data' => [
'new_string' => 'string'
],
"permissions" => ["read(\"any\")"]
]
);
$this->assertEquals(201, $doc2['headers']['status-code']);
$this->assertArrayHasKey('new_string', $doc2['body']);
$this->assertEquals('string', $doc2['body']['new_string']);
// Expect fail, try and create a new document with the old attribute
$doc3 = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/documents',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]),
[
'documentId' => 'unique()',
'data' => [
'string' => 'string'
],
"permissions" => ["read(\"any\")"]
]
);
$this->assertEquals(400, $doc3['headers']['status-code']);
}
public function createRelationshipCollections()
{
// Prepare the database with collections and relationships
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => 'database1',
'name' => 'Test Database'
]);
$databaseId = $database['body']['$id'];
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => 'collection1',
'name' => 'level1',
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => 'collection2',
'name' => 'level2',
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
Permission::read(Role::user($this->getUser()['$id'])),
Permission::update(Role::user($this->getUser()['$id'])),
Permission::delete(Role::user($this->getUser()['$id'])),
]
]);
\sleep(2);
}
public function cleanupRelationshipCollection()
{
$this->client->call(Client::METHOD_DELETE, '/databases/database1', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
\sleep(2);
}
public function testAttributeRenameRelationshipOneToMany()
{
$databaseId = 'database1';
$collection1Id = 'collection1';
$collection2Id = 'collection2';
$this->createRelationshipCollections();
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2Id,
'type' => 'oneToMany',
'twoWay' => true,
'onDelete' => 'cascade',
'key' => 'level2',
'twoWayKey' => 'level1'
]);
\sleep(3);
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$collection1RelationAttribute = $collection1Attributes['body']['attributes'][0];
$this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']);
$this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']);
$this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']);
// Create a document for checking later
$originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => 'unique()',
'data' => [
'level2' => [[
'$id' => 'unique()',
'$permissions' => ["read(\"any\")"]
]],
],
"permissions" => ["read(\"any\")"]
]);
$this->assertEquals(201, $originalDocument['headers']['status-code']);
// Rename the attribute
$update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/level2' . '/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'newKey' => 'new_level_2'
]);
$this->assertEquals(200, $update['headers']['status-code']);
// Check the document's key has been renamed
$newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents/' . $originalDocument['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('new_level_2', $newDocument['body']);
$this->assertEquals(1, count($newDocument['body']['new_level_2']));
$this->assertArrayNotHasKey('level2', $newDocument['body']);
// Check level2 document has been renamed
$level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id . '/documents/' . $newDocument['body']['new_level_2'][0]['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('level1', $level2Document['body']);
$this->assertNotEmpty($level2Document['body']['level1']);
// Check if attribute was renamed on the parent's side
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection1Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection1Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']);
// Check if attribute was renamed on the child's side
$collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection2Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection2Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']);
$this->cleanupRelationshipCollection();
}
public function testAttributeRenameRelationshipOneToOne()
{
$databaseId = 'database1';
$collection1Id = 'collection1';
$collection2Id = 'collection2';
$this->createRelationshipCollections();
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2Id,
'type' => 'oneToOne',
'twoWay' => true,
'onDelete' => 'cascade',
'key' => 'level2',
'twoWayKey' => 'level1'
]);
\sleep(3);
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$collection1RelationAttribute = $collection1Attributes['body']['attributes'][0];
$this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']);
$this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']);
$this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']);
// Create a document for checking later
$originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => 'unique()',
'data' => [
'level2' => [
'$id' => 'unique()',
'$permissions' => ["read(\"any\")"]
],
],
"permissions" => ["read(\"any\")"]
]);
$this->assertEquals(201, $originalDocument['headers']['status-code']);
// Rename the attribute
$update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/level2' . '/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'newKey' => 'new_level_2'
]);
$this->assertEquals(200, $update['headers']['status-code']);
// Check the document's key has been renamed
$newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents/' . $originalDocument['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('new_level_2', $newDocument['body']);
$this->assertNotEmpty($newDocument['body']['new_level_2']);
$this->assertArrayNotHasKey('level2', $newDocument['body']);
// Check level2 document has been renamed
$level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id . '/documents/' . $newDocument['body']['new_level_2']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('level1', $level2Document['body']);
$this->assertNotEmpty($level2Document['body']['level1']);
// Check if attribute was renamed on the parent's side
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection1Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection1Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']);
// Check if attribute was renamed on the child's side
$collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection2Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection2Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']);
$this->cleanupRelationshipCollection();
}
public function testAttributeRenameRelationshipManyToOne()
{
$databaseId = 'database1';
$collection1Id = 'collection1';
$collection2Id = 'collection2';
$this->createRelationshipCollections();
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2Id,
'type' => 'manyToOne',
'twoWay' => true,
'onDelete' => 'cascade',
'key' => 'level2',
'twoWayKey' => 'level1'
]);
\sleep(3);
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$collection1RelationAttribute = $collection1Attributes['body']['attributes'][0];
$this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']);
$this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']);
$this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']);
// Create a document for checking later
$originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => 'unique()',
'data' => [
'level2' => [
'$id' => 'unique()',
'$permissions' => ["read(\"any\")"]
],
],
"permissions" => ["read(\"any\")"]
]);
$this->assertEquals(201, $originalDocument['headers']['status-code']);
// Rename the attribute
$update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/level2' . '/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'newKey' => 'new_level_2'
]);
$this->assertEquals(200, $update['headers']['status-code']);
// Check the document's key has been renamed
$newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents/' . $originalDocument['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('new_level_2', $newDocument['body']);
$this->assertNotEmpty($newDocument['body']['new_level_2']);
$this->assertArrayNotHasKey('level2', $newDocument['body']);
// Check level2 document has been renamed
$level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id . '/documents/' . $newDocument['body']['new_level_2']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('level1', $level2Document['body']);
$this->assertNotEmpty($level2Document['body']['level1']);
// Check if attribute was renamed on the parent's side
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection1Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection1Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']);
// Check if attribute was renamed on the child's side
$collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection2Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection2Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']);
$this->cleanupRelationshipCollection();
}
public function testAttributeRenameRelationshipManyToMany()
{
$databaseId = 'database1';
$collection1Id = 'collection1';
$collection2Id = 'collection2';
$this->createRelationshipCollections();
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2Id,
'type' => 'manyToOne',
'twoWay' => true,
'onDelete' => 'cascade',
'key' => 'level2',
'twoWayKey' => 'level1'
]);
\sleep(3);
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$collection1RelationAttribute = $collection1Attributes['body']['attributes'][0];
$this->assertEquals($relation['body']['side'], $collection1RelationAttribute['side']);
$this->assertEquals($relation['body']['twoWayKey'], $collection1RelationAttribute['twoWayKey']);
$this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']);
// Create a document for checking later
$originalDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => 'unique()',
'data' => [
'level2' => [
'$id' => 'unique()',
'$permissions' => ["read(\"any\")"]
],
],
"permissions" => ["read(\"any\")"]
]);
$this->assertEquals(201, $originalDocument['headers']['status-code']);
// Rename the attribute
$update = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/attributes/level2' . '/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'newKey' => 'new_level_2'
]);
$this->assertEquals(200, $update['headers']['status-code']);
// Check the document's key has been renamed
$newDocument = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id . '/documents/' . $originalDocument['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('new_level_2', $newDocument['body']);
$this->assertNotEmpty($newDocument['body']['new_level_2']);
$this->assertArrayNotHasKey('level2', $newDocument['body']);
// Check level2 document has been renamed
$level2Document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id . '/documents/' . $newDocument['body']['new_level_2']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]));
$this->assertArrayHasKey('level1', $level2Document['body']);
$this->assertNotEmpty($level2Document['body']['level1']);
// Check if attribute was renamed on the parent's side
$collection1Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection1Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection1Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection1Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection1Attributes['body']['attributes'][0]['key']);
// Check if attribute was renamed on the child's side
$collection2Attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collection2Id, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]);
$this->assertEquals(200, $collection2Attributes['headers']['status-code']);
$this->assertEquals(1, count($collection2Attributes['body']['attributes']));
$this->assertEquals('new_level_2', $collection2Attributes['body']['attributes'][0]['twoWayKey']);
$this->cleanupRelationshipCollection();
}
}