diff --git a/app/config/collections2.php b/app/config/collections2.php index d8f10b1233..878f6b622c 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -1711,6 +1711,17 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -1720,6 +1731,13 @@ $collections = [ 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => '_fulltext_search', + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [16384], + 'orders' => [Database::ORDER_ASC], + ], ], ], diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 28ff911c10..00ed181239 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -680,8 +680,10 @@ App::post('/v1/functions/:functionId/executions') Authorization::disable(); + $executionId = $dbForInternal->getId(); + $execution = $dbForInternal->createDocument('executions', new Document([ - '$id' => $dbForInternal->getId(), + '$id' => $executionId, '$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [], '$write' => [], 'dateCreated' => time(), @@ -693,6 +695,7 @@ App::post('/v1/functions/:functionId/executions') 'stdout' => '', 'stderr' => '', 'time' => 0.0, + 'search' => implode(' ', [$functionId, $executionId]), ])); Authorization::reset(); @@ -747,10 +750,11 @@ App::get('/v1/functions/:functionId/executions') ->param('functionId', '', new UID(), 'Function unique ID.') ->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true) ->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->param('after', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true) ->inject('response') ->inject('dbForInternal') - ->action(function ($functionId, $limit, $offset, $after, $response, $dbForInternal) { + ->action(function ($functionId, $limit, $offset, $search, $after, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ @@ -770,13 +774,17 @@ App::get('/v1/functions/:functionId/executions') } } - $results = $dbForInternal->find('executions', [ - new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), - ], $limit, $offset, [], [Database::ORDER_DESC], $afterExecution ?? null); + $queries = [ + new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]) + ]; - $sum = $dbForInternal->count('executions', [ - new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), - ], APP_LIMIT_COUNT); + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + + $results = $dbForInternal->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $afterExecution ?? null); + + $sum = $dbForInternal->count('executions', $queries, APP_LIMIT_COUNT); $response->dynamic(new Document([ 'executions' => $results, diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index c2c7e29873..7adaa747c2 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -108,10 +108,10 @@ App::get('/v1/users') $afterUser = $dbForInternal->getDocument('users', $after); if ($afterUser->isEmpty()) { - throw new Exception('User for after not found', 400); + throw new Exception("User '{$after}' for the 'after' value not found.", 400); } } - + $queries = []; if (!empty($search)) { @@ -123,8 +123,8 @@ App::get('/v1/users') ; $response->dynamic(new Document([ - 'users' => $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $afterUser ?? null), - 'sum' => $dbForInternal->count('users', [], APP_LIMIT_COUNT), + 'users' => $dbForInternal->find('users', $queries, $limit, $offset, [], [$orderType], $afterUser ?? null), + 'sum' => $dbForInternal->count('users', $queries, APP_LIMIT_COUNT), ]), Response::MODEL_USER_LIST); }); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index cb820b45c3..57b8ad9218 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -78,6 +78,45 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ + /** + * Test search queries + */ + $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['functionId'] + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertCount(1, $response['body']['functions']); + $this->assertEquals($response['body']['functions'][0]['name'], 'Test'); + + $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Test' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertCount(1, $response['body']['functions']); + $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); + + $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'php-8.0' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertCount(1, $response['body']['functions']); + $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); + + /** + * Test pagination + */ $response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -122,7 +161,6 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $response['body']['functions']); $this->assertEquals($response['body']['functions'][0]['name'], 'Test 2'); - return $data; } @@ -283,6 +321,48 @@ class FunctionsCustomServerTest extends Scope $this->assertIsArray($function['body']['tags']); $this->assertCount(1, $function['body']['tags']); + /** + * Test search queries + */ + $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => $data['functionId'] + ])); + + $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals($function['body']['sum'], 1); + $this->assertIsArray($function['body']['tags']); + $this->assertCount(1, $function['body']['tags']); + $this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']); + + $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => 'Test' + ])); + + $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals($function['body']['sum'], 1); + $this->assertIsArray($function['body']['tags']); + $this->assertCount(1, $function['body']['tags']); + $this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']); + + $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => 'php-8.0' + ])); + + $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals($function['body']['sum'], 1); + $this->assertIsArray($function['body']['tags']); + $this->assertCount(1, $function['body']['tags']); + $this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']); + return $data; } @@ -394,6 +474,36 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $function['body']['executions']); $this->assertEquals($function['body']['executions'][0]['$id'], $data['executionId']); + /** + * Test search queries + */ + + $response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['executionId'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['executions']); + $this->assertEquals($data['functionId'], $response['body']['executions'][0]['functionId']); + + $response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['functionId'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['executions']); + $this->assertEquals($data['executionId'], $response['body']['executions'][0]['$id']); + return $data; } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 1842891492..56daab688d 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -87,6 +87,7 @@ class ProjectsConsoleClientTest extends Scope /** * Test for SUCCESS */ + $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -97,6 +98,35 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals($id, $response['body']['projects'][0]['$id']); $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); + /** + * Test search queries + */ + $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => $id + ])); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['sum'], 1); + $this->assertIsArray($response['body']['projects']); + $this->assertCount(1, $response['body']['projects']); + $this->assertEquals($response['body']['projects'][0]['name'], 'Project Test'); + + $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => 'Project Test' + ])); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['sum'], 1); + $this->assertIsArray($response['body']['projects']); + $this->assertCount(1, $response['body']['projects']); + $this->assertEquals($response['body']['projects'][0]['$id'], $data['projectId']); + /** * Test after pagination */ diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 57a5ee6972..ab1a08c682 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -163,7 +163,7 @@ trait StorageBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'fileId' => 'unique()', - 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'), + 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/file.png'), 'image/png', 'file.png'), 'read' => ['role:all'], 'write' => ['role:all'], ]); @@ -171,9 +171,9 @@ trait StorageBase $this->assertEquals($file['headers']['status-code'], 201); $this->assertNotEmpty($file['body']['$id']); $this->assertIsInt($file['body']['dateCreated']); - $this->assertEquals('logo.png', $file['body']['name']); - $this->assertEquals('image/png', $file['body']['mimeType']); - $this->assertEquals(47218, $file['body']['sizeOriginal']); + $this->assertEquals('file.png', $file['body']['name']); + $this->assertEquals('image/jpeg', $file['body']['mimeType']); + $this->assertEquals(16804, $file['body']['sizeOriginal']); $files = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ 'content-type' => 'application/json', @@ -197,6 +197,32 @@ trait StorageBase $this->assertEquals($files['body']['files'][1]['$id'], $response['body']['files'][0]['$id']); $this->assertCount(1, $response['body']['files']); + $response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['fileId'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['files']); + $this->assertEquals('logo.png', $response['body']['files'][0]['name']); + + $response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'logo', + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['files']); + $this->assertEquals($data['fileId'], $response['body']['files'][0]['$id']); + /** * Test for FAILURE */ diff --git a/tests/e2e/Services/Teams/TeamsBase.php b/tests/e2e/Services/Teams/TeamsBase.php index 6c1ebbe36a..b594bbfa67 100644 --- a/tests/e2e/Services/Teams/TeamsBase.php +++ b/tests/e2e/Services/Teams/TeamsBase.php @@ -172,6 +172,19 @@ trait TeamsBase $this->assertCount(1, $response['body']['teams']); $this->assertEquals('Manchester United', $response['body']['teams'][0]['name']); + $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['teamUid'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertGreaterThan(0, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['teams']); + $this->assertEquals('Arsenal', $response['body']['teams'][0]['name']); + $teams = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 90b59a8c16..ee35e66fc3 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -16,14 +16,14 @@ trait UsersBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'unique()', - 'email' => 'users.service@example.com', + 'email' => 'cristiano.ronaldo@manchester-united.co.uk', 'password' => 'password', - 'name' => 'Project User', + 'name' => 'Cristiano Ronaldo', ]); $this->assertEquals($user['headers']['status-code'], 201); - $this->assertEquals($user['body']['name'], 'Project User'); - $this->assertEquals($user['body']['email'], 'users.service@example.com'); + $this->assertEquals($user['body']['name'], 'Cristiano Ronaldo'); + $this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk'); $this->assertEquals($user['body']['status'], true); $this->assertGreaterThan(0, $user['body']['registration']); @@ -35,15 +35,15 @@ trait UsersBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'user1', - 'email' => 'users.service1@example.com', + 'email' => 'lionel.messi@psg.fr', 'password' => 'password', - 'name' => 'Project User', + 'name' => 'Lionel Messi', ]); $this->assertEquals($res['headers']['status-code'], 201); $this->assertEquals($res['body']['$id'], 'user1'); - $this->assertEquals($res['body']['name'], 'Project User'); - $this->assertEquals($res['body']['email'], 'users.service1@example.com'); + $this->assertEquals($res['body']['name'], 'Lionel Messi'); + $this->assertEquals($res['body']['email'], 'lionel.messi@psg.fr'); $this->assertEquals(true, $res['body']['status']); $this->assertGreaterThan(0, $res['body']['registration']); @@ -56,7 +56,7 @@ trait UsersBase public function testListUsers(array $data): void { /** - * Test for SUCCESS + * Test for SUCCESS listUsers */ $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ 'content-type' => 'application/json', @@ -82,8 +82,72 @@ trait UsersBase $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); $this->assertCount(1, $response['body']['users']); - $this->assertEquals($response['body']['users'][0]['$id'], 'user1'); + + /** + * Test for SUCCESS searchUsers + */ + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Ronaldo' + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'cristiano.ronaldo' + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'manchester' + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'manchester-united.co.uk' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertIsArray($response['body']['users']); + $this->assertIsInt($response['body']['sum']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertCount(1, $response['body']['users']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['userId'] + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); } /** @@ -100,8 +164,8 @@ trait UsersBase ], $this->getHeaders())); $this->assertEquals($user['headers']['status-code'], 200); - $this->assertEquals($user['body']['name'], 'Project User'); - $this->assertEquals($user['body']['email'], 'users.service@example.com'); + $this->assertEquals($user['body']['name'], 'Cristiano Ronaldo'); + $this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk'); $this->assertEquals($user['body']['status'], true); $this->assertGreaterThan(0, $user['body']['registration']); @@ -132,21 +196,6 @@ trait UsersBase $this->assertIsInt($users['body']['sum']); $this->assertGreaterThan(0, $users['body']['sum']); - $users = $this->client->call(Client::METHOD_GET, '/users', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'search' => 'example.com' - ]); - - $this->assertEquals($users['headers']['status-code'], 200); - $this->assertIsArray($users['body']); - $this->assertIsArray($users['body']['users']); - $this->assertIsInt($users['body']['sum']); - $this->assertEquals(2, $users['body']['sum']); - $this->assertGreaterThan(0, $users['body']['sum']); - $this->assertCount(2, $users['body']['users']); - return $data; }