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

Add additional Validators for Queries

This commit is contained in:
Steven 2022-08-19 23:20:22 +00:00
parent a5bc07c9e4
commit 3890dea35b
14 changed files with 846 additions and 32 deletions

View file

@ -574,7 +574,7 @@ 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('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)
->param('queries', [], new Queries(new LimitOffsetQuery()), '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')

View file

@ -88,9 +88,6 @@
"ext-phpiredis": "*"
},
"config": {
"optimize-autoloader": true,
"allow-plugins": false,
"preferred-install": "dist",
"platform": {
"php": "8.0"
}

10
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": "5d5b72c7940a8376e187cce1b33d327a",
"content-hash": "0e850206a924d2a48861ecd290f59bc0",
"packages": [
{
"name": "adhocore/jwt",
@ -5354,12 +5354,6 @@
"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",
@ -5389,5 +5383,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.2.0"
}

View file

@ -0,0 +1,151 @@
<?php
namespace Appwrite\Utopia\Database\Validator;
use Appwrite\Utopia\Database\Validator\Queries;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query as QueryValidator;
class IndexedQueries extends Queries
{
/**
* @var Document[]
*/
protected $attributes = [];
/**
* @var Document[]
*/
protected $indexes = [];
/**
* Expression constructor
*
* This Queries Validator filters indexes for only available indexes
*
* @param QueryValidator $validator
* @param Document[] $attributes
* @param Document[] $indexes
* @param bool $strict
*/
public function __construct($validator, $attributes = [], $indexes = [])
{
// Remove failed/stuck/processing indexes
$availableIndexes = \array_filter($indexes, function ($index) {
return $index->getAttribute('status') === 'available';
});
$this->attributes = $attributes;
$this->indexes[] = new Document([
'type' => Database::INDEX_UNIQUE,
'attributes' => ['$id']
]);
$this->indexes[] = new Document([
'type' => Database::INDEX_KEY,
'attributes' => ['$createdAt']
]);
$this->indexes[] = new Document([
'type' => Database::INDEX_KEY,
'attributes' => ['$updatedAt']
]);
foreach ($indexes ?? [] as $index) {
$this->indexes[] = $index;
}
parent::__construct($validator);
}
/**
* Check if indexed array $indexes matches $queries
*
* @param array $indexes
* @param array $queries
*
* @return bool
*/
protected function arrayMatch(array $indexes, array $queries): bool
{
// Check the count of indexes first for performance
if (count($queries) !== count($indexes)) {
return false;
}
// Sort them for comparison, the order is not important here anymore.
sort($indexes, SORT_STRING);
sort($queries, SORT_STRING);
// Only matching arrays will have equal diffs in both directions
if (array_diff_assoc($indexes, $queries) !== array_diff_assoc($queries, $indexes)) {
return false;
}
return true;
}
/**
* Is valid.
*
* Returns false if:
* 1. any query in $value is invalid based on $validator
* 2. there is no index with an exact match of the filters
* 3. there is no index with an exact match of the order attributes
*
* Otherwise, returns true.
*
* @param mixed $value
* @return bool
*/
public function isValid($value): bool
{
if (!$this->isValid($value)) {
return false;
}
$queries = Query::parseQueries($value);
$grouped = Query::groupByType($queries);
/** @var Query[] */ $filters = $grouped['filters'];
/** @var string[] */ $orderAttributes = $grouped['orderAttributes'];
// Check filter queries for exact index match
if (count($filters) > 0) {
$filtersByAttribute = [];
foreach ($filters as $filter) {
$filtersByAttribute[$filter->getAttribute()] = $filter->getMethod();
}
$found = null;
foreach ($this->indexes as $index) {
if ($this->arrayMatch($index->getAttribute('attributes'), array_keys($filtersByAttribute))) {
$found = $index;
}
}
if (!$found) {
$this->message = 'Index not found: ' . implode(",", array_keys($filtersByAttribute));
return false;
}
// search method requires fulltext index
if (in_array(Query::TYPE_SEARCH, array_values($filtersByAttribute)) && $found['type'] !== Database::INDEX_FULLTEXT) {
$this->message = 'Search method requires fulltext index: ' . implode(",", array_keys($filtersByAttribute));
return false;
}
}
// Check order attributes for exact index match
$validator = new OrderAttributes($this->attributes, $this->indexes, true);
if (count($orderAttributes) > 0 && !$validator->isValid($orderAttributes)) {
$this->message = $validator->getDescription();
return false;
}
return true;
}
}

View file

@ -2,28 +2,103 @@
namespace Appwrite\Utopia\Database\Validator;
use Utopia\Validator;
use Utopia\Database\Document;
use Utopia\Database\Validator\Queries as ValidatorQueries;
use Utopia\Database\Validator\Query as QueryValidator;
use Utopia\Database\Query;
class Queries extends ValidatorQueries
class Queries extends Validator
{
/**
* Expression constructor
* @var string
*/
protected $message = 'Invalid queries';
/**
* @var QueryValidator
*/
protected $validator;
/**
* Queries constructor
*
* This Queries Validator that filters indexes for only available indexes
*
* @param QueryValidator $validator
* @param Document[] $attributes
* @param Document[] $indexes
* @param Validator $validator used to validate each query
* @param Document[] $attributes allowed attributes to be queried
* @param Document[] $indexes available for strict query matching
* @param bool $strict
*/
public function __construct($validator, $attributes = [], $indexes = [], $strict = true)
public function __construct(Validator $validator)
{
// Remove failed/stuck/processing indexes
$availableIndexes = \array_filter($indexes, function ($index) {
return $index->getAttribute('status') === 'available';
});
$this->validator = $validator;
}
parent::__construct($validator, $attributes, $availableIndexes, $strict);
/**
* Get Description.
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return $this->message;
}
/**
* Is valid.
*
* Returns false if:
* 1. any query in $value is invalid based on $validator
*
* Otherwise, returns true.
*
* @param mixed $value
* @return bool
*/
public function isValid($value): bool
{
$queries = [];
foreach ($value as $query) {
if (!$query instanceof Query) {
try {
$query = Query::parse($query);
} catch (\Throwable $th) {
$this->message = 'Invalid query: ${query}';
return false;
}
}
if (!$this->validator->isValid($query)) {
$this->message = 'Query not valid: ' . $this->validator->getDescription();
return false;
}
$queries[] = $query;
}
return true;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return true;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_OBJECT;
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetCursorFilterQuery;
use Utopia\Database\Query;
class LimitOffsetCursorFilterOrderQuery extends LimitOffsetCursorFilterQuery
{
/**
* @var string
*/
protected $message = 'Invalid query';
/**
* @var array
*/
protected $schema = [];
/**
* Query constructor
*
* @param int $maxLimit
* @param int $maxOffset
* @param int $maxValuesCount
*/
public function __construct(int $maxLimit = 100, int $maxOffset = 5000, array $attributes = [], int $maxValuesCount = 100)
{
foreach ($attributes as $attribute) {
$this->schema[$attribute->getAttribute('key')] = $attribute->getArrayCopy();
}
$this->maxValuesCount = $maxValuesCount;
parent::__construct($maxLimit, $maxOffset);
}
/**
* Is valid.
*
* Returns true if:
* 1. method is limit or offset and values are within range
* 2. method is cursorBefore or cursorAfter and value is not null
* 3. method is a filter method, attribute exists, and value matches attribute type
* 4. method is orderAsc or orderDesc and attribute exists or is empty string
*
* Otherwise, returns false
*
* @param Query $value
*
* @return bool
*/
public function isValid($query): bool
{
$method = $query->getMethod();
$attribute = $query->getAttribute();
if ($method === Query::TYPE_ORDERASC || $method === Query::TYPE_ORDERDESC) {
if ($attribute === '') {
return true;
}
return $this->isValidAttribute($attribute);
}
return parent::isValid($query);
}
}

View file

@ -0,0 +1,137 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetCursorQuery;
use Utopia\Database\Database;
use Utopia\Database\Query;
class LimitOffsetCursorFilterQuery extends LimitOffsetCursorQuery
{
/**
* @var string
*/
protected $message = 'Invalid query';
/**
* @var array
*/
protected $schema = [];
/**
* Query constructor
*
* @param int $maxLimit
* @param int $maxOffset
* @param int $maxValuesCount
*/
public function __construct(int $maxLimit = 100, int $maxOffset = 5000, array $attributes = [], int $maxValuesCount = 100)
{
foreach ($attributes as $attribute) {
$this->schema[$attribute->getAttribute('key')] = $attribute->getArrayCopy();
}
$this->maxValuesCount = $maxValuesCount;
parent::__construct($maxLimit, $maxOffset);
}
protected function isValidAttribute($attribute): bool
{
// Search for attribute in schema
if (!isset($this->schema[$attribute])) {
$this->message = 'Attribute not found in schema: ' . $attribute;
return false;
}
return true;
}
protected function isValidAttributeAndValues(string $attribute, array $values): bool
{
if (!$this->isValidAttribute($attribute)) {
return false;
}
$attributeSchema = $this->schema[$attribute];
if (count($values) > $this->maxValuesCount) {
$this->message = 'Query on attribute has greater than ' . $this->maxValuesCount . ' values: ' . $attribute;
return false;
}
// Extract the type of desired attribute from collection $schema
$attributeType = $attributeSchema['type'];
foreach ($values as $value) {
$condition = match ($attributeType) {
Database::VAR_DATETIME => gettype($value) === Database::VAR_STRING,
default => gettype($value) === $attributeType
};
if (!$condition) {
$this->message = 'Query type does not match expected: ' . $attributeType;
return false;
}
}
return true;
}
protected function isValidContains(string $attribute, array $values): bool
{
if (!$this->isValidAttributeAndValues($attribute, $values)) {
return false;
}
$attributeSchema = $this->schema[$attribute];
// Contains method only supports array attributes
if (!$attributeSchema['array']) {
$this->message = 'Query method only supported on array attributes: ' . Query::TYPE_CONTAINS;
return false;
}
return true;
}
/**
* Is valid.
*
* Returns true if:
* 1. method is limit or offset and values are within range
* 2. method is cursorBefore or cursorAfter and value is not null
* 3. method is a filter method, attribute exists, and value matches attribute type
*
* Otherwise, returns false
*
* @param Query $value
*
* @return bool
*/
public function isValid($query): bool
{
// Validate method
$method = $query->getMethod();
$attribute = $query->getAttribute();
switch ($method) {
case Query::TYPE_CONTAINS:
$values = $query->getValues();
return $this->isValidContains($attribute, $values);
case Query::TYPE_EQUAL:
case Query::TYPE_NOTEQUAL:
case Query::TYPE_LESSER:
case Query::TYPE_LESSEREQUAL:
case Query::TYPE_GREATER:
case Query::TYPE_GREATEREQUAL:
case Query::TYPE_SEARCH:
$values = $query->getValues();
return $this->isValidAttributeAndValues($attribute, $values);
default:
return parent::isValid($query);
}
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Utopia\Database\Query;
use Utopia\Database\Validator\UID;
class LimitOffsetCursorQuery extends LimitOffsetQuery
{
/**
* @var string
*/
protected $message = 'Invalid query';
/**
* Query constructor
*
* @param int $maxLimit
* @param int $maxOffset
* @param int $maxValuesCount
*/
public function __construct(int $maxLimit = 100, int $maxOffset = 5000)
{
parent::__construct($maxLimit, $maxOffset);
}
protected function isValidCursor($cursor): bool
{
$validator = new UID();
if ($validator->isValid($cursor)) {
return true;
}
$this->message = 'Invalid cursor: ' . $validator->getDescription();
return false;
}
/**
* Is valid.
*
* Returns true if:
* 1. method is limit or offset and values are within range
* 2. method is cursorBefore or cursorAfter and value is not null.
*
* Otherwise, returns false
*
* @param Query $value
*
* @return bool
*/
public function isValid($query): bool
{
// Validate method
$method = $query->getMethod();
if ($method === Query::TYPE_CURSORAFTER || $method === Query::TYPE_CURSORBEFORE) {
$cursor = $query->getValue();
return $this->isValidCursor($cursor);
}
return parent::isValid($query);
}
}

View file

@ -3,9 +3,10 @@
namespace Appwrite\Utopia\Database\Validator\Queries;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query as QueryValidator;
use Utopia\Validator\Range;
use Utopia\Validator;
class LimitOffsetQuery extends QueryValidator
class LimitOffsetQuery extends Validator
{
/**
* @var string
@ -28,11 +29,45 @@ class LimitOffsetQuery extends QueryValidator
$this->maxOffset = $maxOffset;
}
/**
* Get Description.
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return $this->message;
}
protected function isValidLimit($limit): bool
{
$validator = new Range(0, $this->maxLimit);
if ($validator->isValid($limit)) {
return true;
}
$this->message = 'Invalid limit: ' . $validator->getDescription();
return false;
}
protected function isValidOffset($offset): bool
{
$validator = new Range(0, $this->maxOffset);
if ($validator->isValid($offset)) {
return true;
}
$this->message = 'Invalid offset: ' . $validator->getDescription();
return false;
}
/**
* Is valid.
*
* Returns true if method is limit or offset and values are within range.
*
*
* @param Query $value
*
* @return bool
@ -55,4 +90,28 @@ class LimitOffsetQuery extends QueryValidator
return false;
}
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_OBJECT;
}
}

View file

@ -7,6 +7,9 @@ use Appwrite\Utopia\Database\Validator\Queries\Collection;
class Users extends Collection
{
public const ALLOWED_ATTRIBUTES = [
'$id',
'$createdAt',
'$updatedAt',
'name',
'email',
'phone',

View file

@ -0,0 +1,94 @@
<?php
namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetCursorFilterOrderQuery;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Validator;
use PHPUnit\Framework\TestCase;
class LimitOffsetCursorFilterOrderQueryTest extends TestCase
{
/**
* @var Validator
*/
protected $validator = null;
public function setUp(): void
{
$this->validator = new LimitOffsetCursorFilterOrderQuery(
attributes: [
new Document([
'key' => 'attr',
'type' => Database::VAR_STRING,
'array' => false,
]),
],
);
}
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());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORAFTER, values: ['asdf'])), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORBEFORE, values: ['asdf'])), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderAsc('')), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderDesc('')), 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());
$this->assertEquals($this->validator->isValid(Query::equal('dne', ['v'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::equal('', ['v'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderDesc('dne')), false, $this->validator->getDescription());
}
public function testValues(): void
{
$validator = new Queries($this->validator);
// 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());
$this->assertEquals($validator->isValid(['cursorAfter("asdf")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['cursorBefore("asdf")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['equal("attr", "v")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderAsc("attr")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderAsc("")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderDesc("attr")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderDesc("")']), 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());
$this->assertEquals($validator->isValid(['equal("dne", "v")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['equal("", "v")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderDesc("dne")']), false, $validator->getDescription());
}
}

View file

@ -0,0 +1,88 @@
<?php
namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetCursorFilterQuery;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Validator;
use PHPUnit\Framework\TestCase;
class LimitOffsetCursorFilterQueryTest extends TestCase
{
/**
* @var Validator
*/
protected $validator = null;
public function setUp(): void
{
$this->validator = new LimitOffsetCursorFilterQuery(
attributes: [
new Document([
'key' => 'attr',
'type' => Database::VAR_STRING,
'array' => false,
]),
],
);
}
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());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORAFTER, values: ['asdf'])), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORBEFORE, values: ['asdf'])), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), 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());
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::equal('dne', ['v'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::equal('', ['v'])), false, $this->validator->getDescription());
}
public function testValues(): void
{
$validator = new Queries($this->validator);
// 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());
$this->assertEquals($validator->isValid(['cursorAfter("asdf")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['cursorBefore("asdf")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['equal("attr", "v")']), 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());
$this->assertEquals($validator->isValid(['orderAsc("attr")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderDesc("attr")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['equal("dne", "v")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['equal("", "v")']), false, $validator->getDescription());
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\LimitOffsetCursorQuery;
use Utopia\Database\Query;
use Utopia\Validator;
use PHPUnit\Framework\TestCase;
class LimitOffsetCursorQueryTest extends TestCase
{
/**
* @var Validator
*/
protected $validator = null;
public function setUp(): void
{
$this->validator = new LimitOffsetCursorQuery();
}
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());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORAFTER, values: ['asdf'])), true, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORBEFORE, values: ['asdf'])), 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());
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), false, $this->validator->getDescription());
}
public function testValues(): void
{
$validator = new Queries($this->validator);
// 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());
$this->assertEquals($validator->isValid(['cursorAfter("asdf")']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['cursorBefore("asdf")']), 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());
$this->assertEquals($validator->isValid(['equal("attr", "v")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderAsc("attr")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderDesc("attr")']), false, $validator->getDescription());
}
}

View file

@ -5,12 +5,13 @@ 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 Utopia\Validator;
use PHPUnit\Framework\TestCase;
class LimitOffsetQueryTest extends TestCase
{
/**
* @var Key
* @var Validator
*/
protected $validator = null;
@ -38,13 +39,18 @@ class LimitOffsetQueryTest extends TestCase
$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());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORAFTER, values: ['asdf'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORBEFORE, values: ['asdf'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), false, $this->validator->getDescription());
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), false, $this->validator->getDescription());
}
public function testValues(): void
{
$validator = new Queries($this->validator, strict: false);
$validator = new Queries($this->validator);
// Test for Success
$this->assertEquals($validator->isValid(['limit(1)']), true, $validator->getDescription());
$this->assertEquals($validator->isValid(['limit(0)']), true, $validator->getDescription());
@ -59,5 +65,10 @@ class LimitOffsetQueryTest extends TestCase
$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());
$this->assertEquals($validator->isValid(['cursorAfter("asdf")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['cursorBefore("asdf")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['equal("attr", "v")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderAsc("attr")']), false, $validator->getDescription());
$this->assertEquals($validator->isValid(['orderDesc("attr")']), false, $validator->getDescription());
}
}