Merge branch 'response-refactor' of github.com:appwrite/appwrite into graphql

This commit is contained in:
Eldad Fux
2020-06-23 23:55:18 +03:00
22 changed files with 609 additions and 95 deletions
+7
View File
@@ -18,6 +18,11 @@
- Upgraded Redis Resque queue library to version 1.3.6
- Added container names to docker-compose.yml (@drandell)
- Upgraded ClamAV container image to version 1.0.9
- Optimised function execution by using fully-qualified function calls
- New and consistent response format for all API object + new response examples in the docs
- Removed user roles attribute from user object (can be fetched from /v1/teams/memberships) **
- Removed type attribute from session object response (used only internally)
- ** - might be changed before merging to master
## Bug Fixes
@@ -31,6 +36,8 @@
- Fixed update form labels and tooltips for Flutter Android apps
- Fixed missing custom scopes param for OAuth2 session create API route
- Fixed wrong JSON validation when creating and updating database documnets
- Fixed bug where max file size was limited to max of 10MB
- Fixed bug preventing the deletion of the project logo
## Security
+2 -3
View File
@@ -94,7 +94,7 @@ RUN \
add-apt-repository ppa:certbot/certbot && \
apt-get update && \
apt-get install -y --no-install-recommends --no-install-suggests php$PHP_VERSION php$PHP_VERSION-fpm \
php$PHP_VERSION-mysqlnd php$PHP_VERSION-curl php$PHP_VERSION-imagick php$PHP_VERSION-mbstring php$PHP_VERSION-dom webp certbot && \
php$PHP_VERSION-mysqlnd php$PHP_VERSION-curl php$PHP_VERSION-imagick php$PHP_VERSION-mbstring php$PHP_VERSION-dom php$PHP_VERSION-yaml webp certbot && \
# Nginx
wget http://nginx.org/download/nginx-1.19.0.tar.gz && \
tar -xzvf nginx-1.19.0.tar.gz && rm nginx-1.19.0.tar.gz && \
@@ -130,13 +130,12 @@ RUN \
# Set Upload Limit (default to 100MB)
RUN echo "upload_max_filesize = ${_APP_STORAGE_LIMIT}" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "post_max_size = ${_APP_STORAGE_LIMIT}" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "env[TESTME] = your-secret-key" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
# Add logs file
RUN echo "" >> /var/log/appwrite.log
# Nginx Configuration (with self-signed ssl certificates)
COPY ./docker/nginx.conf /etc/nginx/nginx.conf
COPY ./docker/nginx.conf.template /etc/nginx/nginx.conf.template
COPY ./docker/ssl/cert.pem /etc/nginx/ssl/cert.pem
COPY ./docker/ssl/key.pem /etc/nginx/ssl/key.pem
+4 -5
View File
@@ -17,6 +17,7 @@ use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Response;
/*
* Configuration files
@@ -270,7 +271,6 @@ $utopia->options(function () use ($request, $response) {
});
$utopia->error(function ($error /* @var $error Exception */) use ($request, $response, $utopia, $project) {
$env = Config::getParam('env');
$version = Config::getParam('version');
switch ($error->getCode()) {
@@ -292,7 +292,7 @@ $utopia->error(function ($error /* @var $error Exception */) use ($request, $res
$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
$output = ((App::MODE_TYPE_DEVELOPMENT == $env)) ? [
$output = ($utopia->isDevelopment()) ? [
'message' => $error->getMessage(),
'code' => $error->getCode(),
'file' => $error->getFile(),
@@ -337,9 +337,8 @@ $utopia->error(function ($error /* @var $error Exception */) use ($request, $res
$response->send($layout->render());
}
$response
->json($output)
;
$response->dynamic(new Document($output),
$utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_LOCALE);
});
$utopia->get('/manifest.json')
+9 -51
View File
@@ -4,7 +4,6 @@ global $utopia, $register, $request, $response, $user, $audit,
$webhook, $mail, $project, $projectDB, $clients;
use Utopia\Exception;
use Utopia\Response;
use Utopia\Config\Config;
use Utopia\Validator\Assoc;
use Utopia\Validator\Text;
@@ -25,6 +24,7 @@ use Appwrite\Database\Validator\Authorization;
use Appwrite\Template\Template;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\URL\URL as URLParser;
use Appwrite\Utopia\Response;
use DeviceDetector\DeviceDetector;
use GeoIp2\Database\Reader;
use Utopia\Validator\ArrayList;
@@ -45,7 +45,6 @@ $utopia->init(function() use (&$oauth2Keys) {
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
});
$utopia->post('/v1/account')
@@ -134,17 +133,8 @@ $utopia->post('/v1/account')
->setParam('resource', 'users/'.$user->getId())
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($user->setAttribute('roles', Authorization::getRoles()), Response::MODEL_USER);
}
);
@@ -233,8 +223,9 @@ $utopia->post('/v1/account/sessions')
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($session->getArrayCopy(['$id', 'type', 'expire']))
;
$response->dynamic($session, Response::MODEL_SESSION);
}
);
@@ -537,16 +528,7 @@ $utopia->get('/v1/account')
->label('sdk.response', ['200' => 'user'])
->action(
function () use ($response, &$user, $oauth2Keys) {
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'emailVerification',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user->setAttribute('roles', Authorization::getRoles()), Response::MODEL_USER);
}
);
@@ -727,15 +709,7 @@ $utopia->patch('/v1/account/name')
->setParam('resource', 'users/'.$user->getId())
;
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user->setAttribute('roles', Authorization::getRoles()), Response::MODEL_USER);
}
);
@@ -769,15 +743,7 @@ $utopia->patch('/v1/account/password')
->setParam('resource', 'users/'.$user->getId())
;
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user->setAttribute('roles', Authorization::getRoles()), Response::MODEL_USER);
}
);
@@ -827,15 +793,7 @@ $utopia->patch('/v1/account/email')
->setParam('resource', 'users/'.$user->getId())
;
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user->setAttribute('roles', Authorization::getRoles()), Response::MODEL_USER);
}
);
+1 -20
View File
@@ -115,25 +115,6 @@ $utopia->get('/v1/database/collections')
->param('orderType', 'ASC', function () { return new WhiteList(['ASC', 'DESC']); }, 'Order result by ASC or DESC order.', true)
->action(
function ($search, $limit, $offset, $orderType) use ($response, $projectDB) {
/*$vl = new Structure($projectDB);
var_dump($vl->isValid(new Document([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => ['*'],
'write' => ['*'],
],
'label' => 'Platforms',
'key' => 'platforms',
'type' => 'document',
'default' => [],
'required' => false,
'array' => true,
'options' => [Database::SYSTEM_COLLECTION_PLATFORMS],
])));
var_dump($vl->getDescription());*/
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
@@ -357,7 +338,7 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('parentDocument', '', function () { return new UID(); }, 'Parent document unique ID. Use when you want your new document to be a child of a parent document.', true)
->param('parentProperty', '', function () { return new Key(); }, 'Parent document property name. Use when you want your new document to be a child of a parent document.', true)
->param('parentPropertyType', Document::SET_TYPE_ASSIGN, function () { return new WhiteList([Document::SET_TYPE_ASSIGN, Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND]); }, 'Parent document property connection type. You can set this value to **assign**, **append** or **prepend**, default value is assign. Use when you want your new document to be a child of a parent document.', true)
->param('parentPropertyType', Document::SET_TYPE_ASSIGN, function () { return new WhiteList([Document::SET_TYPE_ASSIGN, Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND]); }, 'Parent document property connection type. You can set this value to **assign**, **append** or **prepend**, default value is assign. **append** or **prepend** should be set when the parent property is array. Use when you want your new document to be a child of a parent document.', true)
->action(
function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType) use ($response, $projectDB, $webhook, $audit) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
+6 -2
View File
@@ -2,6 +2,8 @@
global $utopia, $register, $request, $response, $projectDB, $project, $user, $audit;
use Appwrite\Database\Document;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Locale\Locale;
use GeoIp2\Database\Reader;
@@ -43,7 +45,7 @@ $utopia->get('/v1/locale')
$output['continentCode'] = $record->continent->code;
$output['eu'] = (\in_array($record->country->isoCode, $eu)) ? true : false;
foreach ($currencies as $code => $element) {
foreach ($currencies as $element) {
if (isset($element['locations']) && isset($element['code']) && \in_array($record->country->isoCode, $element['locations'])) {
$currency = $element['code'];
}
@@ -62,7 +64,9 @@ $utopia->get('/v1/locale')
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->json($output);
;
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
}
);
+3 -11
View File
@@ -3,7 +3,6 @@
global $utopia, $response, $projectDB;
use Utopia\Exception;
use Utopia\Response;
use Utopia\Validator\Assoc;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Email;
@@ -18,6 +17,7 @@ use Appwrite\Auth\Validator\Password;
use Appwrite\Database\Database;
use Appwrite\Database\Exception\Duplicate;
use Appwrite\Database\Validator\UID;
use Appwrite\Utopia\Response;
use DeviceDetector\DeviceDetector;
use GeoIp2\Database\Reader;
@@ -79,16 +79,8 @@ $utopia->post('/v1/users')
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
], $oauth2Keys)), ['roles' => []]));
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($user, Response::MODEL_USER);
}
);
+1 -1
View File
@@ -13,7 +13,6 @@ if (\file_exists(__DIR__.'/../vendor/autoload.php')) {
use Utopia\App;
use Utopia\Request;
use Utopia\Response;
use Utopia\Config\Config;
use Utopia\Locale\Locale;
use Utopia\Registry\Registry;
@@ -23,6 +22,7 @@ use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
const APP_NAME = 'Appwrite';
+2
View File
@@ -4,6 +4,8 @@ export PHP_VERSION=$PHP_VERSION
chown -Rf www-data.www-data /usr/share/nginx/html/
sed 's/%_APP_STORAGE_LIMIT%/'$_APP_STORAGE_LIMIT'/g' /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
# Function to update the fpm configuration to make the service environment variables available
function setEnvironmentVariable() {
if [ -z "$2" ]; then
+1
View File
@@ -23,6 +23,7 @@
"ext-imagick": "*",
"ext-mbstring": "*",
"ext-json": "*",
"ext-yaml": "*",
"ext-dom": "*",
"ext-redis": "*",
"ext-pdo": "*",
-1
View File
@@ -54,7 +54,6 @@ services:
- ./docs:/usr/share/nginx/html/docs
- ./public:/usr/share/nginx/html/public
- ./src:/usr/share/nginx/html/src
- ./docker/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- mariadb
- redis
@@ -16,7 +16,7 @@ http {
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 10M;
client_max_body_size %_APP_STORAGE_LIMIT%;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
+143
View File
@@ -0,0 +1,143 @@
<?php
namespace Appwrite\Utopia;
use Exception;
use Appwrite\Database\Document;
use Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response\Model\Error;
use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\User;
use Appwrite\Utopia\Response\Model\Locale;
use Utopia\Response as UtopiaResponse;
class Response extends UtopiaResponse
{
// General
const MODEL_LOG = 'log';
const MODEL_ERROR = 'error';
const MODEL_ERROR_DEV = 'errorDev';
// Users
const MODEL_USER = 'user';
const MODEL_SESSION = 'session';
const MODEL_TOKEN = 'token';
// Database
const MODEL_COLLECTION = 'collection';
// Locale
const MODEL_LOCALE = 'locale';
const MODEL_COUNTRY = 'country';
const MODEL_CONTINENT = 'continent';
const MODEL_CURRENCY = 'currency';
const MODEL_LANGUAGE = 'langauge';
const MODEL_PHONE = 'phone';
// Storage
const MODEL_FILE = 'file';
const MODEL_BUCKET = 'bucket';
// Teams
const MODEL_TEAM = 'team';
const MODEL_MEMBERSHIP = 'membership';
public function __construct()
{
$this
->setModel(new Error())
->setModel(new ErrorDev())
->setModel(new User())
->setModel(new Locale())
;
}
/**
* HTTP content types
*/
const CONTENT_TYPE_YAML = 'application/x-yaml';
/**
* List of defined output objects
*/
protected $models = [];
/**
* Set Model Object
*
* @return self
*/
public function setModel(Model $instance): self
{
$this->models[$instance->getType()] = $instance;
return $this;
}
/**
* Get Model Object
*
* @return Model
*/
public function getModel(string $key): Model
{
if(!isset($this->models[$key])) {
throw new Exception('Undefined model: '.$key);
}
return $this->models[$key];
}
/**
* Validate response objects and outputs
* the response according to given format type
*/
public function dynamic(Document $document, string $model)
{
$data = $document->getArrayCopy();
$model = $this->getModel($model);
$output = [];
foreach($model->getRules() as $key => $rule) {
if(!isset($data[$key])) {
if(!is_null($rule['default'])) {
$data[$key] = $rule['default'];
}
else {
throw new Exception('Missing response key: '.$key);
}
}
if($rule['array'] && !is_array($data[$key])) {
throw new Exception($key.' must be an array of '.$rule['type'].' types');
}
$output[$key] = $data[$key];
}
return $this->json($output);
//return $this->yaml($output);
}
/**
* YAML
*
* This helper is for sending YAML HTTP response.
* It sets relevant content type header ('application/x-yaml') and convert a PHP array ($data) to valid YAML using native yaml_parse
*
* @see https://en.wikipedia.org/wiki/YAML
*
* @param array $data
*/
public function yaml(array $data)
{
if(!extension_loaded('yaml')) {
throw new Exception('Missing yaml extension. Learn more at: https://www.php.net/manual/en/book.yaml.php');
}
$this
->setContentType(Response::CONTENT_TYPE_YAML)
->send(yaml_emit($data, YAML_UTF8_ENCODING))
;
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace Appwrite\Utopia\Response;
abstract class Model
{
protected $rules = [];
/**
* Get Name
*
* @return string
*/
abstract public function getName():string;
/**
* Get Collection
*
* @return string
*/
abstract public function getType():string;
/**
* Get Rules
*
* @return string
*/
public function getRules(): array
{
return $this->rules;
}
/**
* Add a New Rule
*/
protected function addRule(string $key, array $options): self
{
$this->rules[$key] = array_merge([
'type' => '',
'description' => '',
'default' => null,
'example' => '',
'array' => false,
], $options);
return $this;
}
}
@@ -0,0 +1,50 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Error extends Model
{
public function __construct()
{
$this
->addRule('message', [
'type' => 'string',
'description' => 'Error message.',
'example' => 'Not found',
])
->addRule('code', [
'type' => 'string',
'description' => 'Error code.',
'example' => '404',
])
->addRule('version', [
'type' => 'string',
'description' => 'Server version number.',
'example' => APP_VERSION_STABLE,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Error';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_ERROR;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class ErrorDev extends Error
{
public function __construct()
{
parent::__construct();
$this
->addRule('file', [
'type' => 'string',
'description' => 'File path.',
'example' => '/usr/share/nginx/html/vendor/utopia-php/framework/src/App.php',
])
->addRule('line', [
'type' => 'integer',
'description' => 'Line number.',
'example' => 209,
])
// ->addRule('trace', [
// 'type' => 'string',
// 'description' => 'Error trace.',
// 'example' => [
// ''
// ],
// ])
;
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_ERROR_DEV;
}
}
@@ -0,0 +1,34 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class File extends Model
{
public function __construct()
{
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'File';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_LOCALE;
}
}
@@ -0,0 +1,72 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Locale extends Model
{
public function __construct()
{
$this
->addRule('ip', [
'type' => 'string',
'description' => 'User IP address.',
'example' => '127.0.0.1',
])
->addRule('countryCode', [
'type' => 'string',
'description' => 'Country code in [ISO 3166-1](http://en.wikipedia.org/wiki/ISO_3166-1) two-character format',
'example' => 'US',
])
->addRule('country', [
'type' => 'string',
'description' => 'Country name. This field support localization.',
'example' => 'United States',
])
->addRule('continentCode', [
'type' => 'string',
'description' => 'Continent code. A two character continent code "AF" for Africa, "AN" for Antarctica, "AS" for Asia, "EU" for Europe, "NA" for North America, "OC" for Oceania, and "SA" for South America.',
'example' => 'NA',
])
->addRule('continent', [
'type' => 'string',
'description' => 'Continent name. This field support localization.',
'example' => 'North America',
])
->addRule('eu', [
'type' => 'Boolean',
'description' => 'True if country is part of the Europian Union.',
'default' => false,
'example' => false,
])
->addRule('currency', [
'type' => 'string',
'description' => 'ISO 4217 Email verification status.',
'description' => 'Currency code in [ISO 4217-1](http://en.wikipedia.org/wiki/ISO_4217) three-character format',
'example' => 'USD',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Locale';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_LOCALE;
}
}
@@ -0,0 +1,33 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Log extends Model
{
public function __construct()
{
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Session';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_LOCALE;
}
}
@@ -0,0 +1,33 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Session extends Model
{
public function __construct()
{
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Session';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_LOCALE;
}
}
@@ -0,0 +1,33 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Token extends Model
{
public function __construct()
{
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'User';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_LOCALE;
}
}
@@ -0,0 +1,83 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class User extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'User ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'description' => 'User name.',
'default' => '',
'example' => 'John Doe',
])
->addRule('registration', [
'type' => 'integer',
'description' => 'User registration date in unix timestamp.',
'default' => false,
'example' => true,
])
->addRule('status', [
'type' => 'integer',
'description' => 'User status. 0 for Unavtivated, 1 for active and 2 is blocked.',
'default' => false,
'example' => true,
])
->addRule('email', [
'type' => 'string',
'description' => 'User email address.',
'default' => '',
'example' => 'john@appwrite.io',
])
->addRule('emailVerification', [
'type' => 'boolean',
'description' => 'Email verification status.',
'default' => false,
'example' => true,
])
->addRule('prefs', [
'type' => 'json',
'description' => 'User preferences as a key-value object',
'default' => new \stdClass,
'example' => ['theme' => 'dark', 'timezone' => 'UTC'],
])
->addRule('roles', [
'type' => 'string',
'description' => 'User list of roles',
'default' => [],
'example' => [],
'array' => true,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'User';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USER;
}
}