mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22b4cc9e8b | ||
|
|
59897433a4 | ||
|
|
f99d929628 | ||
|
|
3333031433 | ||
|
|
5074988700 | ||
|
|
7d7e861125 |
@@ -124,9 +124,18 @@ class Create extends Action
|
||||
$collectionKey = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence();
|
||||
$databaseKey = 'database_' . $database->getSequence();
|
||||
|
||||
// Map 'float' to 'double' (Database::VAR_FLOAT) before validation
|
||||
$attributes = array_map(function ($attr) {
|
||||
if (isset($attr['type']) && $attr['type'] === 'float') {
|
||||
$attr['type'] = Database::VAR_FLOAT;
|
||||
}
|
||||
return $attr;
|
||||
}, $attributes);
|
||||
|
||||
$attributesValidator = new AttributesValidator(
|
||||
APP_LIMIT_ARRAY_PARAMS_SIZE,
|
||||
$dbForProject->getAdapter()->getSupportForSpatialAttributes()
|
||||
$dbForProject->getAdapter()->getSupportForSpatialAttributes(),
|
||||
$this->isCollectionsAPI() ? AttributesValidator::TYPE_LEGACY : AttributesValidator::TYPE_TABLESDB
|
||||
);
|
||||
|
||||
if (!$attributesValidator->isValid($attributes)) {
|
||||
@@ -134,10 +143,11 @@ class Create extends Action
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $attributesValidator->getDescription());
|
||||
}
|
||||
|
||||
$structureTerm = $this->isCollectionsAPI() ? 'attributes' : 'columns';
|
||||
foreach ($attributes as $attribute) {
|
||||
if (($attribute['type'] ?? '') === Database::VAR_RELATIONSHIP) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Relationship attributes cannot be created inline. Use the create relationship endpoint instead.');
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, "Relationship {$structureTerm} cannot be created inline. Use the create relationship endpoint instead.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +165,10 @@ class Create extends Action
|
||||
}
|
||||
|
||||
// Validate indexes
|
||||
$indexesValidator = new IndexesValidator($dbForProject->getLimitForIndexes());
|
||||
$indexesValidator = new IndexesValidator(
|
||||
$dbForProject->getLimitForIndexes(),
|
||||
$this->isCollectionsAPI() ? IndexesValidator::TYPE_LEGACY : IndexesValidator::TYPE_TABLESDB
|
||||
);
|
||||
if (!$indexesValidator->isValid($indexes)) {
|
||||
$dbForProject->deleteDocument($databaseKey, $collection->getId());
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $indexesValidator->getDescription());
|
||||
@@ -186,7 +199,9 @@ class Create extends Action
|
||||
$dbForProject->getAdapter()->getSupportForVectors(),
|
||||
$dbForProject->getAdapter()->getSupportForAttributes(),
|
||||
$dbForProject->getAdapter()->getSupportForMultipleFulltextIndexes(),
|
||||
$dbForProject->getAdapter()->getSupportForIdenticalIndexes()
|
||||
$dbForProject->getAdapter()->getSupportForIdenticalIndexes(),
|
||||
$dbForProject->getAdapter()->getSupportForObject(),
|
||||
|
||||
);
|
||||
|
||||
foreach ($collectionIndexes as $indexDoc) {
|
||||
@@ -237,6 +252,12 @@ class Create extends Action
|
||||
$dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId());
|
||||
$dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence());
|
||||
|
||||
// Set attributes and indexes on the collection document for the response
|
||||
// Use the full database documents for attributes (they have status and other fields needed for response models)
|
||||
// Use the simpler collection documents for indexes (they have the basic fields without internal tracking)
|
||||
$collection->setAttribute('attributes', $attributeDocuments);
|
||||
$collection->setAttribute('indexes', $collectionIndexes);
|
||||
|
||||
$queueForEvents
|
||||
->setContext('database', $database)
|
||||
->setParam('databaseId', $databaseId)
|
||||
|
||||
@@ -14,9 +14,18 @@ use Utopia\Validator\URL;
|
||||
|
||||
class Attributes extends Validator
|
||||
{
|
||||
public const TYPE_LEGACY = 'legacy';
|
||||
public const TYPE_TABLESDB = 'tablesdb';
|
||||
|
||||
protected int $maxAttributes;
|
||||
protected string $message = 'Invalid attributes';
|
||||
|
||||
/**
|
||||
* @var string The term to use in error messages (attributes/columns)
|
||||
*/
|
||||
protected string $termPlural;
|
||||
protected string $termSingular;
|
||||
|
||||
/**
|
||||
* @var array<string> Supported attribute types
|
||||
*/
|
||||
@@ -45,12 +54,23 @@ class Attributes extends Validator
|
||||
/**
|
||||
* @param int $maxAttributes Maximum number of attributes allowed
|
||||
* @param bool $supportForSpatialAttributes Whether DB supports spatial attributes
|
||||
* @param string $type The API type context ('legacy' for attributes, 'tablesdb' for columns)
|
||||
*/
|
||||
public function __construct(
|
||||
int $maxAttributes = APP_LIMIT_ARRAY_PARAMS_SIZE,
|
||||
protected bool $supportForSpatialAttributes = true,
|
||||
string $type = self::TYPE_LEGACY,
|
||||
) {
|
||||
$this->maxAttributes = $maxAttributes;
|
||||
|
||||
// Set terminology based on API type
|
||||
if ($type === self::TYPE_TABLESDB) {
|
||||
$this->termPlural = 'Columns';
|
||||
$this->termSingular = 'Column';
|
||||
} else {
|
||||
$this->termPlural = 'Attributes';
|
||||
$this->termSingular = 'Attribute';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,13 +93,16 @@ class Attributes extends Validator
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
$termLower = \strtolower($this->termSingular);
|
||||
$termPluralLower = \strtolower($this->termPlural);
|
||||
|
||||
if (!\is_array($value)) {
|
||||
$this->message = 'Attributes must be an array';
|
||||
$this->message = "{$this->termPlural} must be an array";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\count($value) > $this->maxAttributes) {
|
||||
$this->message = 'Maximum of ' . $this->maxAttributes . ' attributes allowed';
|
||||
$this->message = "Maximum of {$this->maxAttributes} {$termPluralLower} allowed";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,30 +111,30 @@ class Attributes extends Validator
|
||||
|
||||
foreach ($value as $index => $attribute) {
|
||||
if (!\is_array($attribute)) {
|
||||
$this->message = "Attribute at index $index must be an object";
|
||||
$this->message = "{$this->termSingular} at index $index must be an object";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!isset($attribute['key'])) {
|
||||
$this->message = "Attribute at index $index is missing required field 'key'";
|
||||
$this->message = "{$this->termSingular} at index $index is missing required field 'key'";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($attribute['type'])) {
|
||||
$this->message = "Attribute at index $index is missing required field 'type'";
|
||||
$this->message = "{$this->termSingular} at index $index is missing required field 'type'";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate key
|
||||
if (!$keyValidator->isValid($attribute['key'])) {
|
||||
$this->message = "Invalid key for attribute at index $index: " . $keyValidator->getDescription();
|
||||
$this->message = "Invalid key for {$termLower} at index $index: " . $keyValidator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate keys
|
||||
if (in_array($attribute['key'], $keys)) {
|
||||
$this->message = "Duplicate attribute key: " . $attribute['key'];
|
||||
$this->message = "Duplicate {$termLower} key: " . $attribute['key'];
|
||||
return false;
|
||||
}
|
||||
$keys[] = $attribute['key'];
|
||||
@@ -119,26 +142,26 @@ class Attributes extends Validator
|
||||
// Check for reserved keys
|
||||
$reservedKeys = ['$id', '$createdAt', '$updatedAt', '$permissions', '$collection'];
|
||||
if (\in_array($attribute['key'], $reservedKeys)) {
|
||||
$this->message = "Attribute key '" . $attribute['key'] . "' is reserved and cannot be used";
|
||||
$this->message = "{$this->termSingular} key '" . $attribute['key'] . "' is reserved and cannot be used";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate type
|
||||
if (!\in_array($attribute['type'], $this->supportedTypes)) {
|
||||
$this->message = "Invalid type for attribute '" . $attribute['key'] . "': " . $attribute['type'];
|
||||
$this->message = "Invalid type for {$termLower} '" . $attribute['key'] . "': " . $attribute['type'];
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate spatial type support
|
||||
if (\in_array($attribute['type'], Database::SPATIAL_TYPES) && !$this->supportForSpatialAttributes) {
|
||||
$this->message = "Spatial attributes are not supported by the current database";
|
||||
$this->message = "Spatial {$termPluralLower} are not supported by the current database";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate size for string types
|
||||
if ($attribute['type'] === Database::VAR_STRING) {
|
||||
if (!isset($attribute['size']) || !is_int($attribute['size']) || $attribute['size'] < 1 || $attribute['size'] > APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH) {
|
||||
$this->message = "Invalid or missing size for string attribute '" . $attribute['key'] . "'. Size must be between 1 and " . APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH;
|
||||
$this->message = "Invalid or missing size for string {$termLower} '" . $attribute['key'] . "'. Size must be between 1 and " . APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -147,61 +170,61 @@ class Attributes extends Validator
|
||||
if (isset($attribute['format']) && $attribute['format'] !== '') {
|
||||
// Format is only allowed for string type
|
||||
if ($attribute['type'] !== Database::VAR_STRING) {
|
||||
$this->message = "Format is only allowed for string type for attribute '" . $attribute['key'] . "'";
|
||||
$this->message = "Format is only allowed for string type for {$termLower} '" . $attribute['key'] . "'";
|
||||
return false;
|
||||
}
|
||||
if (!in_array($attribute['format'], $this->supportedFormats)) {
|
||||
$this->message = "Invalid format for attribute '" . $attribute['key'] . "': " . $attribute['format'];
|
||||
$this->message = "Invalid format for {$termLower} '" . $attribute['key'] . "': " . $attribute['format'];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate required field if provided
|
||||
if (isset($attribute['required']) && !is_bool($attribute['required'])) {
|
||||
$this->message = "Invalid 'required' value for attribute '" . $attribute['key'] . "': must be a boolean";
|
||||
$this->message = "Invalid 'required' value for {$termLower} '" . $attribute['key'] . "': must be a boolean";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate array field if provided
|
||||
if (isset($attribute['array']) && !is_bool($attribute['array'])) {
|
||||
$this->message = "Invalid 'array' value for attribute '" . $attribute['key'] . "': must be a boolean";
|
||||
$this->message = "Invalid 'array' value for {$termLower} '" . $attribute['key'] . "': must be a boolean";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate signed field if provided
|
||||
if (isset($attribute['signed']) && !is_bool($attribute['signed'])) {
|
||||
$this->message = "Invalid 'signed' value for attribute '" . $attribute['key'] . "': must be a boolean";
|
||||
$this->message = "Invalid 'signed' value for {$termLower} '" . $attribute['key'] . "': must be a boolean";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate signed only for integer/float types
|
||||
if (isset($attribute['signed']) && !in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': 'signed' can only be used with integer or float types";
|
||||
$this->message = "{$this->termSingular} '" . $attribute['key'] . "': 'signed' can only be used with integer or float types";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate required and default conflict
|
||||
if (isset($attribute['required']) && $attribute['required'] === true && isset($attribute['default']) && $attribute['default'] !== null) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "' cannot have a default value when required is true";
|
||||
$this->message = "{$this->termSingular} '" . $attribute['key'] . "' cannot have a default value when required is true";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate array and default conflict
|
||||
if (isset($attribute['array']) && $attribute['array'] === true && isset($attribute['default']) && $attribute['default'] !== null) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "' cannot have a default value when array is true";
|
||||
$this->message = "{$this->termSingular} '" . $attribute['key'] . "' cannot have a default value when array is true";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate min/max range for integer/float
|
||||
if (isset($attribute['min']) || isset($attribute['max'])) {
|
||||
if (!in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': min/max can only be used with integer or float types";
|
||||
$this->message = "{$this->termSingular} '" . $attribute['key'] . "': min/max can only be used with integer or float types";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If both are set, validate ordering
|
||||
if (isset($attribute['min']) && isset($attribute['max']) && $attribute['min'] > $attribute['max']) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "': minimum value must be less than or equal to maximum value";
|
||||
$this->message = "{$this->termSingular} '" . $attribute['key'] . "': minimum value must be less than or equal to maximum value";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -211,7 +234,7 @@ class Attributes extends Validator
|
||||
switch ($attribute['type']) {
|
||||
case Database::VAR_STRING:
|
||||
if (!is_string($attribute['default'])) {
|
||||
$this->message = "Default value for string attribute '" . $attribute['key'] . "' must be a string";
|
||||
$this->message = "Default value for string {$termLower} '" . $attribute['key'] . "' must be a string";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -220,7 +243,7 @@ class Attributes extends Validator
|
||||
if ($size > 0) {
|
||||
$textValidator = new Text($size, 0);
|
||||
if (!$textValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for attribute '" . $attribute['key'] . "' exceeds maximum size of $size characters";
|
||||
$this->message = "Default value for {$termLower} '" . $attribute['key'] . "' exceeds maximum size of $size characters";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -230,19 +253,19 @@ class Attributes extends Validator
|
||||
if ($format === APP_DATABASE_ATTRIBUTE_EMAIL) {
|
||||
$emailValidator = new Email();
|
||||
if (!$emailValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for email attribute '" . $attribute['key'] . "' must be a valid email address";
|
||||
$this->message = "Default value for email {$termLower} '" . $attribute['key'] . "' must be a valid email address";
|
||||
return false;
|
||||
}
|
||||
} elseif ($format === APP_DATABASE_ATTRIBUTE_IP) {
|
||||
$ipValidator = new IP();
|
||||
if (!$ipValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for IP attribute '" . $attribute['key'] . "' must be a valid IP address";
|
||||
$this->message = "Default value for IP {$termLower} '" . $attribute['key'] . "' must be a valid IP address";
|
||||
return false;
|
||||
}
|
||||
} elseif ($format === APP_DATABASE_ATTRIBUTE_URL) {
|
||||
$urlValidator = new URL();
|
||||
if (!$urlValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for URL attribute '" . $attribute['key'] . "' must be a valid URL";
|
||||
$this->message = "Default value for URL {$termLower} '" . $attribute['key'] . "' must be a valid URL";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -250,7 +273,7 @@ class Attributes extends Validator
|
||||
|
||||
case Database::VAR_INTEGER:
|
||||
if (!is_int($attribute['default'])) {
|
||||
$this->message = "Default value for integer attribute '" . $attribute['key'] . "' must be an integer";
|
||||
$this->message = "Default value for integer {$termLower} '" . $attribute['key'] . "' must be an integer";
|
||||
return false;
|
||||
}
|
||||
// Validate within range if min/max specified
|
||||
@@ -259,7 +282,7 @@ class Attributes extends Validator
|
||||
$max = $attribute['max'] ?? \PHP_INT_MAX;
|
||||
$rangeValidator = new Range($min, $max, Database::VAR_INTEGER);
|
||||
if (!$rangeValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for integer attribute '" . $attribute['key'] . "' must be between $min and $max";
|
||||
$this->message = "Default value for integer {$termLower} '" . $attribute['key'] . "' must be between $min and $max";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -267,7 +290,7 @@ class Attributes extends Validator
|
||||
|
||||
case Database::VAR_FLOAT:
|
||||
if (!is_float($attribute['default']) && !is_int($attribute['default'])) {
|
||||
$this->message = "Default value for float attribute '" . $attribute['key'] . "' must be a number";
|
||||
$this->message = "Default value for float {$termLower} '" . $attribute['key'] . "' must be a number";
|
||||
return false;
|
||||
}
|
||||
// Validate within range if min/max specified
|
||||
@@ -276,7 +299,7 @@ class Attributes extends Validator
|
||||
$max = $attribute['max'] ?? \PHP_FLOAT_MAX;
|
||||
$rangeValidator = new Range($min, $max, Database::VAR_FLOAT);
|
||||
if (!$rangeValidator->isValid((float)$attribute['default'])) {
|
||||
$this->message = "Default value for float attribute '" . $attribute['key'] . "' must be between $min and $max";
|
||||
$this->message = "Default value for float {$termLower} '" . $attribute['key'] . "' must be between $min and $max";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -284,20 +307,20 @@ class Attributes extends Validator
|
||||
|
||||
case Database::VAR_BOOLEAN:
|
||||
if (!is_bool($attribute['default'])) {
|
||||
$this->message = "Default value for boolean attribute '" . $attribute['key'] . "' must be a boolean";
|
||||
$this->message = "Default value for boolean {$termLower} '" . $attribute['key'] . "' must be a boolean";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Database::VAR_DATETIME:
|
||||
if (!is_string($attribute['default'])) {
|
||||
$this->message = "Default value for datetime attribute '" . $attribute['key'] . "' must be a string in ISO 8601 format";
|
||||
$this->message = "Default value for datetime {$termLower} '" . $attribute['key'] . "' must be a string in ISO 8601 format";
|
||||
return false;
|
||||
}
|
||||
// Basic datetime format validation
|
||||
$datetimeValidator = new DatetimeValidator();
|
||||
if (!$datetimeValidator->isValid($attribute['default'])) {
|
||||
$this->message = "Default value for datetime attribute '" . $attribute['key'] . "' must be in valid ISO 8601 format";
|
||||
$this->message = "Default value for datetime {$termLower} '" . $attribute['key'] . "' must be in valid ISO 8601 format";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -307,18 +330,18 @@ class Attributes extends Validator
|
||||
// Validate enum elements if format is enum
|
||||
if (isset($attribute['format']) && $attribute['format'] === APP_DATABASE_ATTRIBUTE_ENUM) {
|
||||
if (!isset($attribute['elements']) || !is_array($attribute['elements']) || empty($attribute['elements'])) {
|
||||
$this->message = "Attribute '" . $attribute['key'] . "' with enum format must have 'elements' array";
|
||||
$this->message = "{$this->termSingular} '" . $attribute['key'] . "' with enum format must have 'elements' array";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate each enum element
|
||||
foreach ($attribute['elements'] as $elementIndex => $element) {
|
||||
if (!is_string($element) || empty($element)) {
|
||||
$this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' must be a non-empty string";
|
||||
$this->message = "Enum element at index $elementIndex for {$termLower} '" . $attribute['key'] . "' must be a non-empty string";
|
||||
return false;
|
||||
}
|
||||
if (strlen($element) > Database::LENGTH_KEY) {
|
||||
$this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' exceeds maximum length of " . Database::LENGTH_KEY . " characters";
|
||||
$this->message = "Enum element at index $elementIndex for {$termLower} '" . $attribute['key'] . "' exceeds maximum length of " . Database::LENGTH_KEY . " characters";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -326,7 +349,7 @@ class Attributes extends Validator
|
||||
// Validate default exists in elements
|
||||
if (isset($attribute['default']) && $attribute['default'] !== null) {
|
||||
if (!in_array($attribute['default'], $attribute['elements'], true)) {
|
||||
$this->message = "Default value for enum attribute '" . $attribute['key'] . "' must be one of the provided elements";
|
||||
$this->message = "Default value for enum {$termLower} '" . $attribute['key'] . "' must be one of the provided elements";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,16 @@ use Utopia\Validator;
|
||||
|
||||
class Indexes extends Validator
|
||||
{
|
||||
public const TYPE_LEGACY = 'legacy';
|
||||
public const TYPE_TABLESDB = 'tablesdb';
|
||||
|
||||
protected string $message = 'Invalid indexes';
|
||||
|
||||
/**
|
||||
* @var string The term to use in error messages for attributes/columns
|
||||
*/
|
||||
protected string $attributeTerm;
|
||||
|
||||
/**
|
||||
* @var array<string> Supported index types
|
||||
*/
|
||||
@@ -30,10 +38,14 @@ class Indexes extends Validator
|
||||
|
||||
/**
|
||||
* @param int $maxIndexes Maximum number of indexes allowed
|
||||
* @param string $type The API type context ('legacy' for attributes, 'tablesdb' for columns)
|
||||
*/
|
||||
public function __construct(
|
||||
protected int $maxIndexes = APP_LIMIT_ARRAY_PARAMS_SIZE,
|
||||
string $type = self::TYPE_LEGACY,
|
||||
) {
|
||||
// Set terminology based on API type
|
||||
$this->attributeTerm = ($type === self::TYPE_TABLESDB) ? 'column' : 'attribute';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,11 +96,21 @@ class Indexes extends Validator
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($index['attributes']) || !is_array($index['attributes'])) {
|
||||
$this->message = "Index at position $i is missing required field 'attributes' (must be an array)";
|
||||
$attributesFieldName = ($this->attributeTerm === 'column') ? 'columns' : 'attributes';
|
||||
if (!isset($index['attributes']) && !isset($index['columns'])) {
|
||||
$this->message = "Index at position $i is missing required field '{$attributesFieldName}' (must be an array)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Support both 'attributes' and 'columns' field names for TablesDB compatibility
|
||||
$indexAttributes = $index['attributes'] ?? $index['columns'] ?? null;
|
||||
if (!is_array($indexAttributes)) {
|
||||
$this->message = "Index at position $i is missing required field '{$attributesFieldName}' (must be an array)";
|
||||
return false;
|
||||
}
|
||||
// Normalize to 'attributes' for internal processing
|
||||
$index['attributes'] = $indexAttributes;
|
||||
|
||||
// Validate key format
|
||||
if (!$keyValidator->isValid($index['key'])) {
|
||||
$this->message = "Invalid key for index at position $i: " . $keyValidator->getDescription();
|
||||
@@ -110,12 +132,12 @@ class Indexes extends Validator
|
||||
|
||||
// Validate attributes array
|
||||
if (empty($index['attributes'])) {
|
||||
$this->message = "Index '" . $index['key'] . "' must have at least one attribute";
|
||||
$this->message = "Index '" . $index['key'] . "' must have at least one {$this->attributeTerm}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($index['attributes']) > APP_LIMIT_ARRAY_PARAMS_SIZE) {
|
||||
$this->message = "Index '" . $index['key'] . "' cannot have more than " . APP_LIMIT_ARRAY_PARAMS_SIZE . " attributes";
|
||||
$this->message = "Index '" . $index['key'] . "' cannot have more than " . APP_LIMIT_ARRAY_PARAMS_SIZE . " {$this->attributeTerm}s";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -123,11 +145,11 @@ class Indexes extends Validator
|
||||
$systemAttrs = ['$id', '$createdAt', '$updatedAt'];
|
||||
foreach ($index['attributes'] as $attrIndex => $attr) {
|
||||
if (!is_string($attr)) {
|
||||
$this->message = "Invalid attribute at position $attrIndex in index '" . $index['key'] . "': must be a string";
|
||||
$this->message = "Invalid {$this->attributeTerm} at position $attrIndex in index '" . $index['key'] . "': must be a string";
|
||||
return false;
|
||||
}
|
||||
if (!$keyValidator->isValid($attr) && !in_array($attr, $systemAttrs)) {
|
||||
$this->message = "Invalid attribute name '$attr' in index '" . $index['key'] . "'";
|
||||
$this->message = "Invalid {$this->attributeTerm} name '$attr' in index '" . $index['key'] . "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -142,7 +164,7 @@ class Indexes extends Validator
|
||||
}
|
||||
|
||||
if (count($index['orders']) !== $attrCount) {
|
||||
$this->message = "Index '" . $index['key'] . "': orders array length (" . count($index['orders']) . ") must match attributes array length ($attrCount)";
|
||||
$this->message = "Index '" . $index['key'] . "': orders array length (" . count($index['orders']) . ") must match {$attributesFieldName} array length ($attrCount)";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,7 +184,7 @@ class Indexes extends Validator
|
||||
}
|
||||
|
||||
if (count($index['lengths']) !== $attrCount) {
|
||||
$this->message = "Index '" . $index['key'] . "': lengths array length (" . count($index['lengths']) . ") must match attributes array length ($attrCount)";
|
||||
$this->message = "Index '" . $index['key'] . "': lengths array length (" . count($index['lengths']) . ") must match {$attributesFieldName} array length ($attrCount)";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,12 +95,9 @@ class ColumnIndex extends Model
|
||||
|
||||
public function filter(Document $document): Document
|
||||
{
|
||||
$columns = $document->getAttribute('attributes', []);
|
||||
$document
|
||||
->removeAttribute('attributes')
|
||||
->setAttribute('columns', $columns);
|
||||
$attributes = $document->getAttribute('attributes', []);
|
||||
$document->setAttribute('columns', $attributes);
|
||||
|
||||
return $document;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,914 @@ trait DatabasesBase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithStringAttribute(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestStringAttr',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals('TestStringAttr', $collection['body']['name']);
|
||||
$this->assertCount(1, $collection['body']['attributes']);
|
||||
$this->assertEquals('title', $collection['body']['attributes'][0]['key']);
|
||||
$this->assertEquals('string', $collection['body']['attributes'][0]['type']);
|
||||
$this->assertEquals(255, $collection['body']['attributes'][0]['size']);
|
||||
$this->assertEquals(true, $collection['body']['attributes'][0]['required']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithMultipleAttributeTypes(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestMultiAttr',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'name',
|
||||
'type' => 'string',
|
||||
'size' => 100,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'age',
|
||||
'type' => 'integer',
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'min' => 0,
|
||||
'max' => 150,
|
||||
],
|
||||
[
|
||||
'key' => 'score',
|
||||
'type' => 'float',
|
||||
'required' => false,
|
||||
'default' => 0.0,
|
||||
'min' => 0.0,
|
||||
'max' => 100.0,
|
||||
],
|
||||
[
|
||||
'key' => 'isActive',
|
||||
'type' => 'boolean',
|
||||
'required' => false,
|
||||
'default' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'createdAt',
|
||||
'type' => 'datetime',
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals('TestMultiAttr', $collection['body']['name']);
|
||||
$this->assertCount(5, $collection['body']['attributes']);
|
||||
|
||||
// Verify each attribute type
|
||||
$attributeKeys = array_column($collection['body']['attributes'], 'key');
|
||||
$this->assertContains('name', $attributeKeys);
|
||||
$this->assertContains('age', $attributeKeys);
|
||||
$this->assertContains('score', $attributeKeys);
|
||||
$this->assertContains('isActive', $attributeKeys);
|
||||
$this->assertContains('createdAt', $attributeKeys);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithArrayAttribute(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestArrayAttr',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'tags',
|
||||
'type' => 'string',
|
||||
'size' => 50,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals('TestArrayAttr', $collection['body']['name']);
|
||||
$this->assertCount(1, $collection['body']['attributes']);
|
||||
$this->assertEquals('tags', $collection['body']['attributes'][0]['key']);
|
||||
$this->assertEquals(true, $collection['body']['attributes'][0]['array']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithDefaultValues(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestDefaults',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'status',
|
||||
'type' => 'string',
|
||||
'size' => 20,
|
||||
'required' => false,
|
||||
'default' => 'pending',
|
||||
],
|
||||
[
|
||||
'key' => 'count',
|
||||
'type' => 'integer',
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
],
|
||||
[
|
||||
'key' => 'enabled',
|
||||
'type' => 'boolean',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(3, $collection['body']['attributes']);
|
||||
|
||||
// Find and verify default values
|
||||
foreach ($collection['body']['attributes'] as $attr) {
|
||||
if ($attr['key'] === 'status') {
|
||||
$this->assertEquals('pending', $attr['default']);
|
||||
}
|
||||
if ($attr['key'] === 'count') {
|
||||
$this->assertEquals(0, $attr['default']);
|
||||
}
|
||||
if ($attr['key'] === 'enabled') {
|
||||
$this->assertEquals(false, $attr['default']);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithKeyIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestKeyIndex',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(1, $collection['body']['attributes']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals('title_idx', $collection['body']['indexes'][0]['key']);
|
||||
$this->assertEquals('key', $collection['body']['indexes'][0]['type']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithUniqueIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestUniqueIndex',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'email',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'email_unique',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['email'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals('email_unique', $collection['body']['indexes'][0]['key']);
|
||||
$this->assertEquals('unique', $collection['body']['indexes'][0]['type']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithFulltextIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestFulltextIndex',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'content',
|
||||
'type' => 'string',
|
||||
'size' => 10000,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'content_fulltext',
|
||||
'type' => 'fulltext',
|
||||
'attributes' => ['content'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals('content_fulltext', $collection['body']['indexes'][0]['key']);
|
||||
$this->assertEquals('fulltext', $collection['body']['indexes'][0]['type']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithCompoundIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestCompoundIndex',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'userId',
|
||||
'type' => 'string',
|
||||
'size' => 36,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'createdAt',
|
||||
'type' => 'datetime',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'user_date_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['userId', 'createdAt'],
|
||||
'orders' => ['ASC', 'DESC'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(2, $collection['body']['attributes']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals('user_date_idx', $collection['body']['indexes'][0]['key']);
|
||||
$this->assertCount(2, $collection['body']['indexes'][0]['attributes']);
|
||||
$this->assertEquals(['ASC', 'DESC'], $collection['body']['indexes'][0]['orders']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithMultipleIndexes(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestMultiIndex',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'slug',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'body',
|
||||
'type' => 'string',
|
||||
'size' => 50000,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
[
|
||||
'key' => 'slug_unique',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['slug'],
|
||||
],
|
||||
[
|
||||
'key' => 'body_fulltext',
|
||||
'type' => 'fulltext',
|
||||
'attributes' => ['body'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(3, $collection['body']['attributes']);
|
||||
$this->assertCount(3, $collection['body']['indexes']);
|
||||
|
||||
$indexKeys = array_column($collection['body']['indexes'], 'key');
|
||||
$this->assertContains('title_idx', $indexKeys);
|
||||
$this->assertContains('slug_unique', $indexKeys);
|
||||
$this->assertContains('body_fulltext', $indexKeys);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithEmptyArrays(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestEmptyArrays',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [],
|
||||
'indexes' => [],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertEquals('TestEmptyArrays', $collection['body']['name']);
|
||||
$this->assertCount(0, $collection['body']['attributes']);
|
||||
$this->assertCount(0, $collection['body']['indexes']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithIntegerMinMax(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestIntMinMax',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'temperature',
|
||||
'type' => 'integer',
|
||||
'required' => false,
|
||||
'min' => -50,
|
||||
'max' => 50,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(1, $collection['body']['attributes']);
|
||||
$this->assertEquals('temperature', $collection['body']['attributes'][0]['key']);
|
||||
$this->assertEquals(-50, $collection['body']['attributes'][0]['min']);
|
||||
$this->assertEquals(50, $collection['body']['attributes'][0]['max']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithFloatMinMax(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestFloatMinMax',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'rating',
|
||||
'type' => 'float',
|
||||
'required' => false,
|
||||
'min' => 0.0,
|
||||
'max' => 5.0,
|
||||
'default' => 0.0,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(1, $collection['body']['attributes']);
|
||||
$this->assertEquals('rating', $collection['body']['attributes'][0]['key']);
|
||||
$this->assertEquals(0.0, $collection['body']['attributes'][0]['min']);
|
||||
$this->assertEquals(5.0, $collection['body']['attributes'][0]['max']);
|
||||
$this->assertEquals(0.0, $collection['body']['attributes'][0]['default']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionInvalidAttributeType(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestInvalidType',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'invalid',
|
||||
'type' => 'invalid_type',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionInvalidIndexType(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestInvalidIndex',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'invalid_idx',
|
||||
'type' => 'invalid_type',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionMissingAttributeKey(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestMissingKey',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'type' => 'string',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionMissingAttributeType(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestMissingType',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'field',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionStringSizeMissing(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestMissingSize',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionIndexOnNonExistentAttribute(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestBadIndexAttr',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'nonexistent_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['nonexistent_field'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionDuplicateAttributeKeys(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestDuplicateKeys',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionDuplicateIndexKeys(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestDuplicateIndexKeys',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $collection['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateCollectionWithIndexLengths(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'TestIndexLengths',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'attributes' => [
|
||||
[
|
||||
'key' => 'longText',
|
||||
'type' => 'string',
|
||||
'size' => 10000,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'text_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['longText'],
|
||||
'lengths' => [255],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals([255], $collection['body']['indexes'][0]['lengths']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'collectionId' => $collection['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
|
||||
@@ -86,6 +86,914 @@ trait DatabasesBase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithStringColumn(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestStringCol',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertEquals('TestStringCol', $table['body']['name']);
|
||||
$this->assertCount(1, $table['body']['columns']);
|
||||
$this->assertEquals('title', $table['body']['columns'][0]['key']);
|
||||
$this->assertEquals('string', $table['body']['columns'][0]['type']);
|
||||
$this->assertEquals(255, $table['body']['columns'][0]['size']);
|
||||
$this->assertEquals(true, $table['body']['columns'][0]['required']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithMultipleColumnTypes(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestMultiCol',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'name',
|
||||
'type' => 'string',
|
||||
'size' => 100,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'age',
|
||||
'type' => 'integer',
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'min' => 0,
|
||||
'max' => 150,
|
||||
],
|
||||
[
|
||||
'key' => 'score',
|
||||
'type' => 'float',
|
||||
'required' => false,
|
||||
'default' => 0.0,
|
||||
'min' => 0.0,
|
||||
'max' => 100.0,
|
||||
],
|
||||
[
|
||||
'key' => 'isActive',
|
||||
'type' => 'boolean',
|
||||
'required' => false,
|
||||
'default' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'createdAt',
|
||||
'type' => 'datetime',
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertEquals('TestMultiCol', $table['body']['name']);
|
||||
$this->assertCount(5, $table['body']['columns']);
|
||||
|
||||
// Verify each column type
|
||||
$columnKeys = array_column($table['body']['columns'], 'key');
|
||||
$this->assertContains('name', $columnKeys);
|
||||
$this->assertContains('age', $columnKeys);
|
||||
$this->assertContains('score', $columnKeys);
|
||||
$this->assertContains('isActive', $columnKeys);
|
||||
$this->assertContains('createdAt', $columnKeys);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithArrayColumn(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestArrayCol',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'tags',
|
||||
'type' => 'string',
|
||||
'size' => 50,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertEquals('TestArrayCol', $table['body']['name']);
|
||||
$this->assertCount(1, $table['body']['columns']);
|
||||
$this->assertEquals('tags', $table['body']['columns'][0]['key']);
|
||||
$this->assertEquals(true, $table['body']['columns'][0]['array']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithDefaultValues(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestDefaults',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'status',
|
||||
'type' => 'string',
|
||||
'size' => 20,
|
||||
'required' => false,
|
||||
'default' => 'pending',
|
||||
],
|
||||
[
|
||||
'key' => 'count',
|
||||
'type' => 'integer',
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
],
|
||||
[
|
||||
'key' => 'enabled',
|
||||
'type' => 'boolean',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(3, $table['body']['columns']);
|
||||
|
||||
// Find and verify default values
|
||||
foreach ($table['body']['columns'] as $col) {
|
||||
if ($col['key'] === 'status') {
|
||||
$this->assertEquals('pending', $col['default']);
|
||||
}
|
||||
if ($col['key'] === 'count') {
|
||||
$this->assertEquals(0, $col['default']);
|
||||
}
|
||||
if ($col['key'] === 'enabled') {
|
||||
$this->assertEquals(false, $col['default']);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithKeyIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestKeyIndex',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(1, $table['body']['columns']);
|
||||
$this->assertCount(1, $table['body']['indexes']);
|
||||
$this->assertEquals('title_idx', $table['body']['indexes'][0]['key']);
|
||||
$this->assertEquals('key', $table['body']['indexes'][0]['type']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithUniqueIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestUniqueIndex',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'email',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'email_unique',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['email'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(1, $table['body']['indexes']);
|
||||
$this->assertEquals('email_unique', $table['body']['indexes'][0]['key']);
|
||||
$this->assertEquals('unique', $table['body']['indexes'][0]['type']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithFulltextIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestFulltextIndex',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'content',
|
||||
'type' => 'string',
|
||||
'size' => 10000,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'content_fulltext',
|
||||
'type' => 'fulltext',
|
||||
'attributes' => ['content'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(1, $table['body']['indexes']);
|
||||
$this->assertEquals('content_fulltext', $table['body']['indexes'][0]['key']);
|
||||
$this->assertEquals('fulltext', $table['body']['indexes'][0]['type']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithCompoundIndex(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestCompoundIndex',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'userId',
|
||||
'type' => 'string',
|
||||
'size' => 36,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'createdAt',
|
||||
'type' => 'datetime',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'user_date_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['userId', 'createdAt'],
|
||||
'orders' => ['ASC', 'DESC'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(2, $table['body']['columns']);
|
||||
$this->assertCount(1, $table['body']['indexes']);
|
||||
$this->assertEquals('user_date_idx', $table['body']['indexes'][0]['key']);
|
||||
$this->assertCount(2, $table['body']['indexes'][0]['columns']);
|
||||
$this->assertEquals(['ASC', 'DESC'], $table['body']['indexes'][0]['orders']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithMultipleIndexes(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestMultiIndex',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'slug',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'body',
|
||||
'type' => 'string',
|
||||
'size' => 50000,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
[
|
||||
'key' => 'slug_unique',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['slug'],
|
||||
],
|
||||
[
|
||||
'key' => 'body_fulltext',
|
||||
'type' => 'fulltext',
|
||||
'attributes' => ['body'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(3, $table['body']['columns']);
|
||||
$this->assertCount(3, $table['body']['indexes']);
|
||||
|
||||
$indexKeys = array_column($table['body']['indexes'], 'key');
|
||||
$this->assertContains('title_idx', $indexKeys);
|
||||
$this->assertContains('slug_unique', $indexKeys);
|
||||
$this->assertContains('body_fulltext', $indexKeys);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithEmptyArrays(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestEmptyArrays',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [],
|
||||
'indexes' => [],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertEquals('TestEmptyArrays', $table['body']['name']);
|
||||
$this->assertCount(0, $table['body']['columns']);
|
||||
$this->assertCount(0, $table['body']['indexes']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithIntegerMinMax(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestIntMinMax',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'temperature',
|
||||
'type' => 'integer',
|
||||
'required' => false,
|
||||
'min' => -50,
|
||||
'max' => 50,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(1, $table['body']['columns']);
|
||||
$this->assertEquals('temperature', $table['body']['columns'][0]['key']);
|
||||
$this->assertEquals(-50, $table['body']['columns'][0]['min']);
|
||||
$this->assertEquals(50, $table['body']['columns'][0]['max']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithFloatMinMax(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestFloatMinMax',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'rating',
|
||||
'type' => 'float',
|
||||
'required' => false,
|
||||
'min' => 0.0,
|
||||
'max' => 5.0,
|
||||
'default' => 0.0,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(1, $table['body']['columns']);
|
||||
$this->assertEquals('rating', $table['body']['columns'][0]['key']);
|
||||
$this->assertEquals(0.0, $table['body']['columns'][0]['min']);
|
||||
$this->assertEquals(5.0, $table['body']['columns'][0]['max']);
|
||||
$this->assertEquals(0.0, $table['body']['columns'][0]['default']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableInvalidColumnType(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestInvalidType',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'invalid',
|
||||
'type' => 'invalid_type',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableInvalidIndexType(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestInvalidIndex',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'invalid_idx',
|
||||
'type' => 'invalid_type',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableMissingColumnKey(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestMissingKey',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'type' => 'string',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableMissingColumnType(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestMissingType',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'field',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableStringSizeMissing(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestMissingSize',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableIndexOnNonExistentColumn(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestBadIndexCol',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'nonexistent_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['nonexistent_field'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableDuplicateColumnKeys(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestDuplicateKeys',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 100,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableDuplicateIndexKeys(array $data): void
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestDuplicateIndexKeys',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'title',
|
||||
'type' => 'string',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
[
|
||||
'key' => 'title_idx',
|
||||
'type' => 'unique',
|
||||
'attributes' => ['title'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $table['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
*/
|
||||
public function testCreateTableWithIndexLengths(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'TestIndexLengths',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
'columns' => [
|
||||
[
|
||||
'key' => 'longText',
|
||||
'type' => 'string',
|
||||
'size' => 10000,
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'key' => 'text_idx',
|
||||
'type' => 'key',
|
||||
'attributes' => ['longText'],
|
||||
'lengths' => [255],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$this->assertCount(1, $table['body']['indexes']);
|
||||
$this->assertEquals([255], $table['body']['indexes'][0]['lengths']);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $table['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateTable
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user