From 9fe9e3f795dd138ec51b07e9832a196634a316dd Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 8 Sep 2020 23:07:04 +0300 Subject: [PATCH 01/17] Fix for document default value --- src/Appwrite/Database/Document.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)) { From ddfb733d259b858635087cbafb80b4c84b4b93f0 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 8 Sep 2020 23:07:20 +0300 Subject: [PATCH 02/17] Added request catcher --- docker-compose.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 From 14fac182ea088863a4cf64783d771de51734fa1d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 8 Sep 2020 23:07:38 +0300 Subject: [PATCH 03/17] Fixed account warning --- app/controllers/api/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 9dcd2109ef..c48557edc1 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 */ From 9bd3e4dcbdc9d7c377b1d1ff8f9ecca506e884cb Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 8 Sep 2020 23:08:02 +0300 Subject: [PATCH 04/17] Save last payload in memory for request lifetime --- src/Appwrite/Utopia/Response.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index c362df0251..ec5e24497c 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -97,6 +97,11 @@ class Response extends SwooleResponse const MODEL_DOMAIN = 'domain'; const MODEL_DOMAIN_LIST = 'domainList'; + /** + * @var array + */ + protected $payload = []; + /** * Response constructor. */ @@ -240,6 +245,8 @@ class Response extends SwooleResponse $output[$key] = $data[$key]; } + $this->payload = $output; + return $output; } @@ -264,4 +271,12 @@ class Response extends SwooleResponse ->send(yaml_emit($data, YAML_UTF8_ENCODING)) ; } + + /** + * @return array + */ + public function getPayload():array + { + return $this->payload; + } } From fd93c610099556768c033ebdfe7e9d555d3b3737 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 8 Sep 2020 23:08:55 +0300 Subject: [PATCH 05/17] Added response model payload as webhook payload --- app/controllers/general.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2c3df992f9..d4c0c91412 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -274,11 +274,12 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi /** @var bool $mode */ if (!empty($functions->getParam('event'))) { - $functions->setParam('payload', $webhooks->getParam('payload')); + $functions->setParam('payload', $response->getPayload()); $functions->trigger(); } if (!empty($webhooks->getParam('event'))) { + $webhooks->setParam('payload', $response->getPayload()); $webhooks->trigger(); } From 80838cc46595ef93e1a7b25635c1e6532ab70a85 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 8 Sep 2020 23:09:09 +0300 Subject: [PATCH 06/17] Added tests --- tests/e2e/Scopes/Scope.php | 11 ++ tests/e2e/Services/Workers/WebhooksTest.php | 152 ++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 tests/e2e/Services/Workers/WebhooksTest.php 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 From 88e7d8535b0071801a9fd0f2386f6bb35c040e62 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 9 Sep 2020 12:26:55 +0300 Subject: [PATCH 07/17] Removed old payloads --- app/controllers/api/storage.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) 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()) From 6c8e4ea339ef1904dfbffdd56dab031bece1134d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 9 Sep 2020 12:27:08 +0300 Subject: [PATCH 08/17] Added payloads for no-content requests --- app/controllers/api/account.php | 33 +++++--------------------------- src/Appwrite/Utopia/Response.php | 2 +- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index c48557edc1..5a86298aae 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -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/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index ec5e24497c..5f282d5877 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -210,7 +210,7 @@ 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); From 6b871f4adfad739d54c6dc58a96c2ef496cd666f Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 9 Sep 2020 12:53:57 +0300 Subject: [PATCH 09/17] Added Any response model --- app/controllers/api/database.php | 122 ++------------------- src/Appwrite/Utopia/Response.php | 9 +- src/Appwrite/Utopia/Response/Model.php | 13 +++ src/Appwrite/Utopia/Response/Model/Any.php | 34 ++++++ 4 files changed, 64 insertions(+), 114 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/Any.php diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index c0bac82cb4..e4aad46d03 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') @@ -148,71 +148,6 @@ App::get('/v1/database/collections/:collectionId') $response->json($collection->getArrayCopy()); }, ['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']) @@ -433,22 +368,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 +461,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 +530,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 +577,10 @@ 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) - ; - $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/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 5f282d5877..e38fb6f188 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\Continent; use Appwrite\Utopia\Response\Model\Country; @@ -34,6 +35,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'; @@ -133,6 +135,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 Log()) ->setModel(new User()) ->setModel(new Session()) @@ -153,8 +156,6 @@ class Response extends SwooleResponse ->setModel(new Language()) ->setModel(new Currency()) ->setModel(new Phone()) - // Currency - // Phone // Verification // Recovery ; @@ -216,6 +217,10 @@ class Response extends SwooleResponse $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'])) { 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 @@ + Date: Thu, 10 Sep 2020 09:51:49 +0300 Subject: [PATCH 10/17] Fixed rule default value --- src/Appwrite/Utopia/Response/Model/Rule.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Utopia/Response/Model/Rule.php b/src/Appwrite/Utopia/Response/Model/Rule.php index d170348353..10c7dd5cc9 100644 --- a/src/Appwrite/Utopia/Response/Model/Rule.php +++ b/src/Appwrite/Utopia/Response/Model/Rule.php @@ -34,6 +34,7 @@ class Rule extends Model 'type' => 'string', 'description' => 'Rule default value.', 'example' => 'Movie Name', + 'default' => '', ]) ->addRule('array', [ 'type' => 'boolean', From f08cf1286d350e0371fbc125a8dfd4babdbaa208 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 09:52:14 +0300 Subject: [PATCH 11/17] Added database response models --- app/controllers/api/database.php | 43 +++++++++++--------------------- src/Appwrite/Utopia/Response.php | 2 +- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index e4aad46d03..5b2e435fb2 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -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,7 +140,7 @@ 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::put('/v1/database/collections/:collectionId') @@ -209,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') @@ -250,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', $collection->getArrayCopy()) ; $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(); diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3c8789cdf9..70b57c361f 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -118,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)) From 628097a1d84db3e281dde3e619eddca96f10bb94 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 09:52:32 +0300 Subject: [PATCH 12/17] Added option to overwrite webhook payload --- app/controllers/general.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index d4c0c91412..9db6c0f2f1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -274,12 +274,18 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi /** @var bool $mode */ if (!empty($functions->getParam('event'))) { - $functions->setParam('payload', $response->getPayload()); + if(empty($functions->getParam('payload'))) { + $functions->setParam('payload', $response->getPayload()); + } + $functions->trigger(); } if (!empty($webhooks->getParam('event'))) { - $webhooks->setParam('payload', $response->getPayload()); + if(empty($webhooks->getParam('payload'))) { + $webhooks->setParam('payload', $response->getPayload()); + } + $webhooks->trigger(); } From 57bb13b99f41564c767c8f3af9c78b1f401daf3b Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 10:32:58 +0300 Subject: [PATCH 13/17] Fixed events count --- app/views/console/webhooks/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From d97943bb2485106ff73b1337f219d24bc628c48b Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 10:33:20 +0300 Subject: [PATCH 14/17] Added missing rules --- src/Appwrite/Utopia/Response/Model/Rule.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/Rule.php b/src/Appwrite/Utopia/Response/Model/Rule.php index 10c7dd5cc9..35db336799 100644 --- a/src/Appwrite/Utopia/Response/Model/Rule.php +++ b/src/Appwrite/Utopia/Response/Model/Rule.php @@ -15,6 +15,11 @@ class Rule extends Model 'description' => '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: ', @@ -46,6 +51,12 @@ class Rule extends Model 'description' => 'Is required?', 'example' => true, ]) + ->addRule('list', [ + 'type' => 'string', + 'description' => 'List of allowed values', + 'array' => true, + 'example' => ['5e5ea5c168099'], + ]) ; } From 08f69d69d581d9ce938a2f9802a9af8c72a1004d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 10:40:01 +0300 Subject: [PATCH 15/17] Added payload manually for no-content routes --- app/controllers/api/database.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 5b2e435fb2..5413d49788 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -240,7 +240,7 @@ App::delete('/v1/database/collections/:collectionId') } $webhooks - ->setParam('payload', $collection->getArrayCopy()) + ->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION)) ; $audits @@ -564,6 +564,10 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('Failed to remove document from DB', 500); } + $webhooks + ->setParam('payload', $response->output($document, Response::MODEL_ANY)) + ; + $audits ->setParam('event', 'database.documents.delete') ->setParam('resource', 'database/document/'.$document->getId()) From 46a0906e2cfe414d01fd2c0749d5f319ed4a6cd0 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 10:41:22 +0300 Subject: [PATCH 16/17] Updated changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) 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) From 4129e9befb27010edd22b0780ee0721c7da5f9b2 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 10 Sep 2020 10:45:34 +0300 Subject: [PATCH 17/17] Fixed rule default list value --- src/Appwrite/Utopia/Response/Model/Rule.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Utopia/Response/Model/Rule.php b/src/Appwrite/Utopia/Response/Model/Rule.php index 35db336799..234377c432 100644 --- a/src/Appwrite/Utopia/Response/Model/Rule.php +++ b/src/Appwrite/Utopia/Response/Model/Rule.php @@ -55,6 +55,7 @@ class Rule extends Model 'type' => 'string', 'description' => 'List of allowed values', 'array' => true, + 'default' => [], 'example' => ['5e5ea5c168099'], ]) ;