1
0
Fork 0
mirror of synced 2024-06-26 10:10:57 +12:00

fix realtime with db refactor

This commit is contained in:
Torsten Dittmann 2021-10-07 17:35:17 +02:00
parent 42414e8841
commit a585a9090a
12 changed files with 266 additions and 228 deletions

View file

@ -2282,7 +2282,56 @@ $collections = [
'orders' => [Database::ORDER_DESC], 'orders' => [Database::ORDER_DESC],
], ],
], ],
] ],
'realtime' => [
'$collection' => Database::METADATA,
'$id' => 'realtime',
'name' => 'Realtime Connections',
'attributes' => [
[
'$id' => 'container',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'timestamp',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'value',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [], //TODO: use json filter
]
],
'indexes' => [
[
'$id' => '_key_timestamp',
'type' => Database::INDEX_KEY,
'attributes' => ['timestamp'],
'lengths' => [],
'orders' => [Database::ORDER_DESC],
],
]
],
]; ];
return $collections; return $collections;

View file

@ -699,10 +699,10 @@ App::post('/v1/functions/:functionId/executions')
])); ]));
Authorization::reset(); Authorization::reset();
$jwt = ''; // initialize $jwt = ''; // initialize
if (!$user->isEmpty()) { // If userId exists, generate a JWT for function if (!$user->isEmpty()) { // If userId exists, generate a JWT for function
$sessions = $user->getAttribute('sessions', []); $sessions = $user->getAttribute('sessions', []);
$current = new Document(); $current = new Document();

View file

@ -1,13 +1,13 @@
<?php <?php
use Appwrite\Auth\Auth; use Appwrite\Auth\Auth;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Validator\Authorization;
use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Messaging\Adapter\Realtime;
use Utopia\App; use Utopia\App;
use Utopia\Exception; use Utopia\Exception;
use Utopia\Abuse\Abuse; use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Database\Document;
use Utopia\Storage\Device\Local; use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage; use Utopia\Storage\Storage;
@ -209,11 +209,11 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
$target = Realtime::fromPayload($events->getParam('event'), $payload); $target = Realtime::fromPayload($events->getParam('event'), $payload);
Realtime::send( Realtime::send(
$project->getId(), $project->getId(),
$response->getPayload(), $response->getPayload(),
$events->getParam('event'), $events->getParam('event'),
$target['channels'], $target['channels'],
$target['roles'], $target['roles'],
[ [
'permissionsChanged' => $target['permissionsChanged'], 'permissionsChanged' => $target['permissionsChanged'],
'userId' => $events->getParam('userId') 'userId' => $events->getParam('userId')
@ -221,11 +221,11 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
); );
} }
} }
if (!empty($audits->getParam('event'))) { if (!empty($audits->getParam('event'))) {
$audits->trigger(); $audits->trigger();
} }
if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) { if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) {
$deletes->trigger(); $deletes->trigger();
} }
@ -233,13 +233,13 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
if (!empty($database->getParam('type')) && !empty($database->getParam('document'))) { if (!empty($database->getParam('type')) && !empty($database->getParam('document'))) {
$database->trigger(); $database->trigger();
} }
$route = $utopia->match($request); $route = $utopia->match($request);
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled' if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
&& $project->getId() && $project->getId()
&& $mode !== APP_MODE_ADMIN // TODO: add check to make sure user is admin && $mode !== APP_MODE_ADMIN // TODO: add check to make sure user is admin
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode && !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
$usage $usage
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage')) ->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
->setParam('networkResponseSize', $response->getSize()) ->setParam('networkResponseSize', $response->getSize())

View file

@ -131,7 +131,6 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
} }
$dbForConsole->createCollection($key, $attributes, $indexes); $dbForConsole->createCollection($key, $attributes, $indexes);
} }
Console::success('[Setup] - Server database init completed...'); Console::success('[Setup] - Server database init completed...');

View file

@ -28,11 +28,9 @@ use Appwrite\Event\Event;
use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP; use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\URL; use Appwrite\Network\Validator\URL;
use Appwrite\Event\Realtime;
use Appwrite\OpenSSL\OpenSSL; use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Stats\Stats; use Appwrite\Stats\Stats;
use Utopia\App; use Utopia\App;
use Utopia\CLI\Console;
use Utopia\View; use Utopia\View;
use Utopia\Config\Config; use Utopia\Config\Config;
use Utopia\Locale\Locale; use Utopia\Locale\Locale;

View file

@ -18,6 +18,10 @@ use Utopia\Database\Database;
use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Cache\Cache; use Utopia\Cache\Cache;
use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Registry\Registry;
use Utopia\Swoole\Request; use Utopia\Swoole\Request;
use Utopia\WebSocket\Server; use Utopia\WebSocket\Server;
use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Adapter;
@ -40,124 +44,121 @@ $stats->column('messages', Table::TYPE_INT);
$stats->create(); $stats->create();
$containerId = uniqid(); $containerId = uniqid();
$documentId = null; $statsDocument = null;
$adapter = new Adapter\Swoole(port: App::getEnv('PORT', 80)); $adapter = new Adapter\Swoole(port: App::getEnv('PORT', 80));
$adapter->setPackageMaxLength(64000); // Default maximum Package Size (64kb) $adapter->setPackageMaxLength(64000); // Default maximum Package Size (64kb)
$server = new Server($adapter); $server = new Server($adapter);
$server->onStart(function () use ($stats, $register, $containerId, &$documentId) { function getDatabase(Registry &$register, string $namespace)
{
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache);
$database->setNamespace($namespace);
return [
$database,
function () use ($register, $db, $redis) {
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
}
];
};
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument) {
Console::success('Server started succefully'); Console::success('Server started succefully');
// $getConsoleDb = function () use ($register) {
// $db = $register->get('dbPool')->get();
// $cache = $register->get('redisPool')->get();
// $cache = new Cache(new RedisCache($cache));
// $database = new Database(new MariaDB($db), $cache);
// return [
// $database,
// function () use ($register, $db, $cache) {
// $register->get('dbPool')->put($db);
// $register->get('redisPool')->put($cache);
// }
// ];
// };
/** /**
* Create document for this worker to share stats across Containers. * Create document for this worker to share stats across Containers.
*/ */
// go(function () use ($getConsoleDb, $containerId, &$documentId) { go(function () use ($register, $containerId, &$statsDocument) {
// try { try {
// [$consoleDb, $returnConsoleDb] = call_user_func($getConsoleDb); [$database, $returnDatabase] = getDatabase($register, 'project_console_internal');
// // $document = [ $document = new Document([
// // '$collection' => Database::SYSTEM_COLLECTION_CONNECTIONS, '$id' => $database->getId(),
// // '$permissions' => [ '$collection' => 'realtime',
// // 'read' => ['*'], '$read' => [],
// // 'write' => ['*'], '$write' => [],
// // ], 'container' => $containerId,
// // 'container' => $containerId, 'timestamp' => time(),
// // 'timestamp' => time(), 'value' => '{}'
// // 'value' => '{}' ]);
// // ]; $statsDocument = Authorization::skip(function () use ($database, $document) {
// // Authorization::disable(); return $database->createDocument('realtime', $document);
// // $document = $consoleDb->createDocument($document); });
// // Authorization::enable(); } catch (\Throwable $th) {
// // $documentId = $document->getId(); Console::error('[Error] Type: ' . get_class($th));
// } catch (\Throwable $th) { Console::error('[Error] Message: ' . $th->getMessage());
// Console::error('[Error] Type: ' . get_class($th)); Console::error('[Error] File: ' . $th->getFile());
// Console::error('[Error] Message: ' . $th->getMessage()); Console::error('[Error] Line: ' . $th->getLine());
// Console::error('[Error] File: ' . $th->getFile()); } finally {
// Console::error('[Error] Line: ' . $th->getLine()); call_user_func($returnDatabase);
// } finally { }
// call_user_func($returnConsoleDb); });
// }
// });
// /** /**
// * Save current connections to the Database every 5 seconds. * Save current connections to the Database every 5 seconds.
// */ */
// Timer::tick(5000, function () use ($stats, $getConsoleDb, $containerId, &$documentId) { Timer::tick(5000, function () use ($register, $stats, $containerId, &$statsDocument) {
// foreach ($stats as $projectId => $value) { /** @var Document $statsDocument */
// if (empty($value['connections']) && empty($value['messages'])) { foreach ($stats as $projectId => $value) {
// continue; if (empty($value['connections']) && empty($value['messages'])) {
// } continue;
}
// $connections = $stats->get($projectId, 'connections'); $connections = $stats->get($projectId, 'connections');
// $messages = $stats->get($projectId, 'messages'); $messages = $stats->get($projectId, 'messages');
// $usage = new Event('v1-usage', 'UsageV1'); $usage = new Event('v1-usage', 'UsageV1');
// $usage $usage
// ->setParam('projectId', $projectId) ->setParam('projectId', $projectId)
// ->setParam('realtimeConnections', $connections) ->setParam('realtimeConnections', $connections)
// ->setParam('realtimeMessages', $messages) ->setParam('realtimeMessages', $messages)
// ->setParam('networkRequestSize', 0) ->setParam('networkRequestSize', 0)
// ->setParam('networkResponseSize', 0); ->setParam('networkResponseSize', 0);
// $stats->set($projectId, [ $stats->set($projectId, [
// 'messages' => 0, 'messages' => 0,
// 'connections' => 0 'connections' => 0
// ]); ]);
// if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
// $usage->trigger(); $usage->trigger();
// } }
// } }
// $payload = []; $payload = [];
// foreach ($stats as $projectId => $value) { foreach ($stats as $projectId => $value) {
// if (!empty($value['connectionsTotal'])) { if (!empty($value['connectionsTotal'])) {
// $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); $payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
// } }
// } }
// if (empty($payload)) { if (empty($payload) || empty($statsDocument)) {
// return; return;
// } }
// try { try {
// [$consoleDb, $returnConsoleDb] = call_user_func($getConsoleDb); [$database, $returnDatabase] = getDatabase($register, 'project_console_internal');
// // $consoleDb->updateDocument([ $statsDocument
// // '$id' => $documentId, ->setAttribute('timestamp', time())
// // '$collection' => Database::SYSTEM_COLLECTION_CONNECTIONS, ->setAttribute('value', json_encode($payload));
// // '$permissions' => [
// // 'read' => ['*'], Authorization::skip(function () use ($database, $statsDocument) {
// // 'write' => ['*'], $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument);
// // ], });
// // 'container' => $containerId, } catch (\Throwable $th) {
// // 'timestamp' => time(), Console::error('[Error] Type: ' . get_class($th));
// // 'value' => json_encode($payload) Console::error('[Error] Message: ' . $th->getMessage());
// // ]); Console::error('[Error] File: ' . $th->getFile());
// } catch (\Throwable $th) { Console::error('[Error] Line: ' . $th->getLine());
// Console::error('[Error] Type: ' . get_class($th)); } finally {
// Console::error('[Error] Message: ' . $th->getMessage()); call_user_func($returnDatabase);
// Console::error('[Error] File: ' . $th->getFile()); }
// Console::error('[Error] Line: ' . $th->getLine()); });
// } finally {
// call_user_func($returnConsoleDb);
// }
// });
}); });
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime) { $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime) {
@ -171,21 +172,16 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
* Sending current connections to project channels on the console project every 5 seconds. * Sending current connections to project channels on the console project every 5 seconds.
*/ */
if ($realtime->hasSubscriber('console', 'role:member', 'project')) { if ($realtime->hasSubscriber('console', 'role:member', 'project')) {
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($redis)); [$database, $returnDatabase] = getDatabase($register, 'project_console_internal');
$database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_console_internal');
$payload = []; $payload = [];
$list = [];
// $list = $consoleDb->getCollection([ $list = Authorization::skip(function () use ($database) {
// 'filters' => [ return $database->find('realtime', [
// '$collection=' . Database::SYSTEM_COLLECTION_CONNECTIONS, new Query('timestamp', Query::TYPE_GREATER, [(time() - 15)])
// 'timestamp>' . (time() - 15) ]);
// ], });
// ]);
/** /**
* Aggregate stats across containers. * Aggregate stats across containers.
@ -224,8 +220,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
])); ]));
} }
$register->get('dbPool')->put($db); call_user_func($returnDatabase);
$register->get('redisPool')->put($redis);
} }
/** /**
* Sending test message for SDK E2E tests every 5 seconds. * Sending test message for SDK E2E tests every 5 seconds.
@ -284,12 +279,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
return; return;
} }
$db = $register->get('dbPool')->get(); [$database, $returnDatabase] = getDatabase($register, 'project_' . $projectId . '_internal');
$cache = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($cache));
$database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_' . $projectId .'_internal');
$user = $database->getDocument('users', $userId); $user = $database->getDocument('users', $userId);
@ -297,8 +287,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']);
$register->get('dbPool')->put($db); call_user_func($returnDatabase);
$register->get('redisPool')->put($cache);
} }
$receivers = $realtime->getSubscribers($event); $receivers = $realtime->getSubscribers($event);
@ -374,7 +363,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$cache = new Cache(new RedisCache($redis)); $cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_' . $project->getId() .'_internal'); $database->setNamespace('project_' . $project->getId() . '_internal');
/* /*
* Project Check * Project Check
@ -388,17 +377,16 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
* *
* Abuse limits are connecting 128 times per minute and ip address. * Abuse limits are connecting 128 times per minute and ip address.
*/ */
// $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $database); $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $database);
// $timeLimit $timeLimit
// ->setParam('{ip}', $request->getIP()) ->setParam('{ip}', $request->getIP())
// ->setParam('{url}', $request->getURI()) ->setParam('{url}', $request->getURI());
// ->setup();
// $abuse = new Abuse($timeLimit); $abuse = new Abuse($timeLimit);
// if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
// throw new Exception('Too many requests', 1013); throw new Exception('Too many requests', 1013);
// } }
/* /*
* Validate Client Domain - Check to avoid CSRF attack. * Validate Client Domain - Check to avoid CSRF attack.
@ -457,7 +445,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
Console::error('[Error] Connection Error'); Console::error('[Error] Connection Error');
Console::error('[Error] Code: ' . $response['data']['code']); Console::error('[Error] Code: ' . $response['data']['code']);
Console::error('[Error] Message: ' . $response['data']['message']); Console::error('[Error] Message: ' . $response['data']['message']);
var_dump($th->getFile(), $th->getLine(), $th->getTraceAsString());
} }
if ($th instanceof PDOException) { if ($th instanceof PDOException) {
@ -476,28 +463,27 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
try { try {
$response = new Response(new SwooleResponse()); $response = new Response(new SwooleResponse());
$db = $register->get('dbPool')->get(); $db = $register->get('dbPool')->get();
$cache = $register->get('redisPool')->get(); $redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($cache)); $cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache); $database = new Database(new MariaDB($db), $cache);
$database->setNamespace('project_' . $realtime->connections[$connection]['projectId'] .'_internal'); $database->setNamespace('project_' . $realtime->connections[$connection]['projectId'] . '_internal');
/* /*
* Abuse Check * Abuse Check
* *
* Abuse limits are sending 32 times per minute and connection. * Abuse limits are sending 32 times per minute and connection.
*/ */
// $timeLimit = new TimeLimit('url:{url},conection:{connection}', 32, 60, $database); $timeLimit = new TimeLimit('url:{url},conection:{connection}', 32, 60, $database);
// $timeLimit $timeLimit
// ->setParam('{connection}', $connection) ->setParam('{connection}', $connection)
// ->setParam('{container}', $containerId) ->setParam('{container}', $containerId);
// ->setup();
// $abuse = new Abuse($timeLimit); $abuse = new Abuse($timeLimit);
// if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
// throw new Exception('Too many messages', 1013); throw new Exception('Too many messages', 1013);
// } }
$message = json_decode($message, true); $message = json_decode($message, true);
@ -506,7 +492,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
} }
switch ($message['type']) { switch ($message['type']) {
/** /**
* This type is used to authenticate. * This type is used to authenticate.
*/ */
case 'authentication': case 'authentication':
@ -515,8 +501,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
} }
$session = Auth::decodeSession($message['data']['session']); $session = Auth::decodeSession($message['data']['session']);
Auth::$unique = $session['id']; Auth::$unique = $session['id'] ?? '';
Auth::$secret = $session['secret']; Auth::$secret = $session['secret'] ?? '';
$user = $database->getDocument('users', Auth::$unique); $user = $database->getDocument('users', Auth::$unique);
@ -564,7 +550,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
} }
} finally { } finally {
$register->get('dbPool')->put($db); $register->get('dbPool')->put($db);
$register->get('redisPool')->put($cache); $register->get('redisPool')->put($redis);
} }
}); });

View file

@ -184,7 +184,7 @@ class Realtime extends Adapter
*/ */
if ( if (
\array_key_exists($channel, $this->subscriptions[$event['project']][$role]) \array_key_exists($channel, $this->subscriptions[$event['project']][$role])
&& (\in_array($role, $event['roles']) || \in_array('*', $event['roles'])) && (\in_array($role, $event['roles']) || \in_array('role:all', $event['roles']))
) { ) {
/** /**
* Saving all connections that are allowed to receive this event. * Saving all connections that are allowed to receive this event.
@ -277,28 +277,29 @@ class Realtime extends Adapter
case strpos($event, 'database.collections.') === 0: case strpos($event, 'database.collections.') === 0:
$channels[] = 'collections'; $channels[] = 'collections';
$channels[] = 'collections.' . $payload->getId(); $channels[] = 'collections.' . $payload->getId();
$roles = $payload->getAttribute('$permissions.read'); $roles = $payload->getRead();
break; break;
case strpos($event, 'database.documents.') === 0: case strpos($event, 'database.documents.') === 0:
$channels[] = 'documents'; $channels[] = 'documents';
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents'; $channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
$channels[] = 'documents.' . $payload->getId(); $channels[] = 'documents.' . $payload->getId();
$roles = $payload->getAttribute('$permissions.read'); $roles = $payload->getRead();
break; break;
case strpos($event, 'storage.') === 0: case strpos($event, 'storage.') === 0:
$channels[] = 'files'; $channels[] = 'files';
$channels[] = 'files.' . $payload->getId(); $channels[] = 'files.' . $payload->getId();
$roles = $payload->getAttribute('$permissions.read'); $roles = $payload->getRead();
break; break;
case strpos($event, 'functions.executions.') === 0: case strpos($event, 'functions.executions.') === 0:
if (!empty($payload->getAttribute('$permissions.read'))) { \var_dump($payload->getArrayCopy());
if (!empty($payload->getRead())) {
$channels[] = 'executions'; $channels[] = 'executions';
$channels[] = 'executions.' . $payload->getId(); $channels[] = 'executions.' . $payload->getId();
$channels[] = 'functions.' . $payload->getAttribute('functionId'); $channels[] = 'functions.' . $payload->getAttribute('functionId');
$roles = $payload->getAttribute('$permissions.read'); $roles = $payload->getRead();
} }
break; break;
} }

View file

@ -16,12 +16,12 @@ class Execution extends Model
'default' => '', 'default' => '',
'example' => '5e5ea5c16897e', 'example' => '5e5ea5c16897e',
]) ])
->addRule('$permissions', [ ->addRule('$read', [
'type' => Response::MODEL_PERMISSIONS, 'type' => self::TYPE_STRING,
'description' => 'Execution permissions.', 'description' => 'Execution read permissions.',
'default' => new \stdClass, 'default' => '',
'example' => new \stdClass, 'example' => 'role:all',
'array' => false, 'array' => true,
]) ])
->addRule('functionId', [ ->addRule('functionId', [
'type' => self::TYPE_STRING, 'type' => self::TYPE_STRING,

View file

@ -190,7 +190,6 @@ trait RealtimeBase
$user = $this->getUser(); $user = $this->getUser();
$userId = $user['$id'] ?? ''; $userId = $user['$id'] ?? '';
$session = $user['session'] ?? ''; $session = $user['session'] ?? '';
$projectId = $this->getProject()['$id'];
/** /**
* Test for SUCCESS * Test for SUCCESS
@ -617,27 +616,11 @@ trait RealtimeBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
]), [ ]), [
'collectionId' => 'unique()',
'name' => 'Actors', 'name' => 'Actors',
'read' => ['*'], 'read' => ['role:all'],
'write' => ['*'], 'write' => ['role:all'],
'rules' => [ 'permission' => 'collection'
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]); ]);
$response = json_decode($client->receive(), true); $response = json_decode($client->receive(), true);
@ -655,6 +638,24 @@ trait RealtimeBase
$data = ['actorsId' => $actors['body']['$id']]; $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']
]), [
'attributeId' => '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 * Test Document Create
*/ */
@ -662,12 +663,12 @@ trait RealtimeBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [ 'data' => [
'firstName' => 'Chris', 'name' => 'Chris Evans'
'lastName' => 'Evans',
], ],
'read' => ['*'], 'read' => ['role:all'],
'write' => ['*'], 'write' => ['role:all'],
]); ]);
$response = json_decode($client->receive(), true); $response = json_decode($client->receive(), true);
@ -683,6 +684,7 @@ trait RealtimeBase
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']); $this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.create', $response['data']['event']); $this->assertEquals('database.documents.create', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']); $this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
$data['documentId'] = $document['body']['$id']; $data['documentId'] = $document['body']['$id'];
@ -693,12 +695,12 @@ trait RealtimeBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [ 'data' => [
'firstName' => 'Chris1', 'name' => 'Chris Evans 2'
'lastName' => 'Evans2',
], ],
'read' => ['*'], 'read' => ['role:all'],
'write' => ['*'], 'write' => ['role:all'],
]); ]);
$response = json_decode($client->receive(), true); $response = json_decode($client->receive(), true);
@ -715,8 +717,8 @@ trait RealtimeBase
$this->assertEquals('database.documents.update', $response['data']['event']); $this->assertEquals('database.documents.update', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']); $this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['firstName'], 'Chris1'); $this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
$this->assertEquals($response['data']['payload']['lastName'], 'Evans2');
/** /**
* Test Document Delete * Test Document Delete
@ -725,13 +727,12 @@ trait RealtimeBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [ 'data' => [
'firstName' => 'Bradly', 'name' => 'Bradley Cooper'
'lastName' => 'Cooper',
], ],
'read' => ['*'], 'read' => ['role:all'],
'write' => ['*'], 'write' => ['role:all'],
]); ]);
$client->receive(); $client->receive();
@ -754,6 +755,7 @@ trait RealtimeBase
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']); $this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.delete', $response['data']['event']); $this->assertEquals('database.documents.delete', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']); $this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
$client->close(); $client->close();
} }
@ -786,9 +788,10 @@ trait RealtimeBase
'content-type' => 'multipart/form-data', 'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'fileId' => 'unique()',
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'), 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
'read' => ['*'], 'read' => ['role:all'],
'write' => ['*'], 'write' => ['role:all'],
'folderId' => 'xyz', 'folderId' => 'xyz',
]); ]);
@ -814,8 +817,8 @@ trait RealtimeBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'read' => ['*'], 'read' => ['role:all'],
'write' => ['*'], 'write' => ['role:all'],
]); ]);
$response = json_decode($client->receive(), true); $response = json_decode($client->receive(), true);
@ -878,16 +881,17 @@ trait RealtimeBase
$this->assertEquals($user['$id'], $response['data']['user']['$id']); $this->assertEquals($user['$id'], $response['data']['user']['$id']);
/** /**
* Test File Create * Test Functions Create
*/ */
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ $function = $this->client->call(Client::METHOD_POST, '/functions', [
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
]), [ ], [
'functionId' => 'unique()',
'name' => 'Test', 'name' => 'Test',
'execute' => ['role:member'],
'runtime' => 'php-8.0', 'runtime' => 'php-8.0',
'execute' => ['*'],
'timeout' => 10, 'timeout' => 10,
]); ]);
@ -988,6 +992,7 @@ trait RealtimeBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $projectId, 'x-appwrite-project' => $projectId,
], $this->getHeaders()), [ ], $this->getHeaders()), [
'teamId' => 'unique()',
'name' => 'Arsenal' 'name' => 'Arsenal'
]); ]);

View file

@ -3,7 +3,7 @@
namespace Appwrite\Tests; namespace Appwrite\Tests;
use Appwrite\Auth\Auth; use Appwrite\Auth\Auth;
use Appwrite\Database\Document; use Utopia\Database\Document;
use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Messaging\Adapter\Realtime;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -146,14 +146,14 @@ class MessagingChannelsTest extends TestCase
} }
/** /**
* Tests Wildcard (*) Permissions on every channel. * Tests Wildcard (role:all) Permissions on every channel.
*/ */
public function testWildcardPermission() public function testWildcardPermission()
{ {
foreach ($this->allChannels as $index => $channel) { foreach ($this->allChannels as $index => $channel) {
$event = [ $event = [
'project' => '1', 'project' => '1',
'roles' => ['*'], 'roles' => ['role:all'],
'data' => [ 'data' => [
'channels' => [ 'channels' => [
0 => $channel, 0 => $channel,

View file

@ -20,7 +20,7 @@ class MessagingGuestTest extends TestCase
$event = [ $event = [
'project' => '1', 'project' => '1',
'roles' => ['*'], 'roles' => ['role:all'],
'data' => [ 'data' => [
'channels' => [ 'channels' => [
0 => 'documents', 0 => 'documents',
@ -95,7 +95,7 @@ class MessagingGuestTest extends TestCase
$this->assertEmpty($receivers); $this->assertEmpty($receivers);
$event['roles'] = ['*']; $event['roles'] = ['role:all'];
$event['data']['channels'] = ['documents.123']; $event['data']['channels'] = ['documents.123'];
$receivers = $realtime->getSubscribers($event); $receivers = $realtime->getSubscribers($event);

View file

@ -29,7 +29,7 @@ class MessagingTest extends TestCase
$event = [ $event = [
'project' => '1', 'project' => '1',
'roles' => ['*'], 'roles' => ['role:all'],
'data' => [ 'data' => [
'channels' => [ 'channels' => [
0 => 'account.123', 0 => 'account.123',
@ -103,7 +103,7 @@ class MessagingTest extends TestCase
$this->assertEmpty($receivers); $this->assertEmpty($receivers);
$event['roles'] = ['*']; $event['roles'] = ['role:all'];
$event['data']['channels'] = ['documents.123']; $event['data']['channels'] = ['documents.123'];
$receivers = $realtime->getSubscribers($event); $receivers = $realtime->getSubscribers($event);