Merge pull request #11210 from appwrite/fix-events

This commit is contained in:
Jake Barnby 2026-01-30 10:05:18 +00:00 committed by GitHub
commit d3974776be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 121 additions and 27 deletions

View file

@ -39,38 +39,34 @@ class EventProcessor
return \json_decode($cachedFunctionEvents, true) ?? [];
}
try {
$events = [];
$limit = 100;
$sum = 100;
$offset = 0;
$events = [];
$limit = 100;
$sum = 100;
$offset = 0;
while ($sum >= $limit) {
$functions = $dbForProject->find('functions', [
Query::select(['$id', 'events']),
Query::limit($limit),
Query::offset($offset),
Query::orderAsc('$sequence'),
]);
while ($sum >= $limit) {
$functions = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('functions', [
Query::select(['$id', 'events']),
Query::limit($limit),
Query::offset($offset),
Query::orderAsc('$sequence'),
]));
$sum = \count($functions);
$offset = $offset + $limit;
$sum = \count($functions);
$offset = $offset + $limit;
foreach ($functions as $function) {
$functionEvents = $function->getAttribute('events', []);
if (!empty($functionEvents)) {
$events = array_merge($events, $functionEvents);
}
foreach ($functions as $function) {
$functionEvents = $function->getAttribute('events', []);
if (!empty($functionEvents)) {
\array_push($events, ...$functionEvents);
}
}
$uniqueEvents = \array_flip(\array_unique($events));
$dbForProject->getCache()->save($cacheKey, \json_encode($uniqueEvents));
return $uniqueEvents;
} catch (\Throwable $e) {
return [];
}
$uniqueEvents = \array_flip(\array_unique($events));
$dbForProject->getCache()->save($cacheKey, \json_encode($uniqueEvents));
return $uniqueEvents;
}
/**
@ -97,7 +93,7 @@ class EventProcessor
$webhookEvents = $webhook->getAttribute('events', []);
if (!empty($webhookEvents)) {
$events = array_merge($events, $webhookEvents);
\array_push($events, ...$webhookEvents);
}
}

View file

@ -7,6 +7,7 @@ use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\System\System;
@ -510,4 +511,101 @@ class FunctionsCustomClientTest extends Scope
$template = $this->getTemplate('invalid-template-id');
$this->assertEquals(404, $template['headers']['status-code']);
}
/**
* Test that event-triggered functions work when the triggering request
* comes from a client SDK (session auth) that doesn't have permission
* to read the functions collection.
*/
public function testEventTriggerWithClientAuth()
{
$functionId = $this->setupFunction([
'functionId' => ID::unique(),
'name' => 'Test Client Event Trigger',
'runtime' => 'node-22',
'entrypoint' => 'index.js',
'events' => [
'databases.*.collections.*.documents.*.create',
],
'timeout' => 15,
]);
$this->setupDeployment($functionId, [
'code' => $this->packageFunction('event-handler'),
'activate' => true
]);
$database = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'databaseId' => ID::unique(),
'name' => 'Test Database',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'collectionId' => ID::unique(),
'name' => 'Test Collection',
'permissions' => [
Permission::create(Role::users()),
],
'documentSecurity' => false,
]);
$this->assertEquals(201, $collection['headers']['status-code']);
$collectionId = $collection['body']['$id'];
$attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'key' => 'name',
'size' => 255,
'required' => false,
]);
$this->assertEquals(202, $attribute['headers']['status-code']);
sleep(2);
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => ID::unique(),
'data' => ['name' => 'Test Document'],
]);
$this->assertEquals(201, $document['headers']['status-code']);
$documentId = $document['body']['$id'];
$this->assertEventually(function () use ($functionId, $documentId) {
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertGreaterThan(0, count($executions['body']['executions']), 'Function should have been triggered by document creation');
$lastExecution = $executions['body']['executions'][0];
$this->assertEquals('completed', $lastExecution['status']);
$this->assertEquals(204, $lastExecution['responseStatusCode']);
$this->assertStringContainsString($documentId, $lastExecution['logs']);
}, 20000, 500);
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->cleanupFunction($functionId);
}
}