diff --git a/app/config/collections2.php b/app/config/collections2.php index 052ba7cd00..c3e9bea468 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -214,7 +214,7 @@ $collections = [ 'required' => false, 'default' => new stdClass, 'array' => false, - 'filters' => ['json', 'range'], + 'filters' => ['json', 'range', 'enum'], ], [ '$id' => 'filters', diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index d3fd2ab68b..7187b75110 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -749,6 +749,61 @@ App::post('/v1/database/collections/:collectionId/attributes/email') $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); }); +App::post('/v1/database/collections/:collectionId/attributes/enum') + ->desc('Create Enum Attribute') + ->groups(['api', 'database']) + ->label('event', 'database.attributes.create') + ->label('scope', 'collections.write') + ->label('sdk.namespace', 'database') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.method', 'createEnumAttribute') + ->label('sdk.description', '/docs/references/database/create-attribute-enum.md') + ->label('sdk.response.code', Response::STATUS_CODE_CREATED) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_ENUM) + ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') + ->param('attributeId', '', new Key(), 'Attribute ID.') + ->param('elements', [], new ArrayList(new Text(0)), 'Array of elements in enumerated type. Uses length of longest element to determine size.') + ->param('required', null, new Boolean(), 'Is attribute required?') + ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('array', false, new Boolean(), 'Is attribute an array?', true) + ->inject('response') + ->inject('dbForInternal') + ->inject('database') + ->inject('audits') + ->inject('usage') + ->action(function ($collectionId, $attributeId, $elements, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForInternal*/ + /** @var Appwrite\Event\Event $database */ + /** @var Appwrite\Event\Event $audits */ + /** @var Appwrite\Stats\Stats $usage */ + + // use length of longest string as attribute size + $size = 0; + foreach ($elements as $element) { + $length = \strlen($element); + if ($length === 0) { + throw new Exception('Each enum element must not be empty', 400); + + } + $size = ($length > $size) ? $length : $size; + } + + $attribute = createAttribute($collectionId, new Document([ + '$id' => $attributeId, + 'type' => Database::VAR_STRING, + 'size' => $size, + 'required' => $required, + 'default' => $default, + 'array' => $array, + 'format' => APP_DATABASE_ATTRIBUTE_ENUM, + 'formatOptions' => ['elements' => $elements], + ]), $response, $dbForInternal, $database, $audits, $usage); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); + }); + App::post('/v1/database/collections/:collectionId/attributes/ip') ->desc('Create IP Address Attribute') ->groups(['api', 'database']) @@ -1052,6 +1107,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') Response::MODEL_ATTRIBUTE_INTEGER, Response::MODEL_ATTRIBUTE_FLOAT, Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_ENUM, Response::MODEL_ATTRIBUTE_URL, Response::MODEL_ATTRIBUTE_IP, Response::MODEL_ATTRIBUTE_STRING,])// needs to be last, since its condition would dominate any other string attribute @@ -1086,6 +1142,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, Database::VAR_STRING => match($format) { APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_ENUM => Response::MODEL_ATTRIBUTE_ENUM, APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, default => Response::MODEL_ATTRIBUTE_STRING, @@ -1559,6 +1616,7 @@ App::get('/v1/database/collections/:collectionId/documents') throw new Exception($validator->getDescription(), 400); } + $cursorDocument = null; if (!empty($cursor)) { $cursorDocument = $dbForExternal->getDocument($collectionId, $cursor); diff --git a/app/init.php b/app/init.php index 71f8c9c56b..d71cd6757a 100644 --- a/app/init.php +++ b/app/init.php @@ -46,6 +46,7 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Structure; use Utopia\Database\Validator\Authorization; use Utopia\Validator\Range; +use Utopia\Validator\WhiteList; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; @@ -65,6 +66,7 @@ const APP_LIMIT_USERS = 10000; const APP_CACHE_BUSTER = 151; const APP_VERSION_STABLE = '0.11.0'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; +const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; const APP_DATABASE_ATTRIBUTE_URL = 'url'; const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; @@ -199,6 +201,22 @@ Database::addFilter('casting', } ); +Database::addFilter('enum', + function($value, Document $attribute) { + if ($attribute->isSet('elements')) { + $attribute->removeAttribute('elements'); + } + return $value; + }, + function($value, Document $attribute) { + $formatOptions = json_decode($attribute->getAttribute('formatOptions', []), true); + if (isset($formatOptions['elements'])) { + $attribute->setAttribute('elements', $formatOptions['elements']); + } + return $value; + } +); + Database::addFilter('range', function($value, Document $attribute) { if ($attribute->isSet('min')) { @@ -321,6 +339,11 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function() { return new Email(); }, Database::VAR_STRING); +Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function($attribute) { + $elements = $attribute['formatOptions']['elements']; + return new WhiteList($elements); +}, Database::VAR_STRING); + Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function() { return new IP(); }, Database::VAR_STRING); diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index f92023d8dd..61511138fa 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -17,6 +17,7 @@ use Appwrite\Utopia\Response\Model\AttributeInteger; use Appwrite\Utopia\Response\Model\AttributeFloat; use Appwrite\Utopia\Response\Model\AttributeBoolean; use Appwrite\Utopia\Response\Model\AttributeEmail; +use Appwrite\Utopia\Response\Model\AttributeEnum; use Appwrite\Utopia\Response\Model\AttributeIP; use Appwrite\Utopia\Response\Model\AttributeURL; use Appwrite\Utopia\Response\Model\BaseList; @@ -95,11 +96,12 @@ class Response extends SwooleResponse const MODEL_ATTRIBUTE = 'attribute'; const MODEL_ATTRIBUTE_LIST = 'attributeList'; const MODEL_ATTRIBUTE_STRING = 'attributeString'; - const MODEL_ATTRIBUTE_INTEGER= 'attributeInteger'; - const MODEL_ATTRIBUTE_FLOAT= 'attributeFloat'; - const MODEL_ATTRIBUTE_BOOLEAN= 'attributeBoolean'; - const MODEL_ATTRIBUTE_EMAIL= 'attributeEmail'; - const MODEL_ATTRIBUTE_IP= 'attributeIp'; + const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger'; + const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat'; + const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean'; + const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail'; + const MODEL_ATTRIBUTE_ENUM = 'attributeEnum'; + const MODEL_ATTRIBUTE_IP = 'attributeIp'; const MODEL_ATTRIBUTE_URL= 'attributeUrl'; // Users @@ -219,6 +221,7 @@ class Response extends SwooleResponse ->setModel(new AttributeFloat()) ->setModel(new AttributeBoolean()) ->setModel(new AttributeEmail()) + ->setModel(new AttributeEnum()) ->setModel(new AttributeIP()) ->setModel(new AttributeURL()) ->setModel(new Index()) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEnum.php b/src/Appwrite/Utopia/Response/Model/AttributeEnum.php new file mode 100644 index 0000000000..d04ac7491a --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeEnum.php @@ -0,0 +1,66 @@ +addRule('elements', [ + 'type' => self::TYPE_STRING, + 'description' => 'Array of elements in enumerated type.', + 'default' => null, + 'example' => 'element', + 'array' => true, + 'require' => true, + ]) + ->addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => APP_DATABASE_ATTRIBUTE_ENUM, + 'example' => APP_DATABASE_ATTRIBUTE_ENUM, + 'array' => false, + 'require' => true, + ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => 'element', + 'array' => false, + 'require' => false, + ]) + ; + } + + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_ENUM + ]; + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'AttributeEnum'; + } + + /** + * Get Type + * + * @return string + */ + public function getType():string + { + return Response::MODEL_ATTRIBUTE_ENUM; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php index 0cf67b3bd2..bbb5d8001a 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -23,6 +23,7 @@ class AttributeList extends Model Response::MODEL_ATTRIBUTE_INTEGER, Response::MODEL_ATTRIBUTE_FLOAT, Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_ENUM, Response::MODEL_ATTRIBUTE_URL, Response::MODEL_ATTRIBUTE_IP, Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index b2a05846c5..2ed6522211 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -48,6 +48,7 @@ class Collection extends Model Response::MODEL_ATTRIBUTE_INTEGER, Response::MODEL_ATTRIBUTE_FLOAT, Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_ENUM, Response::MODEL_ATTRIBUTE_URL, Response::MODEL_ATTRIBUTE_IP, Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 29cc82825e..216b8c0831 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -144,6 +144,17 @@ trait DatabaseBase 'default' => 'default@example.com', ]); + $enum = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'enum', + 'elements' => ['yes', 'no', 'maybe'], + 'required' => false, + 'default' => 'maybe', + ]); + $ip = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/ip', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -216,6 +227,17 @@ trait DatabaseBase $this->assertEquals('email', $email['body']['format']); $this->assertEquals('default@example.com', $email['body']['default']); + $this->assertEquals(201, $enum['headers']['status-code']); + $this->assertEquals('enum', $enum['body']['key']); + $this->assertEquals('string', $enum['body']['type']); + $this->assertEquals('processing', $enum['body']['status']); + $this->assertEquals(false, $enum['body']['required']); + $this->assertEquals(false, $enum['body']['array']); + $this->assertEquals('enum', $enum['body']['format']); + $this->assertEquals('maybe', $enum['body']['default']); + $this->assertIsArray($enum['body']['elements']); + $this->assertEquals(['yes', 'no', 'maybe'], $enum['body']['elements']); + $this->assertEquals(201, $ip['headers']['status-code']); $this->assertEquals('ip', $ip['body']['key']); $this->assertEquals('string', $ip['body']['type']); @@ -277,6 +299,12 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); + $enumResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$enum['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + $ipResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$ip['body']['key']}",array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -325,6 +353,16 @@ trait DatabaseBase $this->assertEquals($email['body']['format'], $emailResponse['body']['format']); $this->assertEquals($email['body']['default'], $emailResponse['body']['default']); + $this->assertEquals(200, $enumResponse['headers']['status-code']); + $this->assertEquals($enum['body']['key'], $enumResponse['body']['key']); + $this->assertEquals($enum['body']['type'], $enumResponse['body']['type']); + $this->assertEquals('available', $enumResponse['body']['status']); + $this->assertEquals($enum['body']['required'], $enumResponse['body']['required']); + $this->assertEquals($enum['body']['array'], $enumResponse['body']['array']); + $this->assertEquals($enum['body']['format'], $enumResponse['body']['format']); + $this->assertEquals($enum['body']['default'], $enumResponse['body']['default']); + $this->assertEquals($enum['body']['elements'], $enumResponse['body']['elements']); + $this->assertEquals(200, $ipResponse['headers']['status-code']); $this->assertEquals($ip['body']['key'], $ipResponse['body']['key']); $this->assertEquals($ip['body']['type'], $ipResponse['body']['type']); @@ -378,12 +416,12 @@ trait DatabaseBase ])); $this->assertEquals(200, $attributes['headers']['status-code']); - $this->assertEquals(7, $attributes['body']['sum']); + $this->assertEquals(8, $attributes['body']['sum']); $attributes = $attributes['body']['attributes']; $this->assertIsArray($attributes); - $this->assertCount(7, $attributes); + $this->assertCount(8, $attributes); $this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']); $this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']); @@ -401,46 +439,55 @@ trait DatabaseBase $this->assertEquals($emailResponse['body']['default'], $attributes[1]['default']); $this->assertEquals($emailResponse['body']['format'], $attributes[1]['format']); - $this->assertEquals($ipResponse['body']['key'], $attributes[2]['key']); - $this->assertEquals($ipResponse['body']['type'], $attributes[2]['type']); - $this->assertEquals($ipResponse['body']['status'], $attributes[2]['status']); - $this->assertEquals($ipResponse['body']['required'], $attributes[2]['required']); - $this->assertEquals($ipResponse['body']['array'], $attributes[2]['array']); - $this->assertEquals($ipResponse['body']['default'], $attributes[2]['default']); - $this->assertEquals($ipResponse['body']['format'], $attributes[2]['format']); + $this->assertEquals($enumResponse['body']['key'], $attributes[2]['key']); + $this->assertEquals($enumResponse['body']['type'], $attributes[2]['type']); + $this->assertEquals($enumResponse['body']['status'], $attributes[2]['status']); + $this->assertEquals($enumResponse['body']['required'], $attributes[2]['required']); + $this->assertEquals($enumResponse['body']['array'], $attributes[2]['array']); + $this->assertEquals($enumResponse['body']['default'], $attributes[2]['default']); + $this->assertEquals($enumResponse['body']['format'], $attributes[2]['format']); + $this->assertEquals($enumResponse['body']['elements'], $attributes[2]['elements']); - $this->assertEquals($urlResponse['body']['key'], $attributes[3]['key']); - $this->assertEquals($urlResponse['body']['type'], $attributes[3]['type']); - $this->assertEquals($urlResponse['body']['status'], $attributes[3]['status']); - $this->assertEquals($urlResponse['body']['required'], $attributes[3]['required']); - $this->assertEquals($urlResponse['body']['array'], $attributes[3]['array']); - $this->assertEquals($urlResponse['body']['default'], $attributes[3]['default']); - $this->assertEquals($urlResponse['body']['format'], $attributes[3]['format']); + $this->assertEquals($ipResponse['body']['key'], $attributes[3]['key']); + $this->assertEquals($ipResponse['body']['type'], $attributes[3]['type']); + $this->assertEquals($ipResponse['body']['status'], $attributes[3]['status']); + $this->assertEquals($ipResponse['body']['required'], $attributes[3]['required']); + $this->assertEquals($ipResponse['body']['array'], $attributes[3]['array']); + $this->assertEquals($ipResponse['body']['default'], $attributes[3]['default']); + $this->assertEquals($ipResponse['body']['format'], $attributes[3]['format']); - $this->assertEquals($integerResponse['body']['key'], $attributes[4]['key']); - $this->assertEquals($integerResponse['body']['type'], $attributes[4]['type']); - $this->assertEquals($integerResponse['body']['status'], $attributes[4]['status']); - $this->assertEquals($integerResponse['body']['required'], $attributes[4]['required']); - $this->assertEquals($integerResponse['body']['array'], $attributes[4]['array']); - $this->assertEquals($integerResponse['body']['default'], $attributes[4]['default']); - $this->assertEquals($integerResponse['body']['min'], $attributes[4]['min']); - $this->assertEquals($integerResponse['body']['max'], $attributes[4]['max']); + $this->assertEquals($urlResponse['body']['key'], $attributes[4]['key']); + $this->assertEquals($urlResponse['body']['type'], $attributes[4]['type']); + $this->assertEquals($urlResponse['body']['status'], $attributes[4]['status']); + $this->assertEquals($urlResponse['body']['required'], $attributes[4]['required']); + $this->assertEquals($urlResponse['body']['array'], $attributes[4]['array']); + $this->assertEquals($urlResponse['body']['default'], $attributes[4]['default']); + $this->assertEquals($urlResponse['body']['format'], $attributes[4]['format']); - $this->assertEquals($floatResponse['body']['key'], $attributes[5]['key']); - $this->assertEquals($floatResponse['body']['type'], $attributes[5]['type']); - $this->assertEquals($floatResponse['body']['status'], $attributes[5]['status']); - $this->assertEquals($floatResponse['body']['required'], $attributes[5]['required']); - $this->assertEquals($floatResponse['body']['array'], $attributes[5]['array']); - $this->assertEquals($floatResponse['body']['default'], $attributes[5]['default']); - $this->assertEquals($floatResponse['body']['min'], $attributes[5]['min']); - $this->assertEquals($floatResponse['body']['max'], $attributes[5]['max']); + $this->assertEquals($integerResponse['body']['key'], $attributes[5]['key']); + $this->assertEquals($integerResponse['body']['type'], $attributes[5]['type']); + $this->assertEquals($integerResponse['body']['status'], $attributes[5]['status']); + $this->assertEquals($integerResponse['body']['required'], $attributes[5]['required']); + $this->assertEquals($integerResponse['body']['array'], $attributes[5]['array']); + $this->assertEquals($integerResponse['body']['default'], $attributes[5]['default']); + $this->assertEquals($integerResponse['body']['min'], $attributes[5]['min']); + $this->assertEquals($integerResponse['body']['max'], $attributes[5]['max']); - $this->assertEquals($booleanResponse['body']['key'], $attributes[6]['key']); - $this->assertEquals($booleanResponse['body']['type'], $attributes[6]['type']); - $this->assertEquals($booleanResponse['body']['status'], $attributes[6]['status']); - $this->assertEquals($booleanResponse['body']['required'], $attributes[6]['required']); - $this->assertEquals($booleanResponse['body']['array'], $attributes[6]['array']); - $this->assertEquals($booleanResponse['body']['default'], $attributes[6]['default']); + $this->assertEquals($floatResponse['body']['key'], $attributes[6]['key']); + $this->assertEquals($floatResponse['body']['type'], $attributes[6]['type']); + $this->assertEquals($floatResponse['body']['status'], $attributes[6]['status']); + $this->assertEquals($floatResponse['body']['required'], $attributes[6]['required']); + $this->assertEquals($floatResponse['body']['array'], $attributes[6]['array']); + $this->assertEquals($floatResponse['body']['default'], $attributes[6]['default']); + $this->assertEquals($floatResponse['body']['min'], $attributes[6]['min']); + $this->assertEquals($floatResponse['body']['max'], $attributes[6]['max']); + + $this->assertEquals($booleanResponse['body']['key'], $attributes[7]['key']); + $this->assertEquals($booleanResponse['body']['type'], $attributes[7]['type']); + $this->assertEquals($booleanResponse['body']['status'], $attributes[7]['status']); + $this->assertEquals($booleanResponse['body']['required'], $attributes[7]['required']); + $this->assertEquals($booleanResponse['body']['array'], $attributes[7]['array']); + $this->assertEquals($booleanResponse['body']['default'], $attributes[7]['default']); $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -453,7 +500,7 @@ trait DatabaseBase $attributes = $collection['body']['attributes']; $this->assertIsArray($attributes); - $this->assertCount(7, $attributes); + $this->assertCount(8, $attributes); $this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']); $this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']); @@ -471,46 +518,72 @@ trait DatabaseBase $this->assertEquals($emailResponse['body']['default'], $attributes[1]['default']); $this->assertEquals($emailResponse['body']['format'], $attributes[1]['format']); - $this->assertEquals($ipResponse['body']['key'], $attributes[2]['key']); - $this->assertEquals($ipResponse['body']['type'], $attributes[2]['type']); - $this->assertEquals($ipResponse['body']['status'], $attributes[2]['status']); - $this->assertEquals($ipResponse['body']['required'], $attributes[2]['required']); - $this->assertEquals($ipResponse['body']['array'], $attributes[2]['array']); - $this->assertEquals($ipResponse['body']['default'], $attributes[2]['default']); - $this->assertEquals($ipResponse['body']['format'], $attributes[2]['format']); + $this->assertEquals($enumResponse['body']['key'], $attributes[2]['key']); + $this->assertEquals($enumResponse['body']['type'], $attributes[2]['type']); + $this->assertEquals($enumResponse['body']['status'], $attributes[2]['status']); + $this->assertEquals($enumResponse['body']['required'], $attributes[2]['required']); + $this->assertEquals($enumResponse['body']['array'], $attributes[2]['array']); + $this->assertEquals($enumResponse['body']['default'], $attributes[2]['default']); + $this->assertEquals($enumResponse['body']['format'], $attributes[2]['format']); + $this->assertEquals($enumResponse['body']['elements'], $attributes[2]['elements']); - $this->assertEquals($urlResponse['body']['key'], $attributes[3]['key']); - $this->assertEquals($urlResponse['body']['type'], $attributes[3]['type']); - $this->assertEquals($urlResponse['body']['status'], $attributes[3]['status']); - $this->assertEquals($urlResponse['body']['required'], $attributes[3]['required']); - $this->assertEquals($urlResponse['body']['array'], $attributes[3]['array']); - $this->assertEquals($urlResponse['body']['default'], $attributes[3]['default']); - $this->assertEquals($urlResponse['body']['format'], $attributes[3]['format']); + $this->assertEquals($ipResponse['body']['key'], $attributes[3]['key']); + $this->assertEquals($ipResponse['body']['type'], $attributes[3]['type']); + $this->assertEquals($ipResponse['body']['status'], $attributes[3]['status']); + $this->assertEquals($ipResponse['body']['required'], $attributes[3]['required']); + $this->assertEquals($ipResponse['body']['array'], $attributes[3]['array']); + $this->assertEquals($ipResponse['body']['default'], $attributes[3]['default']); + $this->assertEquals($ipResponse['body']['format'], $attributes[3]['format']); - $this->assertEquals($integerResponse['body']['key'], $attributes[4]['key']); - $this->assertEquals($integerResponse['body']['type'], $attributes[4]['type']); - $this->assertEquals($integerResponse['body']['status'], $attributes[4]['status']); - $this->assertEquals($integerResponse['body']['required'], $attributes[4]['required']); - $this->assertEquals($integerResponse['body']['array'], $attributes[4]['array']); - $this->assertEquals($integerResponse['body']['default'], $attributes[4]['default']); - $this->assertEquals($integerResponse['body']['min'], $attributes[4]['min']); - $this->assertEquals($integerResponse['body']['max'], $attributes[4]['max']); + $this->assertEquals($urlResponse['body']['key'], $attributes[4]['key']); + $this->assertEquals($urlResponse['body']['type'], $attributes[4]['type']); + $this->assertEquals($urlResponse['body']['status'], $attributes[4]['status']); + $this->assertEquals($urlResponse['body']['required'], $attributes[4]['required']); + $this->assertEquals($urlResponse['body']['array'], $attributes[4]['array']); + $this->assertEquals($urlResponse['body']['default'], $attributes[4]['default']); + $this->assertEquals($urlResponse['body']['format'], $attributes[4]['format']); - $this->assertEquals($floatResponse['body']['key'], $attributes[5]['key']); - $this->assertEquals($floatResponse['body']['type'], $attributes[5]['type']); - $this->assertEquals($floatResponse['body']['status'], $attributes[5]['status']); - $this->assertEquals($floatResponse['body']['required'], $attributes[5]['required']); - $this->assertEquals($floatResponse['body']['array'], $attributes[5]['array']); - $this->assertEquals($floatResponse['body']['default'], $attributes[5]['default']); - $this->assertEquals($floatResponse['body']['min'], $attributes[5]['min']); - $this->assertEquals($floatResponse['body']['max'], $attributes[5]['max']); + $this->assertEquals($integerResponse['body']['key'], $attributes[5]['key']); + $this->assertEquals($integerResponse['body']['type'], $attributes[5]['type']); + $this->assertEquals($integerResponse['body']['status'], $attributes[5]['status']); + $this->assertEquals($integerResponse['body']['required'], $attributes[5]['required']); + $this->assertEquals($integerResponse['body']['array'], $attributes[5]['array']); + $this->assertEquals($integerResponse['body']['default'], $attributes[5]['default']); + $this->assertEquals($integerResponse['body']['min'], $attributes[5]['min']); + $this->assertEquals($integerResponse['body']['max'], $attributes[5]['max']); - $this->assertEquals($booleanResponse['body']['key'], $attributes[6]['key']); - $this->assertEquals($booleanResponse['body']['type'], $attributes[6]['type']); - $this->assertEquals($booleanResponse['body']['status'], $attributes[6]['status']); - $this->assertEquals($booleanResponse['body']['required'], $attributes[6]['required']); - $this->assertEquals($booleanResponse['body']['array'], $attributes[6]['array']); - $this->assertEquals($booleanResponse['body']['default'], $attributes[6]['default']); + $this->assertEquals($floatResponse['body']['key'], $attributes[6]['key']); + $this->assertEquals($floatResponse['body']['type'], $attributes[6]['type']); + $this->assertEquals($floatResponse['body']['status'], $attributes[6]['status']); + $this->assertEquals($floatResponse['body']['required'], $attributes[6]['required']); + $this->assertEquals($floatResponse['body']['array'], $attributes[6]['array']); + $this->assertEquals($floatResponse['body']['default'], $attributes[6]['default']); + $this->assertEquals($floatResponse['body']['min'], $attributes[6]['min']); + $this->assertEquals($floatResponse['body']['max'], $attributes[6]['max']); + + $this->assertEquals($booleanResponse['body']['key'], $attributes[7]['key']); + $this->assertEquals($booleanResponse['body']['type'], $attributes[7]['type']); + $this->assertEquals($booleanResponse['body']['status'], $attributes[7]['status']); + $this->assertEquals($booleanResponse['body']['required'], $attributes[7]['required']); + $this->assertEquals($booleanResponse['body']['array'], $attributes[7]['array']); + $this->assertEquals($booleanResponse['body']['default'], $attributes[7]['default']); + + /** + * Test for FAILURE + */ + $badEnum = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'enum', + 'elements' => ['yes', 'no', ''], + 'required' => false, + 'default' => 'maybe', + ]); + + $this->assertEquals(400, $badEnum['headers']['status-code']); + $this->assertEquals('Each enum element must not be empty', $badEnum['body']['message']); return $data; } @@ -1176,6 +1249,16 @@ trait DatabaseBase 'required' => false, ]); + $enum = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'enum', + 'elements' => ['yes', 'no', 'maybe'], + 'required' => false, + ]); + $ip = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/ip', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1284,7 +1367,7 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'], ]), []); - $this->assertCount(7, $collection['body']['attributes']); + $this->assertCount(8, $collection['body']['attributes']); /** * Test for successful validation @@ -1302,6 +1385,18 @@ trait DatabaseBase 'write' => ['user:'.$this->getUser()['$id']], ]); + $goodEnum = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'unique()', + 'data' => [ + 'enum' => 'yes', + ], + 'read' => ['user:'.$this->getUser()['$id']], + 'write' => ['user:'.$this->getUser()['$id']], + ]); + $goodIp = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1375,6 +1470,7 @@ trait DatabaseBase ]); $this->assertEquals(201, $goodEmail['headers']['status-code']); + $this->assertEquals(201, $goodEnum['headers']['status-code']); $this->assertEquals(201, $goodIp['headers']['status-code']); $this->assertEquals(201, $goodUrl['headers']['status-code']); $this->assertEquals(201, $goodRange['headers']['status-code']); @@ -1398,6 +1494,18 @@ trait DatabaseBase 'write' => ['user:'.$this->getUser()['$id']], ]); + $badEnum = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'unique()', + 'data' => [ + 'enum' => 'badEnum', + ], + 'read' => ['user:'.$this->getUser()['$id']], + 'write' => ['user:'.$this->getUser()['$id']], + ]); + $badIp = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1471,6 +1579,7 @@ trait DatabaseBase ]); $this->assertEquals(400, $badEmail['headers']['status-code']); + $this->assertEquals(400, $badEnum['headers']['status-code']); $this->assertEquals(400, $badIp['headers']['status-code']); $this->assertEquals(400, $badUrl['headers']['status-code']); $this->assertEquals(400, $badRange['headers']['status-code']); @@ -1478,6 +1587,7 @@ trait DatabaseBase $this->assertEquals(400, $tooHigh['headers']['status-code']); $this->assertEquals(400, $tooLow['headers']['status-code']); $this->assertEquals('Invalid document structure: Attribute "email" has invalid format. Value must be a valid email address', $badEmail['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "enum" has invalid format. Value must be one of (yes, no, maybe)', $badEnum['body']['message']); $this->assertEquals('Invalid document structure: Attribute "ip" has invalid format. Value must be a valid IP address', $badIp['body']['message']); $this->assertEquals('Invalid document structure: Attribute "url" has invalid format. Value must be a valid URL', $badUrl['body']['message']); $this->assertEquals('Invalid document structure: Attribute "range" has invalid format. Value must be a valid range between 1 and 10', $badRange['body']['message']);