Merge branch 'feat-265-realtime' of https://github.com/appwrite/appwrite into feat-265-realtime-usage
This commit is contained in:
commit
ba8fef944c
6 changed files with 208 additions and 3 deletions
|
@ -37,10 +37,12 @@ App::post('/v1/teams')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('user')
|
->inject('user')
|
||||||
->inject('projectDB')
|
->inject('projectDB')
|
||||||
->action(function ($name, $roles, $response, $user, $projectDB) {
|
->inject('events')
|
||||||
|
->action(function ($name, $roles, $response, $user, $projectDB, $events) {
|
||||||
/** @var Appwrite\Utopia\Response $response */
|
/** @var Appwrite\Utopia\Response $response */
|
||||||
/** @var Appwrite\Database\Document $user */
|
/** @var Appwrite\Database\Document $user */
|
||||||
/** @var Appwrite\Database\Database $projectDB */
|
/** @var Appwrite\Database\Database $projectDB */
|
||||||
|
/** @var Appwrite\Event\Event $events */
|
||||||
|
|
||||||
Authorization::disable();
|
Authorization::disable();
|
||||||
|
|
||||||
|
@ -90,6 +92,10 @@ App::post('/v1/teams')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($user->getId())) {
|
||||||
|
$events->setParam('userId', $user->getId());
|
||||||
|
}
|
||||||
|
|
||||||
$response
|
$response
|
||||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||||
->dynamic($team, Response::MODEL_TEAM)
|
->dynamic($team, Response::MODEL_TEAM)
|
||||||
|
|
|
@ -201,6 +201,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
|
||||||
if ($project->getId() !== 'console') {
|
if ($project->getId() !== 'console') {
|
||||||
$realtime
|
$realtime
|
||||||
->setEvent($events->getParam('event'))
|
->setEvent($events->getParam('event'))
|
||||||
|
->setUserId($events->getParam('userId'))
|
||||||
->setProject($project->getId())
|
->setProject($project->getId())
|
||||||
->setPayload($response->getPayload())
|
->setPayload($response->getPayload())
|
||||||
->trigger();
|
->trigger();
|
||||||
|
|
|
@ -17,6 +17,11 @@ class Realtime
|
||||||
*/
|
*/
|
||||||
protected $event = '';
|
protected $event = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $userId = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -27,6 +32,11 @@ class Realtime
|
||||||
*/
|
*/
|
||||||
protected $permissions = [];
|
protected $permissions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var false
|
||||||
|
*/
|
||||||
|
protected $permissionsChanged = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Document
|
* @var Document
|
||||||
*/
|
*/
|
||||||
|
@ -57,6 +67,16 @@ class Realtime
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* return $this
|
||||||
|
*/
|
||||||
|
public function setUserId(string $userId): self
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -120,6 +140,20 @@ class Realtime
|
||||||
$this->channels[] = 'account.' . $this->payload->getId();
|
$this->channels[] = 'account.' . $this->payload->getId();
|
||||||
$this->permissions = ['user:' . $this->payload->getId()];
|
$this->permissions = ['user:' . $this->payload->getId()];
|
||||||
|
|
||||||
|
break;
|
||||||
|
case strpos($this->event, 'teams.memberships') === 0:
|
||||||
|
$this->permissionsChanged = in_array($this->event, ['teams.memberships.update', 'teams.memberships.delete', 'teams.memberships.update.status']);
|
||||||
|
$this->channels[] = 'memberships';
|
||||||
|
$this->channels[] = 'memberships.' . $this->payload->getId();
|
||||||
|
$this->permissions = ['team:' . $this->payload->getAttribute('teamId')];
|
||||||
|
|
||||||
|
break;
|
||||||
|
case strpos($this->event, 'teams.') === 0:
|
||||||
|
$this->permissionsChanged = $this->event === 'teams.create';
|
||||||
|
$this->channels[] = 'teams';
|
||||||
|
$this->channels[] = 'teams.' . $this->payload->getId();
|
||||||
|
$this->permissions = ['team:' . $this->payload->getId()];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case strpos($this->event, 'database.collections.') === 0:
|
case strpos($this->event, 'database.collections.') === 0:
|
||||||
$this->channels[] = 'collections';
|
$this->channels[] = 'collections';
|
||||||
|
@ -148,7 +182,7 @@ class Realtime
|
||||||
$this->permissions = $this->payload->getAttribute('$permissions.read');
|
$this->permissions = $this->payload->getAttribute('$permissions.read');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,6 +200,8 @@ class Realtime
|
||||||
$redis->publish('realtime', json_encode([
|
$redis->publish('realtime', json_encode([
|
||||||
'project' => $this->project,
|
'project' => $this->project,
|
||||||
'permissions' => $this->permissions,
|
'permissions' => $this->permissions,
|
||||||
|
'permissionsChanged' => $this->permissionsChanged,
|
||||||
|
'userId' => $this->userId,
|
||||||
'data' => [
|
'data' => [
|
||||||
'event' => $this->event,
|
'event' => $this->event,
|
||||||
'channels' => $this->channels,
|
'channels' => $this->channels,
|
||||||
|
|
|
@ -163,6 +163,7 @@ class Parser
|
||||||
$connections[$connection] = [
|
$connections[$connection] = [
|
||||||
'projectId' => $projectId,
|
'projectId' => $projectId,
|
||||||
'roles' => $roles,
|
'roles' => $roles,
|
||||||
|
'channels' => $channels
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ class Server
|
||||||
return $db;
|
return $db;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->register->set('cache', function () use (&$redis) { // Register cache connection
|
$this->register->set('cache', function () use (&$redis) {
|
||||||
return $redis;
|
return $redis;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -440,6 +440,10 @@ class Server
|
||||||
{
|
{
|
||||||
$event = json_decode($payload, true);
|
$event = json_decode($payload, true);
|
||||||
|
|
||||||
|
if ($event['permissionsChanged'] && isset($event['userId'])) {
|
||||||
|
$this->addPermission($event);
|
||||||
|
}
|
||||||
|
|
||||||
$receivers = Parser::identifyReceivers($event, $this->subscriptions);
|
$receivers = Parser::identifyReceivers($event, $this->subscriptions);
|
||||||
|
|
||||||
// Temporarily print debug logs by default for Alpha testing.
|
// Temporarily print debug logs by default for Alpha testing.
|
||||||
|
@ -512,4 +516,43 @@ class Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addPermission(array $event)
|
||||||
|
{
|
||||||
|
$project = $event['project'];
|
||||||
|
$userId = $event['userId'];
|
||||||
|
|
||||||
|
if (array_key_exists($project, $this->subscriptions) && array_key_exists('user:'.$userId, $this->subscriptions[$project])) {
|
||||||
|
$connection = array_key_first(reset($this->subscriptions[$project]['user:'.$userId]));
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is redundant soon and will be gone with merging the usage branch.
|
||||||
|
*/
|
||||||
|
$db = $this->register->get('dbPool')->get();
|
||||||
|
$redis = $this->register->get('redisPool')->get();
|
||||||
|
|
||||||
|
$this->register->set('db', function () use (&$db) {
|
||||||
|
return $db;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->register->set('cache', function () use (&$redis) {
|
||||||
|
return $redis;
|
||||||
|
});
|
||||||
|
|
||||||
|
$projectDB = new Database();
|
||||||
|
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($this->register), $this->register));
|
||||||
|
$projectDB->setNamespace('app_'.$project);
|
||||||
|
$projectDB->setMocks(Config::getParam('collections', []));
|
||||||
|
|
||||||
|
$user = $projectDB->getDocument($userId);
|
||||||
|
|
||||||
|
Parser::setUser($user);
|
||||||
|
|
||||||
|
$roles = Parser::getRoles();
|
||||||
|
|
||||||
|
Parser::subscribe($project, $connection, $roles, $this->subscriptions, $this->connections, $this->connections[$connection]['channels']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -686,4 +686,122 @@ trait RealtimeBase
|
||||||
|
|
||||||
$client->close();
|
$client->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testChannelTeams(): array
|
||||||
|
{
|
||||||
|
$user = $this->getUser();
|
||||||
|
$session = $user['session'] ?? '';
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
|
||||||
|
$client = $this->getWebsocket(['teams'], [
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'cookie' => 'a_session_'.$projectId.'=' . $session
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = json_decode($client->receive(), true);
|
||||||
|
|
||||||
|
$this->assertCount(1, $response);
|
||||||
|
$this->assertArrayHasKey('teams', $response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Team Create
|
||||||
|
*/
|
||||||
|
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'name' => 'Arsenal'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$teamId = $team['body']['$id'] ?? '';
|
||||||
|
|
||||||
|
$this->assertEquals(201, $team['headers']['status-code']);
|
||||||
|
$this->assertNotEmpty($team['body']['$id']);
|
||||||
|
|
||||||
|
$response = json_decode($client->receive(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('timestamp', $response);
|
||||||
|
$this->assertCount(2, $response['channels']);
|
||||||
|
$this->assertContains('teams', $response['channels']);
|
||||||
|
$this->assertContains('teams.' . $teamId, $response['channels']);
|
||||||
|
$this->assertEquals('teams.create', $response['event']);
|
||||||
|
$this->assertNotEmpty($response['payload']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Team Update
|
||||||
|
*/
|
||||||
|
$team = $this->client->call(Client::METHOD_PUT, '/teams/'.$teamId, array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'name' => 'Manchester'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($team['headers']['status-code'], 200);
|
||||||
|
$this->assertNotEmpty($team['body']['$id']);
|
||||||
|
|
||||||
|
$response = json_decode($client->receive(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('timestamp', $response);
|
||||||
|
$this->assertCount(2, $response['channels']);
|
||||||
|
$this->assertContains('teams', $response['channels']);
|
||||||
|
$this->assertContains('teams.' . $teamId, $response['channels']);
|
||||||
|
$this->assertEquals('teams.update', $response['event']);
|
||||||
|
$this->assertNotEmpty($response['payload']);
|
||||||
|
|
||||||
|
$client->close();
|
||||||
|
|
||||||
|
return ['teamId' => $teamId];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testChannelTeams
|
||||||
|
*/
|
||||||
|
public function testChannelMemberships(array $data)
|
||||||
|
{
|
||||||
|
$teamId = $data['teamId'] ?? '';
|
||||||
|
|
||||||
|
$user = $this->getUser();
|
||||||
|
$session = $user['session'] ?? '';
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
|
||||||
|
$client = $this->getWebsocket(['memberships'], [
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'cookie' => 'a_session_'.$projectId.'='.$session
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = json_decode($client->receive(), true);
|
||||||
|
|
||||||
|
$this->assertCount(1, $response);
|
||||||
|
$this->assertArrayHasKey('memberships', $response);
|
||||||
|
|
||||||
|
$response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamId.'/memberships', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
$membershipId = $response['body']['memberships'][0]['$id'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Update Membership
|
||||||
|
*/
|
||||||
|
$roles = ['admin', 'editor', 'uncle'];
|
||||||
|
$this->client->call(Client::METHOD_PATCH, '/teams/'.$teamId.'/memberships/'.$membershipId, array_merge([
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'roles' => $roles
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = json_decode($client->receive(), true);
|
||||||
|
$this->assertArrayHasKey('timestamp', $response);
|
||||||
|
$this->assertCount(2, $response['channels']);
|
||||||
|
$this->assertContains('memberships', $response['channels']);
|
||||||
|
$this->assertContains('memberships.' . $membershipId, $response['channels']);
|
||||||
|
$this->assertEquals('teams.memberships.update', $response['event']);
|
||||||
|
$this->assertNotEmpty($response['payload']);
|
||||||
|
|
||||||
|
$client->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue