mirror of
https://github.com/appwrite/appwrite
synced 2026-05-24 09:28:40 +00:00
Merge pull request #10425 from appwrite/bulk-upsert-realtime
Bulk upsert realtime
This commit is contained in:
commit
101da34672
3 changed files with 171 additions and 1 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
||||||
|
|
||||||
|
use Appwrite\Event\Event;
|
||||||
use Appwrite\Event\StatsUsage;
|
use Appwrite\Event\StatsUsage;
|
||||||
use Appwrite\Extend\Exception;
|
use Appwrite\Extend\Exception;
|
||||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
||||||
|
|
@ -74,11 +75,15 @@ class Upsert extends Action
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('queueForStatsUsage')
|
->inject('queueForStatsUsage')
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('queueForRealtime')
|
||||||
|
->inject('queueForFunctions')
|
||||||
|
->inject('queueForWebhooks')
|
||||||
->inject('plan')
|
->inject('plan')
|
||||||
->callback($this->action(...));
|
->callback($this->action(...));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void
|
public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void
|
||||||
{
|
{
|
||||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||||
if ($database->isEmpty()) {
|
if ($database->isEmpty()) {
|
||||||
|
|
@ -141,5 +146,16 @@ class Upsert extends Action
|
||||||
'total' => $modified,
|
'total' => $modified,
|
||||||
$this->getSdkGroup() => $upserted
|
$this->getSdkGroup() => $upserted
|
||||||
]), $this->getResponseModel());
|
]), $this->getResponseModel());
|
||||||
|
|
||||||
|
$this->triggerBulk(
|
||||||
|
'databases.[databaseId].collections.[collectionId].documents.[documentId].upsert',
|
||||||
|
$database,
|
||||||
|
$collection,
|
||||||
|
$upserted,
|
||||||
|
$queueForEvents,
|
||||||
|
$queueForRealtime,
|
||||||
|
$queueForFunctions,
|
||||||
|
$queueForWebhooks
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,10 @@ class Upsert extends DocumentsUpsert
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('queueForStatsUsage')
|
->inject('queueForStatsUsage')
|
||||||
|
->inject('queueForEvents')
|
||||||
|
->inject('queueForRealtime')
|
||||||
|
->inject('queueForFunctions')
|
||||||
|
->inject('queueForWebhooks')
|
||||||
->inject('plan')
|
->inject('plan')
|
||||||
->callback($this->action(...));
|
->callback($this->action(...));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1188,6 +1188,55 @@ class RealtimeCustomClientTest extends Scope
|
||||||
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||||
$this->assertIsArray($response['data']['payload']['$permissions']);
|
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||||
|
|
||||||
|
// bulk upsert
|
||||||
|
$this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$actorsId}/documents", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'documents' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::unique(),
|
||||||
|
'name' => 'Robert Downey Jr.',
|
||||||
|
'$permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$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(6, $response['data']['channels']);
|
||||||
|
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$response['data']['payload']['$id']}.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*", $response['data']['events']);
|
||||||
|
|
||||||
|
$this->assertNotEmpty($response['data']['payload']);
|
||||||
|
$this->assertIsArray($response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('$id', $response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('name', $response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||||
|
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||||
|
|
||||||
$client->close();
|
$client->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1644,6 +1693,107 @@ class RealtimeCustomClientTest extends Scope
|
||||||
$this->assertIsArray($response2['data']['payload']['$permissions']);
|
$this->assertIsArray($response2['data']['payload']['$permissions']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bulk upsert
|
||||||
|
$this->client->call(Client::METHOD_PUT, "/databases/{$databaseId}/collections/{$actorsId}/documents", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'documents' => [
|
||||||
|
[
|
||||||
|
'$id' => ID::unique(),
|
||||||
|
'name' => 'Robert Downey Jr.',
|
||||||
|
'$permissions' => [
|
||||||
|
Permission::read(Role::user($user1Id)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'$id' => ID::unique(),
|
||||||
|
'name' => 'Thor',
|
||||||
|
'$permissions' => [
|
||||||
|
Permission::read(Role::user($user2Id)),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = json_decode($client1->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(6, $response['data']['channels']);
|
||||||
|
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$response['data']['payload']['$id']}.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*", $response['data']['events']);
|
||||||
|
|
||||||
|
$this->assertNotEmpty($response['data']['payload']);
|
||||||
|
$this->assertIsArray($response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('$id', $response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('name', $response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||||
|
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||||
|
|
||||||
|
// client1 shouldnot receive more than 1 event
|
||||||
|
try {
|
||||||
|
json_decode(json_decode($client1->receive(), true));
|
||||||
|
$this->fail('Expected TimeoutException was not thrown.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->assertInstanceOf(TimeoutException::class, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = json_decode($client2->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(6, $response['data']['channels']);
|
||||||
|
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.{$response['data']['payload']['$id']}.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}.documents.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*.collections.{$actorsId}", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.{$databaseId}.collections.*.documents.*.upsert", $response['data']['events']);
|
||||||
|
$this->assertContains("databases.*", $response['data']['events']);
|
||||||
|
|
||||||
|
$this->assertNotEmpty($response['data']['payload']);
|
||||||
|
$this->assertIsArray($response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('$id', $response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('name', $response['data']['payload']);
|
||||||
|
$this->assertArrayHasKey('$permissions', $response['data']['payload']);
|
||||||
|
$this->assertIsArray($response['data']['payload']['$permissions']);
|
||||||
|
|
||||||
|
// client2 shouldnot receive more than 1 event
|
||||||
|
try {
|
||||||
|
json_decode(json_decode($client2->receive(), true));
|
||||||
|
$this->fail('Expected TimeoutException was not thrown.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->assertInstanceOf(TimeoutException::class, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$client1->close();
|
$client1->close();
|
||||||
$client2->close();
|
$client2->close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue