Merge branch 'response-filters-for-databases' into update-exceptions

This commit is contained in:
Darshan 2025-05-07 11:30:48 +05:30 committed by GitHub
commit 0266376315
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1297 additions and 159 deletions

View file

@ -145,6 +145,56 @@ return [
'$description' => 'This event triggers when a table is updated.',
]
],
'collections' => [
'$model' => Response::MODEL_COLLECTION,
'$resource' => true,
'$description' => 'This event triggers on any collection event.',
'documents' => [
'$model' => Response::MODEL_DOCUMENT,
'$resource' => true,
'$description' => 'This event triggers on any document event.',
'create' => [
'$description' => 'This event triggers when a document is created.',
],
'delete' => [
'$description' => 'This event triggers when a document is deleted.'
],
'update' => [
'$description' => 'This event triggers when a document is updated.'
],
],
'indexes' => [
'$model' => Response::MODEL_INDEX,
'$resource' => true,
'$description' => 'This event triggers on any indexes event.',
'create' => [
'$description' => 'This event triggers when an index is created.',
],
'delete' => [
'$description' => 'This event triggers when an index is deleted.'
]
],
'attributes' => [
'$model' => Response::MODEL_ATTRIBUTE,
'$resource' => true,
'$description' => 'This event triggers on any attributes event.',
'create' => [
'$description' => 'This event triggers when an attribute is created.',
],
'delete' => [
'$description' => 'This event triggers when an attribute is deleted.'
]
],
'create' => [
'$description' => 'This event triggers when a collection is created.'
],
'delete' => [
'$description' => 'This event triggers when a collection is deleted.',
],
'update' => [
'$description' => 'This event triggers when a collection is updated.',
]
],
'create' => [
'$description' => 'This event triggers when a database is created.'
],

View file

@ -353,7 +353,7 @@ App::get('/v1/project/usage')
'executionsTotal' => $total[METRIC_EXECUTIONS],
'executionsMbSecondsTotal' => $total[METRIC_EXECUTIONS_MB_SECONDS],
'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS],
'rowsTotal' => $total[METRIC_DOCUMENTS],
'documentsTotal' => $total[METRIC_DOCUMENTS],
'databasesTotal' => $total[METRIC_DATABASES],
'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE],
'usersTotal' => $total[METRIC_USERS],

View file

@ -483,26 +483,10 @@ App::init()
/*
* Background Jobs
*/
$events = $route->getLabel('event', '');
$queueForEvents
->setUser($user)
->setEvent($events)
->setProject($project);
if (str_contains($events, '.tables.')) {
$requestFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($requestFormat && version_compare($requestFormat, '1.7.0', '<')) {
$map = [
'rows' => 'documents',
'tables' => 'collections',
'columns' => 'attributes',
];
$compatibleEvents = str_replace(array_keys($map), array_values($map), $events);
// override the events
$queueForEvents->setEvent($compatibleEvents);
}
}
->setEvent($route->getLabel('event', ''))
->setProject($project)
->setUser($user);
$queueForAudits
->setMode($mode)

View file

@ -707,6 +707,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
},
];
// NOTE: `params` and `urls` are not used internally in the `Schema::build` function below!
$params = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return [ 'queries' => $args['queries']];
@ -721,8 +722,8 @@ App::setResource('schema', function ($utopia, $dbForProject) {
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'rowId' => $id,
'tableId' => $collectionId,
'documentId' => $id,
'collectionId' => $collectionId,
'data' => $args,
'permissions' => $permissions,
];
@ -737,8 +738,8 @@ App::setResource('schema', function ($utopia, $dbForProject) {
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'tableId' => $collectionId,
'rowId' => $documentId,
'collectionId' => $collectionId,
'documentId' => $documentId,
'data' => $args,
'permissions' => $permissions,
];

View file

@ -76,16 +76,18 @@ class Realtime extends Event
$payload = new Document($this->getPayload());
$db = $this->getContext('database');
$table = $this->getContext('table');
$bucket = $this->getContext('bucket');
// can be Tables API or Collections API, generated channels include both!
$tableOrCollection = $this->getContext('table') ?? $this->getContext('collection');
$target = RealtimeAdapter::fromPayload(
// Pass first, most verbose event pattern
event: $allEvents[0],
payload: $payload,
project: $this->getProject(),
database: $db,
table: $table,
table: $tableOrCollection,
bucket: $bucket,
);

View file

@ -285,7 +285,9 @@ class Mapper
case 'Appwrite\Utopia\Database\Validator\Queries\Base':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Tables':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Columns':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
@ -418,8 +420,10 @@ class Mapper
// TODO: Find a better way to do this
switch ($name) {
case 'Columns':
case 'Attributes':
return static::getColumnImplementation($object);
case 'Columns':
return static::getColumnImplementation($object, true);
case 'HashOptions':
return static::getHashOptionsImplementation($object);
}
@ -427,29 +431,24 @@ class Mapper
throw new Exception('Unknown union type: ' . $name);
}
private static function getColumnImplementation(array $object): Type
private static function getColumnImplementation(array $object, bool $isColumns = false): Type
{
switch ($object['type']) {
case 'string':
return match ($object['format'] ?? '') {
'email' => static::model('ColumnEmail'),
'url' => static::model('ColumnUrl'),
'ip' => static::model('ColumnIp'),
default => static::model('ColumnString'),
};
case 'integer':
return static::model('ColumnInteger');
case 'double':
return static::model('ColumnFloat');
case 'boolean':
return static::model('ColumnBoolean');
case 'datetime':
return static::model('ColumnDatetime');
case 'relationship':
return static::model('ColumnRelationship');
}
$prefix = $isColumns ? 'Column' : 'Attribute';
throw new Exception('Unknown column implementation');
return match ($object['type']) {
'string' => match ($object['format'] ?? '') {
'email' => static::model("{$prefix}Email"),
'url' => static::model("{$prefix}Url"),
'ip' => static::model("{$prefix}Ip"),
default => static::model("{$prefix}String"),
},
'integer' => static::model("{$prefix}Integer"),
'double' => static::model("{$prefix}Float"),
'boolean' => static::model("{$prefix}Boolean"),
'datetime' => static::model("{$prefix}Datetime"),
'relationship' => static::model("{$prefix}Relationship"),
default => throw new Exception('Unknown ' . strtolower($prefix) . ' implementation'),
};
}
private static function getHashOptionsImplementation(array $object): Type

View file

@ -303,23 +303,23 @@ class Realtime extends Adapter
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
} elseif (($parts[4] ?? '') === 'rows') {
} elseif (($parts[4] ?? '') === 'rows' || ($parts[4] ?? '') === 'documents') {
if ($database->isEmpty()) {
throw new \Exception('Database needs to be passed to Realtime for Row events in the Database.');
throw new \Exception('Database needs to be passed to Realtime for Document/Row events in the Database.');
}
if ($table->isEmpty()) {
throw new \Exception('Table needs to be passed to Realtime for Row events in the Database.');
throw new \Exception('Collection or the Table needs to be passed to Realtime for Document/Row events in the Database.');
}
// 1.7.x
// 1.7.x - Tables API
$channels[] = 'rows';
$channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows';
$channels[] = 'databases.' . $database->getId() . '.tables.' . $payload->getAttribute('$tableId') . '.rows.' . $payload->getId();
// 1.6.x
// 1.6.x - Collections API
$channels[] = 'documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$tableId') . '.documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$tableId') . '.documents.' . $payload->getId();
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $payload->getAttribute('$collectionId') . '.documents.' . $payload->getId();
$roles = $table->getAttribute('documentSecurity', false)
? \array_merge($table->getRead(), $payload->getRead())

View file

@ -408,8 +408,10 @@ class OpenAPI3 extends Format
];
break;
case 'Appwrite\Utopia\Database\Validator\Queries\Columns':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Tables':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':

View file

@ -0,0 +1,25 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Attributes extends Base
{
public const ALLOWED_ATTRIBUTES = [
'key',
'type',
'size',
'required',
'array',
'status',
'error'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('attributes', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Collections extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'enabled',
'documentSecurity'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('collections', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -6,37 +6,18 @@ use Appwrite\Utopia\Request\Filter;
class V19 extends Filter
{
// Map old params to new
private const PARAMS_MAP = [
'documentId' => 'rowId',
'attributes' => 'columns',
'collectionId' => 'tableId',
'attributeId' => 'columnId',
'$collectionId' => '$tableId',
'relatedCollection' => 'relatedTable',
'relatedCollectionId' => 'relatedTableId',
];
// Convert 1.6 params to 1.7
public function parse(array $content, string $model): array
{
$content = $this->overrideDatabaseParams($content, $model);
return $content;
}
protected function overrideDatabaseParams(array $content, string $model): array
{
if (!str_starts_with($model, 'databases.')) {
return $content;
}
$intersect = array_intersect_key(self::PARAMS_MAP, $content);
foreach ($intersect as $oldKey => $newKey) {
$content[$newKey] = $content[$oldKey];
unset($content[$oldKey]);
/*
Uncomment with first request filter; current is just a copy of V18
switch ($model) {
case 'functions.create':
$content['something'] = $content['somethingElse'] ?? "";
unset($content['something']);
break;
}
*/
return $content;
}

View file

@ -15,10 +15,23 @@ use Appwrite\Utopia\Response\Model\AlgoScrypt;
use Appwrite\Utopia\Response\Model\AlgoScryptModified;
use Appwrite\Utopia\Response\Model\AlgoSha;
use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\Attribute;
use Appwrite\Utopia\Response\Model\AttributeBoolean;
use Appwrite\Utopia\Response\Model\AttributeDatetime;
use Appwrite\Utopia\Response\Model\AttributeEmail;
use Appwrite\Utopia\Response\Model\AttributeEnum;
use Appwrite\Utopia\Response\Model\AttributeFloat;
use Appwrite\Utopia\Response\Model\AttributeInteger;
use Appwrite\Utopia\Response\Model\AttributeIP;
use Appwrite\Utopia\Response\Model\AttributeList;
use Appwrite\Utopia\Response\Model\AttributeRelationship;
use Appwrite\Utopia\Response\Model\AttributeString;
use Appwrite\Utopia\Response\Model\AttributeURL;
use Appwrite\Utopia\Response\Model\AuthProvider;
use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Branch;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Column;
use Appwrite\Utopia\Response\Model\ColumnBoolean;
use Appwrite\Utopia\Response\Model\ColumnDatetime;
@ -40,6 +53,7 @@ use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Utopia\Response\Model\DetectionFramework;
use Appwrite\Utopia\Response\Model\DetectionRuntime;
use Appwrite\Utopia\Response\Model\DevKey;
use Appwrite\Utopia\Response\Model\Document as ModelDocument;
use Appwrite\Utopia\Response\Model\Error;
use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
@ -106,6 +120,7 @@ use Appwrite\Utopia\Response\Model\TemplateVariable;
use Appwrite\Utopia\Response\Model\Token;
use Appwrite\Utopia\Response\Model\Topic;
use Appwrite\Utopia\Response\Model\UsageBuckets;
use Appwrite\Utopia\Response\Model\UsageCollection;
use Appwrite\Utopia\Response\Model\UsageDatabase;
use Appwrite\Utopia\Response\Model\UsageDatabases;
use Appwrite\Utopia\Response\Model\UsageFunction;
@ -148,6 +163,7 @@ class Response extends SwooleResponse
public const MODEL_USAGE_DATABASES = 'usageDatabases';
public const MODEL_USAGE_DATABASE = 'usageDatabase';
public const MODEL_USAGE_TABLE = 'usageTable';
public const MODEL_USAGE_COLLECTION = 'usageCollection';
public const MODEL_USAGE_USERS = 'usageUsers';
public const MODEL_USAGE_BUCKETS = 'usageBuckets';
public const MODEL_USAGE_STORAGE = 'usageStorage';
@ -160,14 +176,32 @@ class Response extends SwooleResponse
// Database
public const MODEL_DATABASE = 'database';
public const MODEL_DATABASE_LIST = 'databaseList';
public const MODEL_COLLECTION = 'collection';
public const MODEL_COLLECTION_LIST = 'collectionList';
public const MODEL_TABLE = 'table';
public const MODEL_TABLE_LIST = 'tableList';
public const MODEL_INDEX = 'index';
public const MODEL_INDEX_LIST = 'indexList';
public const MODEL_DOCUMENT = 'document';
public const MODEL_DOCUMENT_LIST = 'documentList';
public const MODEL_ROW = 'row';
public const MODEL_ROW_LIST = 'rowList';
// Database Attributes
public const MODEL_ATTRIBUTE = 'attribute';
public const MODEL_ATTRIBUTE_LIST = 'attributeList';
public const MODEL_ATTRIBUTE_STRING = 'attributeString';
public const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger';
public const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat';
public const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean';
public const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail';
public const MODEL_ATTRIBUTE_ENUM = 'attributeEnum';
public const MODEL_ATTRIBUTE_IP = 'attributeIp';
public const MODEL_ATTRIBUTE_URL = 'attributeUrl';
public const MODEL_ATTRIBUTE_DATETIME = 'attributeDatetime';
public const MODEL_ATTRIBUTE_RELATIONSHIP = 'attributeRelationship';
// Database Columns
public const MODEL_COLUMN = 'column';
public const MODEL_COLUMN_LIST = 'columnList';
public const MODEL_COLUMN_STRING = 'columnString';
@ -377,7 +411,9 @@ class Response extends SwooleResponse
->setModel(new ErrorDev())
// Lists
->setModel(new BaseList('Rows List', self::MODEL_ROW_LIST, 'rows', self::MODEL_ROW))
->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT))
->setModel(new BaseList('Tables List', self::MODEL_TABLE_LIST, 'tables', self::MODEL_TABLE))
->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION))
->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE))
->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX))
->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER))
@ -428,6 +464,21 @@ class Response extends SwooleResponse
->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT))
// Entities
->setModel(new Database())
// Collection API Models
->setModel(new Collection())
->setModel(new Attribute())
->setModel(new AttributeList())
->setModel(new AttributeString())
->setModel(new AttributeInteger())
->setModel(new AttributeFloat())
->setModel(new AttributeBoolean())
->setModel(new AttributeEmail())
->setModel(new AttributeEnum())
->setModel(new AttributeIP())
->setModel(new AttributeURL())
->setModel(new AttributeDatetime())
->setModel(new AttributeRelationship())
// Table API Models
->setModel(new Table())
->setModel(new Column())
->setModel(new ColumnList())
@ -443,6 +494,7 @@ class Response extends SwooleResponse
->setModel(new ColumnRelationship())
->setModel(new Index())
->setModel(new Row())
->setModel(new ModelDocument())
->setModel(new Log())
->setModel(new User())
->setModel(new AlgoMd5())
@ -509,6 +561,7 @@ class Response extends SwooleResponse
->setModel(new UsageDatabases())
->setModel(new UsageDatabase())
->setModel(new UsageTable())
->setModel(new UsageCollection())
->setModel(new UsageUsers())
->setModel(new UsageStorage())
->setModel(new UsageBuckets())

View file

@ -7,44 +7,18 @@ use Appwrite\Utopia\Response\Filter;
class V19 extends Filter
{
private const DATABASE_MAPPINGS = [
'table' => 'collection',
'tables' => 'collections',
'$tableId' => '$collectionId',
'tablesTotal' => 'collectionsTotal',
'relatedTable' => 'relatedCollection',
'relatedTableId' => 'relatedCollectionId',
'column' => 'attribute',
'columns' => 'attributes',
'columnsTotal' => 'attributesTotal',
'row' => 'document',
'rows' => 'documents',
'rowsTotal' => 'documentsTotal'
];
// Convert 1.7 Data format to 1.6 format
public function parse(array $content, string $model): array
{
$parsedResponse = $content;
return match ($model) {
Response::MODEL_ROW,
Response::MODEL_TABLE,
Response::MODEL_COLUMN,
Response::MODEL_ROW_LIST,
Response::MODEL_TABLE_LIST,
Response::MODEL_COLUMN_LIST,
Response::MODEL_USAGE_TABLE,
Response::MODEL_USAGE_DATABASE,
Response::MODEL_USAGE_DATABASES,
Response::MODEL_COLUMN_RELATIONSHIP => $this->handleDBTerminology($model, $content),
$parsedResponse = match($model) {
Response::MODEL_FUNCTION => $this->parseFunction($content),
Response::MODEL_FUNCTION_LIST => $this->handleList($content, 'functions', fn ($item) => $this->parseFunction($item)),
default => $parsedResponse,
};
return $parsedResponse;
}
protected function parseFunction(array $content): array
@ -53,49 +27,4 @@ class V19 extends Filter
unset($content['deploymentId']);
return $content;
}
protected function handleDBTerminology(string $model, array $content): array
{
$isListModel = match ($model) {
Response::MODEL_ROW_LIST,
Response::MODEL_TABLE_LIST,
Response::MODEL_COLUMN_LIST => true,
default => false
};
if ($isListModel) {
foreach (self::DATABASE_MAPPINGS as $oldKey => $newKey) {
if (isset($content[$oldKey])) {
$content[$newKey] = array_map(fn ($item) => $this->remapKeys($item), $content[$oldKey]);
unset($content[$oldKey]);
}
}
} else {
$content = $this->remapKeysRecursive($content);
}
return $content;
}
private function remapKeys(array $data): array
{
foreach (self::DATABASE_MAPPINGS as $old => $new) {
if (isset($data[$old])) {
$data[$new] = $data[$old];
unset($data[$old]);
}
}
return $data;
}
private function remapKeysRecursive(array $data): array
{
$result = [];
foreach ($data as $key => $value) {
$newKey = self::DATABASE_MAPPINGS[$key] ?? $key;
$result[$newKey] = \is_array($value) ? $this->remapKeysRecursive($value) : $value;
}
return $result;
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Attribute extends Model
{
public function __construct()
{
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'fullName',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'string',
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, `stuck`, or `failed`',
'default' => '',
'example' => 'available',
])
->addRule('error', [
'type' => self::TYPE_STRING,
'description' => 'Error message. Displays error generated on failure of creating or deleting an attribute.',
'default' => '',
'example' => 'string',
])
->addRule('required', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute required?',
'default' => false,
'example' => true,
])
->addRule('array', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute an array?',
'default' => false,
'required' => false,
'example' => false,
])
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Attribute creation date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Attribute update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
]);
}
public array $conditions = [];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Attribute';
}
/**
* Get Collection
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE;
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeBoolean extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'isEnabled',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'boolean',
])
->addRule('default', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => false
])
;
}
public array $conditions = [
'type' => self::TYPE_BOOLEAN
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeBoolean';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_BOOLEAN;
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeDatetime extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'birthDay',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => self::TYPE_DATETIME,
])
->addRule('format', [
'type' => self::TYPE_DATETIME,
'description' => 'ISO 8601 format.',
'default' => APP_DATABASE_ATTRIBUTE_DATETIME,
'example' => APP_DATABASE_ATTRIBUTE_DATETIME,
'array' => false,
])
->addRule('default', [
'type' => self::TYPE_STRING,
'description' => 'Default value for attribute when not provided. Only null is optional',
'default' => null,
'example' => self::TYPE_DATETIME_EXAMPLE,
'array' => false,
'required' => false,
])
;
}
public array $conditions = [
'type' => self::TYPE_DATETIME
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeDatetime';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_DATETIME;
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeEmail extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'userEmail',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'string',
])
->addRule('format', [
'type' => self::TYPE_STRING,
'description' => 'String format.',
'default' => APP_DATABASE_ATTRIBUTE_EMAIL,
'example' => APP_DATABASE_ATTRIBUTE_EMAIL,
])
->addRule('default', [
'type' => self::TYPE_STRING,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 'default@example.com',
])
;
}
public array $conditions = [
'type' => self::TYPE_STRING,
'format' => \APP_DATABASE_ATTRIBUTE_EMAIL
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeEmail';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_EMAIL;
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeEnum extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'status',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'string',
])
->addRule('elements', [
'type' => self::TYPE_STRING,
'description' => 'Array of elements in enumerated type.',
'default' => null,
'example' => 'element',
'array' => true,
])
->addRule('format', [
'type' => self::TYPE_STRING,
'description' => 'String format.',
'default' => APP_DATABASE_ATTRIBUTE_ENUM,
'example' => APP_DATABASE_ATTRIBUTE_ENUM,
])
->addRule('default', [
'type' => self::TYPE_STRING,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 'element',
])
;
}
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;
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeFloat extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'percentageCompleted',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'double',
])
->addRule('min', [
'type' => self::TYPE_FLOAT,
'description' => 'Minimum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 1.5,
])
->addRule('max', [
'type' => self::TYPE_FLOAT,
'description' => 'Maximum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 10.5,
])
->addRule('default', [
'type' => self::TYPE_FLOAT,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 2.5,
])
;
}
public array $conditions = [
'type' => self::TYPE_FLOAT,
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeFloat';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_FLOAT;
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeIP extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'ipAddress',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'string',
])
->addRule('format', [
'type' => self::TYPE_STRING,
'description' => 'String format.',
'default' => APP_DATABASE_ATTRIBUTE_IP,
'example' => APP_DATABASE_ATTRIBUTE_IP,
])
->addRule('default', [
'type' => self::TYPE_STRING,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => '192.0.2.0',
])
;
}
public array $conditions = [
'type' => self::TYPE_STRING,
'format' => \APP_DATABASE_ATTRIBUTE_IP
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeIP';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_IP;
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeInteger extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'count',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'integer',
])
->addRule('min', [
'type' => self::TYPE_INTEGER,
'description' => 'Minimum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 1,
])
->addRule('max', [
'type' => self::TYPE_INTEGER,
'description' => 'Maximum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 10,
])
->addRule('default', [
'type' => self::TYPE_INTEGER,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 10,
])
;
}
public array $conditions = [
'type' => self::TYPE_INTEGER,
];
/**
* Get Name *
* @return string
*/
public function getName(): string
{
return 'AttributeInteger';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_INTEGER;
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class AttributeList extends Model
{
public function __construct()
{
$this
->addRule('total', [
'type' => self::TYPE_INTEGER,
'description' => 'Total number of attributes in the given collection.',
'default' => 0,
'example' => 5,
])
->addRule('attributes', [
'type' => [
Response::MODEL_ATTRIBUTE_BOOLEAN,
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_DATETIME,
Response::MODEL_ATTRIBUTE_RELATIONSHIP,
Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute
],
'description' => 'List of attributes.',
'default' => [],
'array' => true
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Attributes List';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_LIST;
}
}

View file

@ -0,0 +1,96 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Utopia\Database\Document;
class AttributeRelationship extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('relatedCollection', [
'type' => self::TYPE_STRING,
'description' => 'The ID of the related collection.',
'default' => null,
'example' => 'collection',
])
->addRule('relationType', [
'type' => self::TYPE_STRING,
'description' => 'The type of the relationship.',
'default' => '',
'example' => 'oneToOne|oneToMany|manyToOne|manyToMany',
])
->addRule('twoWay', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is the relationship two-way?',
'default' => false,
'example' => false,
])
->addRule('twoWayKey', [
'type' => self::TYPE_STRING,
'description' => 'The key of the two-way relationship.',
'default' => '',
'example' => 'string',
])
->addRule('onDelete', [
'type' => self::TYPE_STRING,
'description' => 'How deleting the parent document will propagate to child documents.',
'default' => 'restrict',
'example' => 'restrict|cascade|setNull',
])
->addRule('side', [
'type' => self::TYPE_STRING,
'description' => 'Whether this is the parent or child side of the relationship',
'default' => '',
'example' => 'parent|child',
])
;
}
public array $conditions = [
'type' => self::TYPE_RELATIONSHIP,
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeRelationship';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_RELATIONSHIP;
}
/**
* Process Document before returning it to the client
*
* @return Document
*/
public function filter(Document $document): Document
{
$options = $document->getAttribute('options');
if (!\is_null($options)) {
$document->setAttribute('relatedCollection', $options['relatedCollection']);
$document->setAttribute('relationType', $options['relationType']);
$document->setAttribute('twoWay', $options['twoWay']);
$document->setAttribute('twoWayKey', $options['twoWayKey']);
$document->setAttribute('side', $options['side']);
$document->setAttribute('onDelete', $options['onDelete']);
}
return $document;
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeString extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('size', [
'type' => self::TYPE_INTEGER,
'description' => 'Attribute size.',
'default' => 0,
'example' => 128,
])
->addRule('default', [
'type' => self::TYPE_STRING,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 'default',
])
;
}
public array $conditions = [
'type' => self::TYPE_STRING,
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeString';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_STRING;
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeURL extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'githubUrl',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'string',
])
->addRule('format', [
'type' => self::TYPE_STRING,
'description' => 'String format.',
'default' => APP_DATABASE_ATTRIBUTE_URL,
'example' => APP_DATABASE_ATTRIBUTE_URL,
])
->addRule('default', [
'type' => self::TYPE_STRING,
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 'http://example.com',
])
;
}
public array $conditions = [
'type' => self::TYPE_STRING,
'format' => \APP_DATABASE_ATTRIBUTE_URL
];
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AttributeURL';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_URL;
}
}

View file

@ -32,9 +32,11 @@ class BaseList extends Model
if ($paging) {
$namesWithCap = [
'rows', 'tables', 'users', 'files', 'buckets', 'functions',
'deployments', 'executions', 'projects', 'webhooks', 'keys',
'platforms', 'rules', 'memberships', 'teams'
'rows', 'tables', // new api
'documents', 'collections', // legacy api
'users', 'files', 'buckets', 'functions',
'deployments', 'executions', 'projects',
'webhooks', 'keys', 'platforms', 'rules', 'memberships', 'teams'
];
if (\in_array($name, $namesWithCap)) {

View file

@ -0,0 +1,109 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Collection extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Collection ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Collection creation date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Collection update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$permissions', [
'type' => self::TYPE_STRING,
'description' => 'Collection permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).',
'default' => '',
'example' => ['read("any")'],
'array' => true
])
->addRule('databaseId', [
'type' => self::TYPE_STRING,
'description' => 'Database ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Collection name.',
'default' => '',
'example' => 'My Collection',
])
->addRule('enabled', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Collection enabled. Can be \'enabled\' or \'disabled\'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys.',
'default' => true,
'example' => false,
])
->addRule('documentSecurity', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether document-level permissions are enabled. [Learn more about permissions](https://appwrite.io/docs/permissions).',
'default' => '',
'example' => true,
])
->addRule('attributes', [
'type' => [
Response::MODEL_ATTRIBUTE_BOOLEAN,
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_DATETIME,
Response::MODEL_ATTRIBUTE_RELATIONSHIP,
Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute
],
'description' => 'Collection attributes.',
'default' => [],
'example' => new \stdClass(),
'array' => true,
])
->addRule('indexes', [
'type' => Response::MODEL_INDEX,
'description' => 'Collection indexes.',
'default' => [],
'example' => new \stdClass(),
'array' => true
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Collection';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_COLLECTION;
}
}

View file

@ -0,0 +1,92 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Utopia\Database\Document as DatabaseDocument;
class Document extends Any
{
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Document';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_DOCUMENT;
}
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Document ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$collectionId', [
'type' => self::TYPE_STRING,
'description' => 'Collection ID.',
'default' => '',
'example' => '5e5ea5c15117e',
])
->addRule('$databaseId', [
'type' => self::TYPE_STRING,
'description' => 'Database ID.',
'default' => '',
'example' => '5e5ea5c15117e',
])
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Document creation date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Document update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$permissions', [
'type' => self::TYPE_STRING,
'description' => 'Document permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).',
'default' => '',
'example' => ['read("any")'],
'array' => true,
]);
}
public function filter(DatabaseDocument $document): DatabaseDocument
{
$document->removeAttribute('$internalId');
$document->removeAttribute('$collection');
$document->removeAttribute('$tenant');
foreach ($document->getAttributes() as $attribute) {
if (\is_array($attribute)) {
foreach ($attribute as $subAttribute) {
if ($subAttribute instanceof DatabaseDocument) {
$this->filter($subAttribute);
}
}
} elseif ($attribute instanceof DatabaseDocument) {
$this->filter($attribute);
}
}
return $document;
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class UsageCollection extends Model
{
public function __construct()
{
$this
->addRule('range', [
'type' => self::TYPE_STRING,
'description' => 'Time range of the usage stats.',
'default' => '',
'example' => '30d',
])
->addRule('documentsTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of of documents.',
'default' => 0,
'example' => 0,
])
->addRule('documents', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of documents per period.',
'default' => [],
'example' => [],
'array' => true
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'UsageCollection';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_USAGE_COLLECTION;
}
}