1
0
Fork 0
mirror of synced 2024-06-02 19:04:49 +12:00

Merge branch 'feat-database-indexing' of https://github.com/appwrite/appwrite into feat-arrow-functions-on-auth-skip

This commit is contained in:
Torsten Dittmann 2021-12-17 11:23:22 +01:00
commit f6d7228202
13 changed files with 424 additions and 167 deletions

View file

@ -50,7 +50,7 @@ use Appwrite\Detector\Detector;
*/
function createAttribute($collectionId, $attribute, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage): Document
{
$attributeId = $attribute->getId();
$key = $attribute->getAttribute('key');
$type = $attribute->getAttribute('type', '');
$size = $attribute->getAttribute('size', 0);
$required = $attribute->getAttribute('required', true);
@ -59,7 +59,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
$format = $attribute->getAttribute('format', '');
$formatOptions = $attribute->getAttribute('formatOptions', []);
$filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint
$default = $attribute->getAttribute('default', null);
$default = $attribute->getAttribute('default');
$collection = $dbForInternal->getDocument('collections', $collectionId);
@ -84,8 +84,8 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
try {
$attribute = new Document([
'$id' => $collectionId.'_'.$attributeId,
'key' => $attributeId,
'$id' => $collectionId.'_'.$key,
'key' => $key,
'collectionId' => $collectionId,
'type' => $type,
'status' => 'processing', // processing, available, failed, deleting, stuck
@ -711,7 +711,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_STRING)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
@ -722,7 +722,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $size, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -737,7 +737,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => $size,
'required' => $required,
@ -761,7 +761,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_EMAIL)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -771,7 +771,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -780,7 +780,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 254,
'required' => $required,
@ -805,7 +805,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_ENUM)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('elements', [], new ArrayList(new Text(0)), 'Array of elements in enumerated type. Uses length of longest element to determine size.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
@ -816,7 +816,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $elements, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $elements, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -836,7 +836,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => $size,
'required' => $required,
@ -862,7 +862,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_IP)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new IP(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -872,7 +872,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -881,7 +881,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 39,
'required' => $required,
@ -906,7 +906,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_URL)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -916,7 +916,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForExternal*/
/** @var Appwrite\Event\Event $database */
@ -924,7 +924,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 2000,
'required' => $required,
@ -949,7 +949,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_INTEGER)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true)
->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true)
@ -961,7 +961,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -984,7 +984,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_INTEGER,
'size' => 0,
'required' => $required,
@ -1020,7 +1020,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_FLOAT)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true)
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true)
@ -1032,7 +1032,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -1047,7 +1047,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
if ($min > $max) {
throw new Exception('Minimum value must be lesser than maximum value', 400);
}
// Ensure default value is a float
if (!is_null($default)) {
$default = \floatval($default);
@ -1060,7 +1060,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_FLOAT,
'required' => $required,
'size' => 0,
@ -1096,7 +1096,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_BOOLEAN)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Boolean(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -1106,7 +1106,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Appwrite\Event\Event $database */
@ -1114,7 +1114,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_BOOLEAN,
'size' => 0,
'required' => $required,
@ -1160,7 +1160,7 @@ App::get('/v1/database/collections/:collectionId/attributes')
]), Response::MODEL_ATTRIBUTE_LIST);
});
App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
App::get('/v1/database/collections/:collectionId/attributes/:key')
->desc('Get Attribute')
->groups(['api', 'database'])
->label('scope', 'collections.read')
@ -1180,11 +1180,11 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
Response::MODEL_ATTRIBUTE_IP,
Response::MODEL_ATTRIBUTE_STRING,])// needs to be last, since its condition would dominate any other string attribute
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -1194,7 +1194,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
throw new Exception('Collection not found', 404);
}
$attribute = $collection->find('$id', $attributeId, 'attributes');
$attribute = $collection->find('$id', $key, 'attributes');
if (!$attribute) {
throw new Exception('Attribute not found', 404);
@ -1223,7 +1223,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
$response->dynamic($attribute, $model);
});
App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
App::delete('/v1/database/collections/:collectionId/attributes/:key')
->desc('Delete Attribute')
->groups(['api', 'database'])
->label('scope', 'collections.write')
@ -1235,7 +1235,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('key', '', new Key(), 'Attribute Key.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -1243,7 +1243,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
->inject('events')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $dbForExternal, $database, $events, $audits, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $dbForExternal, $database, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
@ -1258,7 +1258,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
throw new Exception('Collection not found', 404);
}
$attribute = $dbForInternal->getDocument('attributes', $collectionId.'_'.$attributeId);
$attribute = $dbForInternal->getDocument('attributes', $collectionId.'_'.$key);
if (empty($attribute->getId())) {
throw new Exception('Attribute not found', 404);
@ -1324,7 +1324,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_INDEX)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('indexId', null, new Key(), 'Index ID.')
->param('key', null, new Key(), 'Index Key.')
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL, Database::INDEX_ARRAY]), 'Index type.')
->param('attributes', null, new ArrayList(new Key()), 'Array of attributes to index.')
->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING)), 'Array of index orders.', true)
@ -1333,7 +1333,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $indexId, $type, $attributes, $orders, $response, $dbForInternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $type, $attributes, $orders, $response, $dbForInternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $database */
@ -1364,7 +1364,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
// lengths hidden by default
$lengths = [];
foreach ($attributes as $key => $attribute) {
foreach ($attributes as $i => $attribute) {
// find attribute metadata in collection document
$attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key'));
@ -1382,13 +1382,13 @@ App::post('/v1/database/collections/:collectionId/indexes')
}
// set attribute size as index length only for strings
$lengths[$key] = ($attributeType === Database::VAR_STRING) ? $attributeSize : null;
$lengths[$i] = ($attributeType === Database::VAR_STRING) ? $attributeSize : null;
}
try {
$index = $dbForInternal->createDocument('indexes', new Document([
'$id' => $collectionId.'_'.$indexId,
'key' => $indexId,
'$id' => $collectionId.'_'.$key,
'key' => $key,
'status' => 'processing', // processing, available, failed, deleting, stuck
'collectionId' => $collectionId,
'type' => $type,
@ -1455,7 +1455,7 @@ App::get('/v1/database/collections/:collectionId/indexes')
]), Response::MODEL_INDEX_LIST);
});
App::get('/v1/database/collections/:collectionId/indexes/:indexId')
App::get('/v1/database/collections/:collectionId/indexes/:key')
->desc('Get Index')
->groups(['api', 'database'])
->label('scope', 'collections.read')
@ -1467,11 +1467,11 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_INDEX)
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('indexId', null, new Key(), 'Index ID.')
->param('key', null, new Key(), 'Index Key.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($collectionId, $indexId, $response, $dbForInternal, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -1484,7 +1484,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
$indexes = $collection->getAttribute('indexes');
// Search for index
$indexIndex = array_search($indexId, array_column($indexes, '$id'));
$indexIndex = array_search($key, array_column($indexes, 'key'));
if ($indexIndex === false) {
throw new Exception('Index not found', 404);
@ -1495,11 +1495,11 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
])]);
$usage->setParam('database.collections.read', 1);
$response->dynamic($index, Response::MODEL_INDEX);
});
App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
App::delete('/v1/database/collections/:collectionId/indexes/:key')
->desc('Delete Index')
->groups(['api', 'database'])
->label('scope', 'collections.write')
@ -1511,14 +1511,14 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('indexId', '', new Key(), 'Index ID.')
->param('key', '', new Key(), 'Index Key.')
->inject('response')
->inject('dbForInternal')
->inject('database')
->inject('events')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $indexId, $response, $dbForInternal, $database, $events, $audits, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $database, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $database */
@ -1532,7 +1532,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
throw new Exception('Collection not found', 404);
}
$index = $dbForInternal->getDocument('indexes', $collectionId.'_'.$indexId);
$index = $dbForInternal->getDocument('indexes', $collectionId.'_'.$key);
if (empty($index->getId())) {
throw new Exception('Index not found', 404);
@ -1589,13 +1589,15 @@ App::post('/v1/database/collections/:collectionId/documents')
->inject('user')
->inject('audits')
->inject('usage')
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits, $usage) {
->inject('events')
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@ -1607,7 +1609,10 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
@ -1657,6 +1662,8 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('Document already exists', 409);
}
$events->setParam('collection', $collection->getArrayCopy());
$usage
->setParam('database.documents.create', 1)
->setParam('collectionId', $collectionId)
@ -1701,7 +1708,10 @@ App::get('/v1/database/collections/:collectionId/documents')
/** @var Utopia\Database\Database $dbForExternal */
/** @var Appwrite\Stats\Stats $usage */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
@ -1738,8 +1748,10 @@ App::get('/v1/database/collections/:collectionId/documents')
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document[] $documents */
$documents = Authorization::skip(fn() => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection));
$sum = Authorization::skip(fn() => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT));
} else {
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
$sum = $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT);
}
$usage
@ -1748,7 +1760,7 @@ App::get('/v1/database/collections/:collectionId/documents')
;
$response->dynamic(new Document([
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
'sum' => $sum,
'documents' => $documents,
]), Response::MODEL_DOCUMENT_LIST);
});
@ -1775,7 +1787,10 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
/** @var Utopia\Database\Database $$dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
@ -1924,14 +1939,19 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->inject('dbForExternal')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
@ -1943,9 +1963,12 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
if (!$validator->isValid($collection->getWrite())) {
throw new Exception('Unauthorized permissions', 401);
}
$document = Authorization::skip(fn() => $dbForExternal->getDocument($collectionId, $documentId));
} else {
$document = $dbForExternal->getDocument($collectionId, $documentId);
}
$document = $dbForExternal->getDocument($collectionId, $documentId);
if ($document->isEmpty()) {
throw new Exception('Document not found', 404);
@ -2001,7 +2024,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
catch (StructureException $exception) {
throw new Exception($exception->getMessage(), 400);
}
$events->setParam('collection', $collection->getArrayCopy());
$usage
->setParam('database.documents.update', 1)
->setParam('collectionId', $collectionId)
@ -2042,7 +2067,10 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
@ -2067,7 +2095,12 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('No document found', 404);
}
$dbForExternal->deleteDocument($collectionId, $documentId);
if ($collection->getAttribute('permission') === 'collection') {
Authorization::skip(fn() => $dbForExternal->deleteDocument($collectionId, $documentId));
} else {
$dbForExternal->deleteDocument($collectionId, $documentId);
}
$dbForExternal->deleteCachedDocument($collectionId, $documentId);
$usage
@ -2077,6 +2110,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
$events
->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
->setParam('collection', $collection->getArrayCopy());
;
$audits

View file

@ -207,7 +207,14 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
if ($project->getId() !== 'console') {
$payload = new Document($response->getPayload());
$target = Realtime::fromPayload($events->getParam('event'), $payload, $project);
$collection = new Document($events->getParam('collection') ?? []);
$target = Realtime::fromPayload(
event: $events->getParam('event'),
payload: $payload,
project: $project,
collection: $collection
);
Realtime::send(
$target['projectId'] ?? $project->getId(),

View file

@ -240,7 +240,7 @@ class Realtime extends Adapter
* @param Document|null $project
* @return array
*/
public static function fromPayload(string $event, Document $payload, Document $project = null): array
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null): array
{
$channels = [];
$roles = [];
@ -275,12 +275,6 @@ class Realtime extends Adapter
$channels[] = 'teams.' . $payload->getId();
$roles = ['team:' . $payload->getId()];
break;
case strpos($event, 'database.collections.') === 0:
$channels[] = 'collections';
$channels[] = 'collections.' . $payload->getId();
$roles = $payload->getRead();
break;
case strpos($event, 'database.attributes.') === 0:
case strpos($event, 'database.indexes.') === 0:
@ -290,10 +284,15 @@ class Realtime extends Adapter
break;
case strpos($event, 'database.documents.') === 0:
if ($collection->isEmpty()) {
throw new \Exception('Collection need to be passed to to Realtime for Document events in the Database.');
}
$channels[] = 'documents';
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
$channels[] = 'documents.' . $payload->getId();
$roles = $payload->getRead();
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
break;
case strpos($event, 'storage.') === 0:

View file

@ -19,8 +19,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'Movies',
'read' => ['role:all'],
'write' => ['role:all'],
'read' => [],
'write' => [],
'permission' => 'document',
]);
@ -40,7 +40,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -50,7 +50,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'releaseYear',
'key' => 'releaseYear',
'required' => true,
]);
@ -59,7 +59,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'actors',
'key' => 'actors',
'size' => 256,
'required' => false,
'array' => true,
@ -113,8 +113,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'Response Models',
'read' => ['role:all'],
'write' => ['role:all'],
'read' => [],
'write' => [],
'permission' => 'document',
]);
@ -128,7 +128,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'string',
'key' => 'string',
'size' => 16,
'required' => false,
'default' => 'default',
@ -139,7 +139,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'email',
'key' => 'email',
'required' => false,
'default' => 'default@example.com',
]);
@ -149,7 +149,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'enum',
'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
'default' => 'maybe',
@ -160,7 +160,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'ip',
'key' => 'ip',
'required' => false,
'default' => '192.0.2.0',
]);
@ -170,7 +170,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'url',
'key' => 'url',
'required' => false,
'default' => 'http://example.com',
]);
@ -180,7 +180,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'integer',
'key' => 'integer',
'required' => false,
'min' => 1,
'max' => 5,
@ -192,7 +192,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'float',
'key' => 'float',
'required' => false,
'min' => 1.5,
'max' => 5.5,
@ -204,7 +204,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'boolean',
'key' => 'boolean',
'required' => false,
'default' => true,
]);
@ -568,7 +568,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'enum',
'key' => 'enum',
'elements' => ['yes', 'no', ''],
'required' => false,
'default' => 'maybe',
@ -590,7 +590,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'titleIndex',
'key' => 'titleIndex',
'type' => 'fulltext',
'attributes' => ['title'],
]);
@ -606,7 +606,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'releaseYear',
'key' => 'releaseYear',
'type' => 'key',
'attributes' => ['releaseYear'],
]);
@ -1229,8 +1229,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'invalidDocumentStructure',
'read' => ['role:all'],
'write' => ['role:all'],
'read' => [],
'write' => [],
'permission' => 'document',
]);
@ -1244,7 +1244,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'email',
'key' => 'email',
'required' => false,
]);
@ -1253,7 +1253,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'enum',
'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
]);
@ -1263,7 +1263,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'ip',
'key' => 'ip',
'required' => false,
]);
@ -1272,7 +1272,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'url',
'key' => 'url',
'size' => 256,
'required' => false,
]);
@ -1282,7 +1282,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'range',
'key' => 'range',
'required' => false,
'min' => 1,
'max' => 10,
@ -1294,7 +1294,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'floatRange',
'key' => 'floatRange',
'required' => false,
'min' => 1.1,
'max' => 1.4,
@ -1305,7 +1305,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'probability',
'key' => 'probability',
'required' => false,
'min' => 0,
'max' => 1,
@ -1316,7 +1316,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'upperBound',
'key' => 'upperBound',
'required' => false,
'max' => 10,
]);
@ -1326,7 +1326,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'lowerBound',
'key' => 'lowerBound',
'required' => false,
'min' => 5,
]);
@ -1339,7 +1339,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'invalidRange',
'key' => 'invalidRange',
'required' => false,
'min' => 4,
'max' => 3,
@ -1349,7 +1349,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'defaultArray',
'key' => 'defaultArray',
'required' => false,
'default' => 42,
'array' => true,
@ -1788,7 +1788,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute',
'key' => 'attribute',
'size' => 64,
'required' => true,
]);
@ -1804,7 +1804,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'key_attribute',
'key' => 'key_attribute',
'type' => 'key',
'attributes' => [$attribute['body']['key']],
]);
@ -1829,13 +1829,41 @@ trait DatabaseBase
$this->assertEquals(201, $document1['headers']['status-code']);
$document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'attribute' => 'one',
],
'read' => [],
'write' => [$user],
]);
$this->assertEquals(201, $document2['headers']['status-code']);
$document3 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'attribute' => 'one',
],
'read' => [],
'write' => [],
]);
$this->assertEquals(201, $document3['headers']['status-code']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(1, $documents['body']['sum']);
$this->assertCount(1, $documents['body']['documents']);
$this->assertEquals(3, $documents['body']['sum']);
$this->assertCount(3, $documents['body']['documents']);
/*
* Test for Failure
@ -1894,7 +1922,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(404, $documents['headers']['status-code']);
$this->assertEquals(401, $documents['headers']['status-code']);
}
/**
@ -1907,7 +1935,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'unique_title',
'key' => 'unique_title',
'type' => 'unique',
'attributes' => ['title'],
]);

View file

@ -165,7 +165,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'firstName',
'key' => 'firstName',
'size' => 256,
'required' => true,
]);
@ -175,7 +175,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'lastName',
'key' => 'lastName',
'size' => 256,
'required' => true,
]);
@ -185,7 +185,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'unneeded',
'key' => 'unneeded',
'size' => 256,
'required' => true,
]);
@ -214,7 +214,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'key_lastName',
'key' => 'key_lastName',
'type' => 'key',
'attributes' => [
'lastName',
@ -275,7 +275,7 @@ class DatabaseCustomServerTest extends Scope
return [
'collectionId' => $actors['body']['$id'],
'indexId' => $index['body']['key'],
'key' => $index['body']['key'],
];
}
@ -284,7 +284,7 @@ class DatabaseCustomServerTest extends Scope
*/
public function testDeleteIndex($data): array
{
$index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['indexId'], array_merge([
$index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
@ -316,7 +316,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute1',
'key' => 'attribute1',
'size' => 16,
'required' => true,
]);
@ -326,7 +326,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute2',
'key' => 'attribute2',
'size' => 16,
'required' => true,
]);
@ -343,7 +343,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index1',
'key' => 'index1',
'type' => 'key',
'attributes' => ['attribute1', 'attribute2'],
'orders' => ['ASC', 'ASC'],
@ -354,7 +354,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index2',
'key' => 'index2',
'type' => 'key',
'attributes' => ['attribute2'],
]);
@ -428,7 +428,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute1',
'key' => 'attribute1',
'size' => 16,
'required' => true,
]);
@ -438,7 +438,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute2',
'key' => 'attribute2',
'size' => 16,
'required' => true,
]);
@ -455,7 +455,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index1',
'key' => 'index1',
'type' => 'key',
'attributes' => ['attribute1', 'attribute2'],
'orders' => ['ASC', 'ASC'],
@ -466,7 +466,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index2',
'key' => 'index2',
'type' => 'key',
'attributes' => ['attribute2'],
]);
@ -615,7 +615,7 @@ class DatabaseCustomServerTest extends Scope
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]), [
// 'attributeId' => "attribute{$i}",
// 'key' => "attribute{$i}",
// 'required' => false,
// ]);
@ -629,7 +629,7 @@ class DatabaseCustomServerTest extends Scope
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]), [
// 'attributeId' => "tooMany",
// 'key' => "tooMany",
// 'required' => false,
// ]);
@ -663,7 +663,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => "attribute{$i}",
'key' => "attribute{$i}",
'size' => 1024,
'required' => true,
]);
@ -678,7 +678,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'tooWide',
'key' => 'tooWide',
'size' => 1024,
'required' => true,
]);
@ -714,7 +714,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => "attribute{$i}",
'key' => "attribute{$i}",
'size' => 64,
'required' => true,
]);
@ -751,7 +751,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => "key_attribute{$i}",
'key' => "key_attribute{$i}",
'type' => 'key',
'attributes' => ["attribute{$i}"],
]);
@ -780,7 +780,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'tooMany',
'key' => 'tooMany',
'type' => 'key',
'attributes' => ['attribute61'],
]);

View file

@ -26,7 +26,7 @@ class DatabasePermissionsGuestTest extends Scope
$collection = ['id' => $movies['body']['$id']];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $collection['id'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);

View file

@ -65,7 +65,7 @@ class DatabasePermissionsMemberTest extends Scope
$this->collections = ['public' => $public['body']['$id']];
$response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['public'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -83,7 +83,7 @@ class DatabasePermissionsMemberTest extends Scope
$this->collections['private'] = $private['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);

View file

@ -45,7 +45,7 @@ class DatabasePermissionsTeamTest extends Scope
$this->collections['collection1'] = $collection1['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection1'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -61,7 +61,7 @@ class DatabasePermissionsTeamTest extends Scope
$this->collections['collection2'] = $collection2['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection2'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -160,7 +160,7 @@ class DatabasePermissionsTeamTest extends Scope
if ($success) {
$this->assertCount(1, $documents['body']['documents']);
} else {
$this->assertEquals(404, $documents['headers']['status-code']);
$this->assertEquals(401, $documents['headers']['status-code']);
}
}

View file

@ -61,7 +61,7 @@ class RealtimeConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'attributeId' => 'name',
'key' => 'name',
'size' => 256,
'required' => true,
]);
@ -134,7 +134,7 @@ class RealtimeConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'indexId' => 'key_name',
'key' => 'key_name',
'type' => 'key',
'attributes' => [
'name',

View file

@ -77,9 +77,7 @@ class RealtimeCustomClientTest extends Scope
'files',
'files.1',
'collections',
'collections.1',
'collections.1.documents',
'collections.2',
'collections.2.documents',
'documents',
'documents.1',
@ -93,15 +91,13 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals('connected', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertNotEmpty($response['data']['user']);
$this->assertCount(12, $response['data']['channels']);
$this->assertCount(10, $response['data']['channels']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertContains('files', $response['data']['channels']);
$this->assertContains('files.1', $response['data']['channels']);
$this->assertContains('collections', $response['data']['channels']);
$this->assertContains('collections.1', $response['data']['channels']);
$this->assertContains('collections.1.documents', $response['data']['channels']);
$this->assertContains('collections.2', $response['data']['channels']);
$this->assertContains('collections.2.documents', $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.1', $response['data']['channels']);
@ -561,24 +557,11 @@ class RealtimeCustomClientTest extends Scope
]), [
'collectionId' => 'unique()',
'name' => 'Actors',
'read' => ['role:all'],
'write' => ['role:all'],
'permission' => 'collection'
'read' => [],
'write' => [],
'permission' => 'document'
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('collections', $response['data']['channels']);
$this->assertContains('collections.' . $actors['body']['$id'], $response['data']['channels']);
$this->assertEquals('database.collections.create', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$data = ['actorsId' => $actors['body']['$id']];
$name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
@ -662,7 +645,6 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
/**
* Test Document Delete
*/
@ -703,6 +685,166 @@ class RealtimeCustomClientTest extends Scope
$client->close();
}
public function testChannelDatabaseCollectionPermissions()
{
$user = $this->getUser();
$session = $user['session'] ?? '';
$projectId = $this->getProject()['$id'];
$client = $this->getWebsocket(['documents', 'collections'], [
'origin' => 'http://localhost',
'cookie' => 'a_session_'.$projectId.'=' . $session
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('connected', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections', $response['data']['channels']);
$this->assertNotEmpty($response['data']['user']);
$this->assertEquals($user['$id'], $response['data']['user']['$id']);
/**
* Test Collection Create
*/
$actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => 'unique()',
'name' => 'Actors',
'read' => ['role:all'],
'write' => ['role:all'],
'permission' => 'collection'
]);
$data = ['actorsId' => $actors['body']['$id']];
$name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals($name['headers']['status-code'], 201);
$this->assertEquals($name['body']['key'], 'name');
$this->assertEquals($name['body']['type'], 'string');
$this->assertEquals($name['body']['size'], 256);
$this->assertEquals($name['body']['required'], true);
sleep(2);
/**
* Test Document Create
*/
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'name' => 'Chris Evans'
],
'read' => [],
'write' => [],
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(3, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.create', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
$data['documentId'] = $document['body']['$id'];
/**
* Test Document Update
*/
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $data['actorsId'] . '/documents/' . $data['documentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'name' => 'Chris Evans 2'
],
'read' => [],
'write' => [],
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(3, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.update', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
/**
* Test Document Delete
*/
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'name' => 'Bradley Cooper'
],
'read' => [],
'write' => [],
]);
$client->receive();
$this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/documents/' . $document['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(3, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.delete', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
$client->close();
}
public function testChannelFiles()
{
$user = $this->getUser();

View file

@ -57,7 +57,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'firstName',
'key' => 'firstName',
'size' => 256,
'required' => true,
]);
@ -67,7 +67,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'lastName',
'key' => 'lastName',
'size' => 256,
'required' => true,
]);
@ -77,7 +77,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'extra',
'key' => 'extra',
'size' => 64,
'required' => false,
]);

View file

@ -64,7 +64,7 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'fullname',
'key' => 'fullname',
'type' => 'key',
'attributes' => ['lastName', 'firstName'],
'orders' => ['ASC', 'ASC'],
@ -125,7 +125,7 @@ class WebhooksCustomServerTest extends Scope
'write' => ['role:all'],
'permission' => 'document'
]);
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertNotEmpty($actors['body']['$id']);
@ -134,7 +134,7 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($actors['headers']['status-code'], 204);
$webhook = $this->getLastRequest();

View file

@ -2,7 +2,7 @@
namespace Appwrite\Tests;
use Appwrite\Database\Document;
use Utopia\Database\Document;
use Appwrite\Messaging\Adapter\Realtime;
use PHPUnit\Framework\TestCase;
@ -195,4 +195,51 @@ class MessagingTest extends TestCase
$this->assertArrayHasKey('account', $channels);
$this->assertArrayNotHasKey('account.456', $channels);
}
public function testFromPayloadCollectionLevelPermissions(): void
{
/**
* Test Collection Level Permissions
*/
$result = Realtime::fromPayload(
event: 'database.documents.create',
payload: new Document([
'$id' => 'test',
'$collection' => 'collection',
'$read' => ['role:admin'],
'$write' => ['role:admin']
]),
collection: new Document([
'$id' => 'collection',
'$read' => ['role:all'],
'$write' => ['role:all'],
'permission' => 'collection'
])
);
$this->assertContains('role:all', $result['roles']);
$this->assertNotContains('role:admin', $result['roles']);
/**
* Test Document Level Permissions
*/
$result = Realtime::fromPayload(
event: 'database.documents.create',
payload: new Document([
'$id' => 'test',
'$collection' => 'collection',
'$read' => ['role:all'],
'$write' => ['role:all']
]),
collection: new Document([
'$id' => 'collection',
'$read' => ['role:admin'],
'$write' => ['role:admin'],
'permission' => 'document'
])
);
$this->assertContains('role:all', $result['roles']);
$this->assertNotContains('role:admin', $result['roles']);
}
}