Introduce new webhooks API

This commit is contained in:
Matej Bačo
2026-03-17 11:03:18 +01:00
parent b093a3fb24
commit 677bb048cc
15 changed files with 3003 additions and 2749 deletions
+2
View File
@@ -16,6 +16,7 @@ use Appwrite\Platform\Modules\Storage;
use Appwrite\Platform\Modules\Teams;
use Appwrite\Platform\Modules\Tokens;
use Appwrite\Platform\Modules\VCS;
use Appwrite\Platform\Modules\Webhooks;
use Utopia\Platform\Platform;
class Appwrite extends Platform
@@ -36,5 +37,6 @@ class Appwrite extends Platform
$this->addModule(new Tokens\Module());
$this->addModule(new Storage\Module());
$this->addModule(new VCS\Module());
$this->addModule(new Webhooks\Module());
}
}
@@ -0,0 +1,113 @@
<?php
namespace Appwrite\Platform\Modules\Webhooks\Http\Webhooks;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\Queries\Webhooks;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Boolean;
class XList extends Base
{
use HTTP;
public static function getName()
{
return 'listWebhooks';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
->setHttpPath('/v1/webhooks')
->desc('List webhooks')
->groups(['api', 'webhooks'])
->label('scope', 'webhooks.read')
->label('sdk', new Method(
namespace: 'webhooks',
group: null,
name: 'list',
description: <<<EOT
Get a list of all webhooks belonging to the project. You can use the query params to filter your results.
EOT,
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_WEBHOOK_LIST,
)
]
))
->param('queries', [], new Webhooks(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Webhooks::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('project')
->inject('response')
->inject('dbForPlatform')
->inject('authorization')
->callback($this->action(...));
}
public function action(array $queries, bool $includeTotal, Document $project, Response $response, Database $dbForPlatform, Authorization $authorization)
{
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$webhookId = $cursor->getValue();
$cursorDocument = $authorization->skip(fn () => $dbForPlatform->getDocument('webhooks', $webhookId, [
Query::equal('projectInternalId', [$project->getSequence()]),
]));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Webhook '{$webhookId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$webhooks = $authorization->skip(fn () => $dbForPlatform->find('webhooks', $queries));
$total = $includeTotal ? $authorization->skip(fn () => $dbForPlatform->count('webhooks', $filterQueries, APP_LIMIT_COUNT)) : 0;
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'webhooks' => $webhooks,
'total' => $total,
]), Response::MODEL_WEBHOOK_LIST);
}
}
@@ -0,0 +1,14 @@
<?php
namespace Appwrite\Platform\Modules\Webhooks;
use Appwrite\Platform\Modules\Webhooks\Services\Http;
use Utopia\Platform;
class Module extends Platform\Module
{
public function __construct()
{
$this->addService('http', new Http());
}
}
@@ -0,0 +1,17 @@
<?php
namespace Appwrite\Platform\Modules\Webhooks\Services;
use Appwrite\Platform\Modules\Webhooks\Http\Webhooks\XList as ListWebhooks;
use Utopia\Platform\Service;
class Http extends Service
{
public function __construct()
{
$this->type = Service::TYPE_HTTP;
// Webhooks
$this->addAction(ListWebhooks::getName(), new ListWebhooks());
}
}
@@ -332,6 +332,8 @@ class Migrations extends Action
'messages.write',
'targets.read',
'targets.write',
'webhooks.read',
'webhooks.write'
]
]);
@@ -0,0 +1,26 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Webhooks extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'url',
'httpUser',
'security',
'events',
'enabled',
'logs',
'attempts',
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('webhooks', self::ALLOWED_ATTRIBUTES);
}
}