1
0
Fork 0
mirror of synced 2024-09-29 08:51:28 +13:00

Consolidate List User Logs API params

Replace limit and offset params with a single queries param since the
Queries V2 change now allows for different types of queries.
This commit is contained in:
Steven 2022-08-19 00:09:34 +00:00
parent f0d66985f7
commit 27f69e3c09
6 changed files with 244 additions and 25 deletions

View file

@ -10,7 +10,9 @@ use Appwrite\Event\Audit as EventAudit;
use Appwrite\Network\Validator\Email;
use Appwrite\Stats\Stats;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Users;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetQuery;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Audit\Audit;
@ -30,7 +32,6 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Validator\Assoc;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\Range;
use Utopia\Validator\Boolean;
use MaxMind\Db\Reader;
@ -306,14 +307,13 @@ App::get('/v1/users/:userId/logs')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('userId', '', new UID(), 'User ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('queries', [], new Queries(new LimitOffsetQuery(), strict: false), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('usage')
->action(function (string $userId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Stats $usage) {
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Stats $usage) {
$user = $dbForProject->getDocument('users', $userId);
@ -321,6 +321,11 @@ App::get('/v1/users/:userId/logs')
throw new Exception(Exception::USER_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$logs = $audit->getLogsByUser($user->getId(), $limit, $offset);

View file

@ -51,7 +51,7 @@
"utopia-php/cache": "0.6.*",
"utopia-php/cli": "0.13.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.22.*",
"utopia-php/database": "0.23.0 as 0.22.0",
"utopia-php/locale": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/preloader": "0.2.*",
@ -88,6 +88,9 @@
"ext-phpiredis": "*"
},
"config": {
"optimize-autoloader": true,
"allow-plugins": false,
"preferred-install": "dist",
"platform": {
"php": "8.0"
}

20
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "175fe4abafd8bde4053b91eea905c328",
"content-hash": "1858c544a5b5ae59bc6ff146a375a683",
"packages": [
{
"name": "adhocore/jwt",
@ -2052,16 +2052,16 @@
},
{
"name": "utopia-php/database",
"version": "0.22.0",
"version": "0.23.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "22c45ae83612e907203b7571cd8e3115ae3ae4c5"
"reference": "e4a23f8e26a3d96f292e549cebe0ff6937ec5d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/22c45ae83612e907203b7571cd8e3115ae3ae4c5",
"reference": "22c45ae83612e907203b7571cd8e3115ae3ae4c5",
"url": "https://api.github.com/repos/utopia-php/database/zipball/e4a23f8e26a3d96f292e549cebe0ff6937ec5d71",
"reference": "e4a23f8e26a3d96f292e549cebe0ff6937ec5d71",
"shasum": ""
},
"require": {
@ -2110,9 +2110,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.22.0"
"source": "https://github.com/utopia-php/database/tree/0.23.0"
},
"time": "2022-08-17T12:55:37+00:00"
"time": "2022-08-18T19:08:33+00:00"
},
{
"name": "utopia-php/domains",
@ -5354,6 +5354,12 @@
"version": "9999999-dev",
"alias": "0.19.5",
"alias_normalized": "0.19.5.0"
},
{
"package": "utopia-php/database",
"version": "0.23.0.0",
"alias": "0.22.0",
"alias_normalized": "0.22.0.0"
}
],
"minimum-stability": "stable",

View file

@ -0,0 +1,58 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query as QueryValidator;
class LimitOffsetQuery extends QueryValidator
{
/**
* @var string
*/
protected $message = 'Invalid query';
protected int $maxLimit;
protected int $maxOffset;
/**
* Query constructor
*
* @param int $maxLimit
* @param int $maxOffset
* @param int $maxValuesCount
*/
public function __construct(int $maxLimit = 100, int $maxOffset = 5000)
{
$this->maxLimit = $maxLimit;
$this->maxOffset = $maxOffset;
}
/**
* Is valid.
*
* Returns true if method is limit or offset and values are within range.
*
* @param Query $value
*
* @return bool
*/
public function isValid($query): bool
{
// Validate method
$method = $query->getMethod();
switch ($method) {
case Query::TYPE_LIMIT:
$limit = $query->getValue();
return $this->isValidLimit($limit);
case Query::TYPE_OFFSET:
$offset = $query->getValue();
return $this->isValidOffset($offset);
default:
$this->message = 'Query method invalid: ' . $method;
return false;
}
}
}

View file

@ -3,7 +3,6 @@
namespace Tests\E2E\Services\Users;
use Tests\E2E\Client;
use Utopia\Database\Database;
use Utopia\Database\ID;
trait UsersBase
@ -673,26 +672,44 @@ trait UsersBase
/**
* Test for SUCCESS
*/
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$i = 0;
do {
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$i++;
} while ($logs['body']['total'] === 0 && $i < 1000);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$this->assertCount(1, $logs['body']['logs']);
$this->assertEquals(1, $logs['body']['total']);
$this->assertIsArray($logs['body']['logs'][0]);
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1
'queries' => ['limit(1)']
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
$this->assertCount(1, $logs['body']['logs']);
$this->assertEquals(1, $logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(0)']
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertCount(0, $logs['body']['logs']);
$this->assertEquals(1, $logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
@ -703,7 +720,8 @@ trait UsersBase
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$this->assertCount(0, $logs['body']['logs']);
$this->assertEquals(1, $logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
@ -717,8 +735,74 @@ trait UsersBase
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
$this->assertCount(0, $logs['body']['logs']);
$this->assertEquals(1, $logs['body']['total']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(101)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(5001)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
}
/**

View file

@ -0,0 +1,63 @@
<?php
namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetQuery;
use Utopia\Database\Query;
use PHPUnit\Framework\TestCase;
class LimitOffsetQueryTest extends TestCase
{
/**
* @var Key
*/
protected $validator = null;
public function setUp(): void
{
$this->validator = new LimitOffsetQuery();
}
public function tearDown(): void
{
}
public function testValue(): void
{
// Test for Success
$this->assertEquals($this->validator->isValid(Query::limit(1)), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::limit(0)), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::limit(100)), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::offset(1)), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::offset(0)), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::offset(5000)), true, $this->validator->getDescription());
// Test for Failure
$this->assertEquals($this->validator->isValid(Query::limit(-1)), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::limit(101)), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::offset(-1)), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::offset(5001)), false, $this->validator->getDescription());
}
public function testValues(): void
{
$validator = new Queries($this->validator, strict: false);
// Test for Success
$this->assertEquals($validator->isValid(['limit(1)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['limit(0)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['limit(100)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['offset(1)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['offset(0)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['offset(5000)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['limit(25)', 'offset(25)']), true, $validator->getDescription());
// Test for Failure
$this->assertEquals($validator->isValid(['limit(-1)']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['limit(101)']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['offset(-1)']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['offset(5001)']), false, $validator->getDescription());
}
}