1
0
Fork 0
mirror of synced 2024-07-04 06:00:53 +12:00

Merge pull request #5887 from appwrite/fix-5404-update-document-without-relation-permission

Fix 5404 update document without relation permission
This commit is contained in:
Jake Barnby 2023-08-17 17:48:08 -04:00 committed by GitHub
commit a019d01e91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 467 additions and 70 deletions

View file

@ -196,16 +196,14 @@ function createAttribute(string $databaseId, string $collectionId, Document $att
->setType(DATABASE_TYPE_CREATE_ATTRIBUTE)
->setDatabase($db)
->setCollection($collection)
->setDocument($attribute)
;
->setDocument($attribute);
$events
->setContext('collection', $collection)
->setContext('database', $db)
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setParam('attributeId', $attribute->getId())
;
->setParam('attributeId', $attribute->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -682,13 +680,11 @@ App::delete('/v1/databases/:databaseId')
$deletes
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($database)
;
->setDocument($database);
$events
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE))
;
->setPayload($response->output($database, Response::MODEL_DATABASE));
$response->noContent();
});
@ -3250,28 +3246,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
}
$data = \array_merge($document->getArrayCopy(), $data); // Merge existing data with new data
$data['$collection'] = $collection->getId(); // Make sure user doesn't switch collectionID
$data['$collection'] = $document->getAttribute('$collection'); // Make sure user doesn't switch collectionID
$data['$createdAt'] = $document->getCreatedAt(); // Make sure user doesn't switch createdAt
$data['$id'] = $document->getId(); // Make sure user doesn't switch document unique ID
$data['$permissions'] = $permissions;
$newDocument = new Document($data);
$checkPermissions = function (Document $collection, Document $document, Document $old, string $permission) use (&$checkPermissions, $dbForProject, $database) {
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization($permission);
$valid = $validator->isValid($collection->getPermissionsByType($permission));
if (!$documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($permission === Database::PERMISSION_UPDATE) {
$valid = $valid || $validator->isValid($old->getPermissionsByType($permission));
if ($documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
}
$setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database) {
$relationships = \array_filter(
$collection->getAttribute('attributes', []),
fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
@ -3298,6 +3279,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
);
foreach ($relations as &$relation) {
// If the relation is an array it can be either update or create a child document.
if (
\is_array($relation)
&& \array_values($relation) !== $relation
@ -3311,21 +3293,20 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(),
$relation->getId()
));
$relation->removeAttribute('$collectionId');
$relation->removeAttribute('$databaseId');
// Attribute $collection is required for Utopia.
$relation->setAttribute(
'$collection',
'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId()
);
if ($oldDocument->isEmpty()) {
$type = Database::PERMISSION_CREATE;
if (isset($relation['$id']) && $relation['$id'] === 'unique()') {
$relation['$id'] = ID::unique();
}
} else {
$relation->removeAttribute('$collectionId');
$relation->removeAttribute('$databaseId');
$relation->setAttribute('$collection', $relatedCollection->getId());
$type = Database::PERMISSION_UPDATE;
}
$checkPermissions($relatedCollection, $relation, $oldDocument, $type);
$setCollection($relatedCollection, $relation);
}
}
@ -3335,26 +3316,26 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$document->setAttribute($relationship->getAttribute('key'), \reset($relations));
}
}
};
});
$checkPermissions($collection, $newDocument, $document, Database::PERMISSION_UPDATE);
$setCollection($collection, $newDocument);
try {
$document = $dbForProject->withRequestTimestamp(
$requestTimestamp,
fn() => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
)
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (DuplicateException) {
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
} catch (StructureException $exception) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
}
try {
$document = $dbForProject->withRequestTimestamp(
$requestTimestamp,
fn() => $dbForProject->updateDocument(
'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
$document->getId(),
$newDocument
)
);
} catch (AuthorizationException) {
throw new Exception(Exception::USER_UNAUTHORIZED);
} catch (DuplicateException) {
throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS);
} catch (StructureException $exception) {
throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage());
}
// Add $collectionId and $databaseId for all documents
$processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) {

36
composer.lock generated
View file

@ -1516,16 +1516,16 @@
},
{
"name": "utopia-php/database",
"version": "0.42.0",
"version": "0.42.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "5631f151ce2b454517f11c1914e50a1fa70c7bd1"
"reference": "9ff69a9b9eadc581771798833d423829c9d8cc90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/5631f151ce2b454517f11c1914e50a1fa70c7bd1",
"reference": "5631f151ce2b454517f11c1914e50a1fa70c7bd1",
"url": "https://api.github.com/repos/utopia-php/database/zipball/9ff69a9b9eadc581771798833d423829c9d8cc90",
"reference": "9ff69a9b9eadc581771798833d423829c9d8cc90",
"shasum": ""
},
"require": {
@ -1566,9 +1566,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.42.0"
"source": "https://github.com/utopia-php/database/tree/0.42.1"
},
"time": "2023-08-10T23:18:38+00:00"
"time": "2023-08-14T16:09:09+00:00"
},
{
"name": "utopia-php/domains",
@ -2993,16 +2993,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.16.0",
"version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@ -3043,9 +3043,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"time": "2023-06-25T14:52:30+00:00"
"time": "2023-08-13T19:53:39+00:00"
},
{
"name": "phar-io/manifest",
@ -3270,16 +3270,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.7.2",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
"shasum": ""
},
"require": {
@ -3322,9 +3322,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
},
"time": "2023-05-30T18:13:47+00:00"
"time": "2023-08-12T11:01:26+00:00"
},
{
"name": "phpspec/prophecy",

View file

@ -316,4 +316,420 @@ class DatabasesCustomClientTest extends Scope
$this->assertEquals($relation['body']['relatedCollection'], $collection1RelationAttribute['relatedCollection']);
$this->assertEquals('restrict', $collection1RelationAttribute['onDelete']);
}
public function testUpdateWithoutRelationPermission(): void
{
$userId = $this->getUser()['$id'];
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => ID::unique(),
'name' => ID::unique(),
]);
$databaseId = $database['body']['$id'];
// Creating collection 1
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection1'),
'name' => ID::custom('collection1'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Creating collection 2
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection2'),
'name' => ID::custom('collection2'),
'documentSecurity' => false,
'permissions' => [
Permission::read(Role::user($userId)),
]
]);
$collection3 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection3'),
'name' => ID::custom('collection3'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
$collection4 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection4'),
'name' => ID::custom('collection4'),
'documentSecurity' => false,
'permissions' => [
Permission::read(Role::user($userId)),
]
]);
$collection5 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection5'),
'name' => ID::custom('collection5'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Creating one to one relationship from collection 1 to colletion 2
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection2['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection2['body']['$id']
]);
// Creating one to one relationship from collection 2 to colletion 3
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection2['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection3['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection3['body']['$id']
]);
// Creating one to one relationship from collection 3 to colletion 4
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection3['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection4['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection4['body']['$id']
]);
// Creating one to one relationship from collection 4 to colletion 5
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection4['body']['$id'] . '/attributes/relationship', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'relatedCollectionId' => $collection5['body']['$id'],
'type' => 'oneToOne',
'twoWay' => false,
'onDelete' => 'setNull',
'key' => $collection5['body']['$id']
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Title",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection2['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection3['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection4['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection5['body']['$id'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => "Rating",
'size' => 100,
'required' => false,
'array' => false,
'default' => null,
]);
\sleep(2);
// Creating parent document with a child reference to test the permissions
$parentDocument = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => ID::custom($collection1['body']['$id']),
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
'Rating' => '10',
$collection3['body']['$id'] => [
'$id' => ID::custom($collection3['body']['$id']),
'Rating' => '10',
$collection4['body']['$id'] => [
'$id' => ID::custom($collection4['body']['$id']),
'Rating' => '10',
$collection5['body']['$id'] => [
'$id' => ID::custom($collection5['body']['$id']),
'Rating' => '10'
]
]
]
]
]
]);
$this->assertEquals(201, $parentDocument['headers']['status-code']);
// This is the point of the test. We should not need any authorization permission to update the document with same data.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::custom($collection1['body']['$id']),
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => $collection2['body']['$id'],
'Rating' => '10',
$collection3['body']['$id'] => [
'$id' => $collection3['body']['$id'],
'Rating' => '10',
$collection4['body']['$id'] => [
'$id' => $collection4['body']['$id'],
'Rating' => '10',
$collection5['body']['$id'] => [
'$id' => $collection5['body']['$id'],
'Rating' => '10'
]
]
]
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($parentDocument['body'], $response['body']);
// Giving update permission of collection 3 to user.
$this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/collection3', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection3'),
'name' => ID::custom('collection3'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::update(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// This is the point of this test. We should be allowed to do this action, and it should not fail on permission check
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
'Rating' => '10',
$collection3['body']['$id'] => [
'$id' => ID::custom($collection3['body']['$id']),
'Rating' => '11',
$collection4['body']['$id'] => [
'$id' => ID::custom($collection4['body']['$id']),
'Rating' => '10',
$collection5['body']['$id'] => [
'$id' => ID::custom($collection5['body']['$id']),
'Rating' => '11'
]
]
]
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(11, $response['body'][$collection2['body']['$id']]['collection3']['Rating']);
// We should not be allowed to update the document as we do not have permission for collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
'Rating' => '11',
$collection3['body']['$id'] => null,
]
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// We should not be allowed to update the document as we do not have permission for collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection2['body']['$id'] . '/documents/' . $collection2['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Rating' => '11',
]
]);
$this->assertEquals(401, $response['headers']['status-code']);
// Removing update permission from collection 3.
$this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/collection3', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection3'),
'name' => ID::custom('collection3'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Giving update permission to collection 2.
$this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/collection2', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => ID::custom('collection2'),
'name' => ID::custom('collection2'),
'documentSecurity' => false,
'permissions' => [
Permission::create(Role::user($userId)),
Permission::update(Role::user($userId)),
Permission::read(Role::user($userId)),
Permission::delete(Role::user($userId)),
]
]);
// Creating collection 3 new document
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection3['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'documentId' => ID::custom('collection3Doc1'),
'data' => [
'Rating' => '20'
]
]);
$this->assertEquals(201, $response['headers']['status-code']);
// We should be allowed to link a new document from collection 3 to collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
$collection3['body']['$id'] => 'collection3Doc1',
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
// We should be allowed to link and create a new document from collection 3 to collection 2.
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/documents/' . $collection1['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'Title' => 'Captain America',
$collection2['body']['$id'] => [
'$id' => ID::custom($collection2['body']['$id']),
$collection3['body']['$id'] => [
'$id' => ID::custom('collection3Doc2')
],
]
]
]);
$this->assertEquals(200, $response['headers']['status-code']);
}
}