diff --git a/CHANGES.md b/CHANGES.md index b36f2e5109..7d43e22c94 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ - API Key name max length is now 128 chars and not 256 for better API consistency - Task name max length is now 128 chars and not 256 for better API consistency - Platform name max length is now 128 chars and not 256 for better API consistency +- Webhooks payloads are now exactly the same as any of the API response objects - New and consistent response format for all API object + new response examples in the docs - Removed user roles attribute from user object (can be fetched from /v1/teams/memberships) ** - Removed type attribute from session object response (used only internally) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 9dcd2109ef..5a86298aae 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -41,7 +41,7 @@ App::post('/v1/account') ->param('email', '', function () { return new Email(); }, 'User email.') ->param('password', '', function () { return new Password(); }, 'User password. Must be between 6 to 32 chars.') ->param('name', '', function () { return new Text(128); }, 'User name. Max length: 128 chars.', true) - ->action(function ($email, $password, $name, $request, $response, $project, $projectDB, $webhooks, $audits) use ($oauth2Keys) { + ->action(function ($email, $password, $name, $request, $response, $project, $projectDB, $webhooks, $audits) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $project */ @@ -107,13 +107,6 @@ App::post('/v1/account') throw new Exception('Failed saving user to DB', 500); } - $webhooks - ->setParam('payload', [ - 'name' => $name, - 'email' => $email, - ]) - ; - $audits ->setParam('userId', $user->getId()) ->setParam('event', 'account.create') @@ -237,14 +230,7 @@ App::post('/v1/account/sessions') if (false === $profile) { throw new Exception('Failed saving user to DB', 500); } - - $webhooks - ->setParam('payload', [ - 'name' => $profile->getAttribute('name', ''), - 'email' => $profile->getAttribute('email', ''), - ]) - ; - + $audits ->setParam('userId', $profile->getId()) ->setParam('event', 'account.sessions.create') @@ -990,10 +976,7 @@ App::delete('/v1/account') ; $webhooks - ->setParam('payload', [ - 'name' => $user->getAttribute('name', ''), - 'email' => $user->getAttribute('email', ''), - ]) + ->setParam('payload', $response->output($user, Response::MODEL_USER)) ; if (!Config::getParam('domainVerification')) { @@ -1048,10 +1031,7 @@ App::delete('/v1/account/sessions/:sessionId') ; $webhooks - ->setParam('payload', [ - 'name' => $user->getAttribute('name', ''), - 'email' => $user->getAttribute('email', ''), - ]) + ->setParam('payload', $response->output($user, Response::MODEL_USER)) ; if (!Config::getParam('domainVerification')) { @@ -1105,12 +1085,9 @@ App::delete('/v1/account/sessions') ->setParam('event', 'account.sessions.delete') ->setParam('resource', '/user/'.$user->getId()) ; - + $webhooks - ->setParam('payload', [ - 'name' => $user->getAttribute('name', ''), - 'email' => $user->getAttribute('email', ''), - ]) + ->setParam('payload', $response->output($user, Response::MODEL_USER)) ; if (!Config::getParam('domainVerification')) { diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index c0bac82cb4..5413d49788 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -2,7 +2,6 @@ use Utopia\App; use Utopia\Exception; -use Utopia\Response; use Utopia\Validator\Range; use Utopia\Validator\WhiteList; use Utopia\Validator\Text; @@ -20,6 +19,7 @@ use Appwrite\Database\Validator\Collection; use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Exception\Authorization as AuthorizationException; use Appwrite\Database\Exception\Structure as StructureException; +use Appwrite\Utopia\Response; App::post('/v1/database/collections') ->desc('Create Collection') @@ -77,22 +77,14 @@ App::post('/v1/database/collections') throw new Exception('Failed saving collection to DB', 500); } - $data = $data->getArrayCopy(); - - $webhooks - ->setParam('payload', $data) - ; - $audits ->setParam('event', 'database.collections.create') - ->setParam('resource', 'database/collection/'.$data['$id']) - ->setParam('data', $data) + ->setParam('resource', 'database/collection/'.$data->getId()) + ->setParam('data', $data->getArrayCopy()) ; - $response - ->setStatusCode(Response::STATUS_CODE_CREATED) - ->json($data) - ; + $response->setStatusCode(Response::STATUS_CODE_CREATED); + $response->dynamic($data, Response::MODEL_COLLECTION); }, ['response', 'projectDB', 'webhooks', 'audits']); App::get('/v1/database/collections') @@ -123,7 +115,10 @@ App::get('/v1/database/collections') ], ]); - $response->json(['sum' => $projectDB->getSum(), 'collections' => $results]); + $response->dynamic(new Document([ + 'sum' => $projectDB->getSum(), + 'collections' => $results + ]), Response::MODEL_COLLECTION_LIST); }, ['response', 'projectDB']); App::get('/v1/database/collections/:collectionId') @@ -145,74 +140,9 @@ App::get('/v1/database/collections/:collectionId') throw new Exception('Collection not found', 404); } - $response->json($collection->getArrayCopy()); + $response->dynamic($collection, Response::MODEL_COLLECTION); }, ['response', 'projectDB']); -// App::get('/v1/database/collections/:collectionId/logs') -// ->desc('Get Collection Logs') -// ->groups(['api', 'database']) -// ->label('scope', 'collections.read') -// ->label('sdk.platform', [APP_PLATFORM_SERVER]) -// ->label('sdk.namespace', 'database') -// ->label('sdk.method', 'getCollectionLogs') -// ->label('sdk.description', '/docs/references/database/get-collection-logs.md') -// ->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.') -// ->action( -// function ($collectionId) use ($response, $register, $projectDB, $project) { -// $collection = $projectDB->getDocument($collectionId, false); - -// if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) { -// throw new Exception('Collection not found', 404); -// } - -// $adapter = new AuditAdapter($register->get('db')); -// $adapter->setNamespace('app_'.$project->getId()); - -// $audit = new Audit($adapter); - -// $countries = Locale::getText('countries'); - -// $logs = $audit->getLogsByResource('database/collection/'.$collection->getId()); - -// $reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb'); -// $output = []; - -// foreach ($logs as $i => &$log) { -// $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - -// $dd = new DeviceDetector($log['userAgent']); - -// $dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - -// $dd->parse(); - -// $output[$i] = [ -// 'event' => $log['event'], -// 'ip' => $log['ip'], -// 'time' => strtotime($log['time']), -// 'OS' => $dd->getOs(), -// 'client' => $dd->getClient(), -// 'device' => $dd->getDevice(), -// 'brand' => $dd->getBrand(), -// 'model' => $dd->getModel(), -// 'geo' => [], -// ]; - -// try { -// $record = $reader->country($log['ip']); -// $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode); -// $output[$i]['geo']['country'] = $record->country->name; -// $output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown'); -// } catch (\Exception $e) { -// $output[$i]['geo']['isoCode'] = '--'; -// $output[$i]['geo']['country'] = Locale::getText('locale.country.unknown'); -// } -// } - -// $response->json($output); -// } -// ); - App::put('/v1/database/collections/:collectionId') ->desc('Update Collection') ->groups(['api', 'database']) @@ -274,19 +204,13 @@ App::put('/v1/database/collections/:collectionId') throw new Exception('Failed saving collection to DB', 500); } - $data = $collection->getArrayCopy(); - - $webhooks - ->setParam('payload', $data) - ; - $audits ->setParam('event', 'database.collections.update') - ->setParam('resource', 'database/collections/'.$data['$id']) - ->setParam('data', $data) + ->setParam('resource', 'database/collections/'.$collection->getId()) + ->setParam('data', $collection->getArrayCopy()) ; - $response->json($collection->getArrayCopy()); + $response->dynamic($collection, Response::MODEL_COLLECTION); }, ['response', 'projectDB', 'webhooks', 'audits']); App::delete('/v1/database/collections/:collectionId') @@ -315,16 +239,14 @@ App::delete('/v1/database/collections/:collectionId') throw new Exception('Failed to remove collection from DB', 500); } - $data = $collection->getArrayCopy(); - $webhooks - ->setParam('payload', $data) + ->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION)) ; $audits ->setParam('event', 'database.collections.delete') - ->setParam('resource', 'database/collections/'.$data['$id']) - ->setParam('data', $data) + ->setParam('resource', 'database/collections/'.$collection->getId()) + ->setParam('data', $collection->getArrayCopy()) ; $response->noContent(); @@ -433,22 +355,17 @@ App::post('/v1/database/collections/:collectionId/documents') throw new Exception('Failed saving document to DB'.$exception->getMessage(), 500); } - $data = $data->getArrayCopy(); - - $webhooks - ->setParam('payload', $data) - ; - $audits ->setParam('event', 'database.documents.create') ->setParam('resource', 'database/document/'.$data['$id']) - ->setParam('data', $data) + ->setParam('data', $data->getArrayCopy()) ; $response ->setStatusCode(Response::STATUS_CODE_CREATED) - ->json($data) ; + + $response->dynamic($data, Response::MODEL_ANY); }, ['response', 'projectDB', 'webhooks', 'audits']); App::get('/v1/database/collections/:collectionId/documents') @@ -531,27 +448,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('No document found', 404); } - $output = $document->getArrayCopy(); - - $paths = \explode('/', $request->getParam('q', '')); - $paths = \array_slice($paths, 7, \count($paths)); - - if (\count($paths) > 0) { - if (\count($paths) % 2 == 1) { - $output = $document->getAttribute(\implode('.', $paths)); - } else { - $id = (int) \array_pop($paths); - $output = $document->search('$id', $id, $document->getAttribute(\implode('.', $paths))); - } - - $output = ($output instanceof Document) ? $output->getArrayCopy() : $output; - - if (!\is_array($output)) { - throw new Exception('No document found', 404); - } - } - - $response->json($output); + $response->dynamic($document, Response::MODEL_ANY); }, ['request', 'response', 'projectDB']); App::patch('/v1/database/collections/:collectionId/documents/:documentId') @@ -620,19 +517,13 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('Failed saving document to DB', 500); } - $data = $data->getArrayCopy(); - - $webhooks - ->setParam('payload', $data) - ; - $audits ->setParam('event', 'database.documents.update') - ->setParam('resource', 'database/document/'.$data['$id']) - ->setParam('data', $data) + ->setParam('resource', 'database/document/'.$data->getId()) + ->setParam('data', $data->getArrayCopy()) ; - $response->json($data); + $response->dynamic($data, Response::MODEL_ANY); }, ['response', 'projectDB', 'webhooks', 'audits']); App::delete('/v1/database/collections/:collectionId/documents/:documentId') @@ -673,16 +564,14 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('Failed to remove document from DB', 500); } - $data = $document->getArrayCopy(); - $webhooks - ->setParam('payload', $data) + ->setParam('payload', $response->output($document, Response::MODEL_ANY)) ; - + $audits ->setParam('event', 'database.documents.delete') - ->setParam('resource', 'database/document/'.$data['$id']) - ->setParam('data', $data) // Audit document in case of malicious or disastrous action + ->setParam('resource', 'database/document/'.$document->getId()) + ->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action ; $response->noContent(); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 1fd15c615c..710264b852 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -141,10 +141,6 @@ App::post('/v1/storage/files') throw new Exception('Failed saving file to DB', 500); } - $webhooks - ->setParam('payload', $file->getArrayCopy()) - ; - $audits ->setParam('event', 'storage.files.create') ->setParam('resource', 'storage/files/'.$file->getId()) @@ -504,10 +500,6 @@ App::put('/v1/storage/files/:fileId') throw new Exception('Failed saving file to DB', 500); } - $webhooks - ->setParam('payload', $file->getArrayCopy()) - ; - $audits ->setParam('event', 'storage.files.update') ->setParam('resource', 'storage/files/'.$file->getId()) @@ -546,11 +538,7 @@ App::delete('/v1/storage/files/:fileId') throw new Exception('Failed to remove file from DB', 500); } } - - $webhooks - ->setParam('payload', $file->getArrayCopy()) - ; - + $audits ->setParam('event', 'storage.files.delete') ->setParam('resource', 'storage/files/'.$file->getId()) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2c3df992f9..9db6c0f2f1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -274,11 +274,18 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi /** @var bool $mode */ if (!empty($functions->getParam('event'))) { - $functions->setParam('payload', $webhooks->getParam('payload')); + if(empty($functions->getParam('payload'))) { + $functions->setParam('payload', $response->getPayload()); + } + $functions->trigger(); } if (!empty($webhooks->getParam('event'))) { + if(empty($webhooks->getParam('payload'))) { + $webhooks->setParam('payload', $response->getPayload()); + } + $webhooks->trigger(); } diff --git a/app/views/console/webhooks/index.phtml b/app/views/console/webhooks/index.phtml index 99411f5767..47c7d30f01 100644 --- a/app/views/console/webhooks/index.phtml +++ b/app/views/console/webhooks/index.phtml @@ -136,7 +136,7 @@ $events = array_keys($this->getParam('events', [])); -   ( events) +   ( events)   (SSL/TLS Disabled) diff --git a/docker-compose.yml b/docker-compose.yml index c5a090479a..dd44ea52c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -134,6 +134,7 @@ services: depends_on: - redis - mariadb + - request-catcher environment: - _APP_ENV - _APP_REDIS_HOST @@ -292,7 +293,7 @@ services: - MYSQL_PASSWORD=password command: 'mysqld --innodb-flush-method=fsync' # add ' --query_cache_size=0' for DB tests - maildev: + maildev: # used mainly for dev tests image: djfarrelly/maildev container_name: appwrite-maildev restart: unless-stopped @@ -301,6 +302,15 @@ services: networks: - appwrite + request-catcher: # used mainly for dev tests + image: smarterdm/http-request-catcher + container_name: appwrite-request-catcher + restart: unless-stopped + ports: + - '5000:5000' + networks: + - appwrite + # smtp: # image: appwrite/smtp:1.0.1 # container_name: appwrite-smtp diff --git a/src/Appwrite/Database/Document.php b/src/Appwrite/Database/Document.php index 2c0ad4131d..575b86bff7 100644 --- a/src/Appwrite/Database/Document.php +++ b/src/Appwrite/Database/Document.php @@ -21,7 +21,7 @@ class Document extends ArrayObject * @param int $flags * @param string $iterator_class */ - public function __construct($input = null, $flags = 0, $iterator_class = 'ArrayIterator') + public function __construct($input = [], $flags = 0, $iterator_class = 'ArrayIterator') { foreach ($input as $key => &$value) { if (\is_array($value)) { diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3795778261..70b57c361f 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -7,6 +7,7 @@ use Utopia\Swoole\Response as SwooleResponse; use Swoole\Http\Response as SwooleHTTPResponse; use Appwrite\Database\Document; use Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\Continent; @@ -36,6 +37,7 @@ use Appwrite\Utopia\Response\Model\Webhook; class Response extends SwooleResponse { // General + const MODEL_ANY = 'any'; const MODEL_LOG = 'log'; const MODEL_LOG_LIST = 'logList'; const MODEL_ERROR = 'error'; @@ -101,6 +103,11 @@ class Response extends SwooleResponse const MODEL_DOMAIN = 'domain'; const MODEL_DOMAIN_LIST = 'domainList'; + /** + * @var array + */ + protected $payload = []; + /** * Response constructor. */ @@ -111,7 +118,7 @@ class Response extends SwooleResponse ->setModel(new Error()) ->setModel(new ErrorDev()) // Lists - ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'users', self::MODEL_COLLECTION)) + ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG, false)) @@ -133,6 +140,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) // Entities + ->setModel(new Any()) ->setModel(new Collection()) ->setModel(new Rule()) ->setModel(new Log()) @@ -210,12 +218,16 @@ class Response extends SwooleResponse /** * Generate valid response object from document data */ - protected function output(Document $document, string $model): array + public function output(Document $document, string $model): array { $data = $document; $model = $this->getModel($model); $output = []; + if($model->isAny()) { + return $document->getArrayCopy(); + } + foreach($model->getRules() as $key => $rule) { if(!$document->isSet($key)) { if(!is_null($rule['default'])) { @@ -245,6 +257,8 @@ class Response extends SwooleResponse $output[$key] = $data[$key]; } + $this->payload = $output; + return $output; } @@ -269,4 +283,12 @@ class Response extends SwooleResponse ->send(yaml_emit($data, YAML_UTF8_ENCODING)) ; } + + /** + * @return array + */ + public function getPayload():array + { + return $this->payload; + } } diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index b50224935b..0a113aa7b2 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -4,6 +4,14 @@ namespace Appwrite\Utopia\Response; abstract class Model { + /** + * @var bool + */ + protected $any = false; + + /** + * @var array + */ protected $rules = []; /** @@ -45,4 +53,9 @@ abstract class Model return $this; } + + public function isAny(): bool + { + return $this->any; + } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/Any.php b/src/Appwrite/Utopia/Response/Model/Any.php new file mode 100644 index 0000000000..1a5defec66 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Any.php @@ -0,0 +1,34 @@ + 'Rule ID.', 'example' => '5e5ea5c16897e', ]) + ->addRule('$collection', [ // TODO remove this from public response + 'type' => 'string', + 'description' => 'Rule Collection.', + 'example' => '5e5e66c16897e', + ]) ->addRule('type', [ 'type' => 'string', 'description' => 'Rule type. Possible values: ', @@ -34,6 +39,7 @@ class Rule extends Model 'type' => 'string', 'description' => 'Rule default value.', 'example' => 'Movie Name', + 'default' => '', ]) ->addRule('array', [ 'type' => 'boolean', @@ -45,6 +51,13 @@ class Rule extends Model 'description' => 'Is required?', 'example' => true, ]) + ->addRule('list', [ + 'type' => 'string', + 'description' => 'List of allowed values', + 'array' => true, + 'default' => [], + 'example' => ['5e5ea5c168099'], + ]) ; } diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index 61920877b7..07e9ec67e5 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -34,6 +34,7 @@ abstract class Scope extends TestCase protected function getLastEmail():array { sleep(10); + $emails = json_decode(file_get_contents('http://maildev/email'), true); if($emails && is_array($emails)) { @@ -43,6 +44,16 @@ abstract class Scope extends TestCase return []; } + protected function getLastRequest():array + { + sleep(10); + + $resquest = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true); + $resquest['data'] = json_decode($resquest['data'], true); + + return $resquest; + } + /** * @return array */ diff --git a/tests/e2e/Services/Workers/WebhooksTest.php b/tests/e2e/Services/Workers/WebhooksTest.php new file mode 100644 index 0000000000..67c51785cd --- /dev/null +++ b/tests/e2e/Services/Workers/WebhooksTest.php @@ -0,0 +1,152 @@ +client->call(Client::METHOD_POST, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Project Test', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + $this->assertEquals('Project Test', $team['body']['name']); + $this->assertNotEmpty($team['body']['$id']); + + $response = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Project Test', + 'teamId' => $team['body']['$id'], + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Project Test', $response['body']['name']); + $this->assertEquals($team['body']['$id'], $response['body']['teamId']); + $this->assertArrayHasKey('platforms', $response['body']); + $this->assertArrayHasKey('webhooks', $response['body']); + $this->assertArrayHasKey('keys', $response['body']); + $this->assertArrayHasKey('tasks', $response['body']); + + $projectId = $response['body']['$id']; + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => '', + 'teamId' => $team['body']['$id'], + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Project Test', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + return ['projectId' => $projectId]; + } + + /** + * @depends testCreateProject + */ + public function testCreateWebhook($data): array + { + $id = (isset($data['projectId'])) ? $data['projectId'] : ''; + + $response = $this->client->call(Client::METHOD_POST, '/projects/'.$id.'/webhooks', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Webhook Worker Test', + 'events' => ['account.create', 'account.update.email'], + 'url' => 'http://request-catcher:5000/webhook', + 'security' => true, + 'httpUser' => 'username', + 'httpPass' => 'password', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertContains('account.create', $response['body']['events']); + $this->assertContains('account.update.email', $response['body']['events']); + $this->assertCount(2, $response['body']['events']); + $this->assertEquals('http://request-catcher:5000/webhook', $response['body']['url']); + $this->assertIsBool($response['body']['security']); + $this->assertEquals(true, $response['body']['security']); + $this->assertEquals('username', $response['body']['httpUser']); + + $data = array_merge($data, ['webhookId' => $response['body']['$id']]); + + /** + * Test for FAILURE + */ + + return $data; + } + + /** + * @depends testCreateWebhook + */ + public function testCreateAccount($data) + { + $projectId = (isset($data['projectId'])) ? $data['projectId'] : ''; + $email = uniqid().'webhook.user@localhost.test'; + $password = 'password'; + $name = 'User Name'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + ]), [ + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + + $this->assertEquals($response['headers']['status-code'], 201); + + $webhook = $this->getLastRequest(); + + $this->assertNotEmpty($webhook['data']); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertIsNumeric($webhook['data']['status']); + $this->assertIsNumeric($webhook['data']['registration']); + $this->assertEquals($webhook['data']['email'], $email); + $this->assertEquals($webhook['data']['name'], $name); + $this->assertIsBool($webhook['data']['emailVerification']); + $this->assertIsArray($webhook['data']['prefs']); + $this->assertIsArray($webhook['data']['roles']); + } +} \ No newline at end of file