Add additional Validators for Queries
This commit is contained in:
parent
a5bc07c9e4
commit
3890dea35b
14 changed files with 846 additions and 32 deletions
|
@ -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')
|
||||
|
|
|
@ -88,9 +88,6 @@
|
|||
"ext-phpiredis": "*"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"allow-plugins": false,
|
||||
"preferred-install": "dist",
|
||||
"platform": {
|
||||
"php": "8.0"
|
||||
}
|
||||
|
|
10
composer.lock
generated
10
composer.lock
generated
|
@ -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"
|
||||
}
|
||||
|
|
151
src/Appwrite/Utopia/Database/Validator/IndexedQueries.php
Normal file
151
src/Appwrite/Utopia/Database/Validator/IndexedQueries.php
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue