init: move to module structure for database; start with columns.

This commit is contained in:
Darshan 2025-05-04 13:22:26 +05:30
parent 7ffd5cfde6
commit b3b9961860
30 changed files with 3125 additions and 1930 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,417 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Response;
use Throwable;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Conflict as ConflictException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Index as IndexException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Structure;
use Utopia\Platform\Action as UtopiaAction;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Range;
class Action extends UtopiaAction
{
/**
* * Create column of varying type
*
* @param string $databaseId
* @param string $tableId
* @param Document $column
* @param Response $response
* @param Database $dbForProject
* @param EventDatabase $queueForDatabase
* @param Event $queueForEvents
*
* @return Document Newly created attribute document
*
* @throws AuthorizationException
* @throws Exception
* @throws LimitException
* @throws RestrictedException
* @throws StructureException
* @throws \Utopia\Database\Exception
* @throws ConflictException
*/
protected function createColumn(
string $databaseId,
string $tableId,
Document $column,
Response $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): Document {
$key = $column->getAttribute('key');
$type = $column->getAttribute('type', '');
$size = $column->getAttribute('size', 0);
$required = $column->getAttribute('required', true);
$signed = $column->getAttribute('signed', true); // integers are signed by default
$array = $column->getAttribute('array', false);
$format = $column->getAttribute('format', '');
$formatOptions = $column->getAttribute('formatOptions', []);
$filters = $column->getAttribute('filters', []); // filters are hidden from the endpoint
$default = $column->getAttribute('default');
$options = $column->getAttribute('options', []);
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId);
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
if (!empty($format)) {
if (!Structure::hasFormat($format, $type)) {
throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} columns.");
}
}
// Must throw here since dbForProject->createAttribute is performed by db worker
if ($required && isset($default)) {
throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column');
}
if ($array && isset($default)) {
throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns');
}
if ($type === Database::VAR_RELATIONSHIP) {
$options['side'] = Database::RELATION_SIDE_PARENT;
$relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection'] ?? '');
if ($relatedTable->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related table was not found.');
}
}
try {
$column = new Document([
'$id' => ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $key),
'key' => $key,
'databaseInternalId' => $db->getInternalId(),
'databaseId' => $db->getId(),
'collectionInternalId' => $table->getInternalId(),
'collectionId' => $tableId,
'type' => $type,
'status' => 'processing', // processing, available, failed, deleting, stuck
'size' => $size,
'required' => $required,
'signed' => $signed,
'default' => $default,
'array' => $array,
'format' => $format,
'formatOptions' => $formatOptions,
'filters' => $filters,
'options' => $options,
]);
$dbForProject->checkAttribute($table, $column);
$column = $dbForProject->createDocument('attributes', $column);
} catch (DuplicateException) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId());
throw $e;
}
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId());
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
$twoWayKey = $options['twoWayKey'];
$options['relatedCollection'] = $table->getId();
$options['twoWayKey'] = $key;
$options['side'] = Database::RELATION_SIDE_CHILD;
try {
$twoWayAttribute = new Document([
'$id' => ID::custom($db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $twoWayKey),
'key' => $twoWayKey,
'databaseInternalId' => $db->getInternalId(),
'databaseId' => $db->getId(),
'collectionInternalId' => $relatedTable->getInternalId(),
'collectionId' => $relatedTable->getId(),
'type' => $type,
'status' => 'processing', // processing, available, failed, deleting, stuck
'size' => $size,
'required' => $required,
'signed' => $signed,
'default' => $default,
'array' => $array,
'format' => $format,
'formatOptions' => $formatOptions,
'filters' => $filters,
'options' => $options,
]);
$dbForProject->checkAttribute($relatedTable, $twoWayAttribute);
$dbForProject->createDocument('attributes', $twoWayAttribute);
} catch (DuplicateException) {
$dbForProject->deleteDocument('attributes', $column->getId());
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
} catch (LimitException) {
$dbForProject->deleteDocument('attributes', $column->getId());
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (Throwable $e) {
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId());
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId());
throw $e;
}
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId());
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId());
}
$queueForDatabase
->setType(DATABASE_TYPE_CREATE_ATTRIBUTE)
->setDatabase($db)
->setTable($table)
->setRow($column);
$queueForEvents
->setContext('table', $table)
->setContext('database', $db)
->setParam('databaseId', $databaseId)
->setParam('tableId', $table->getId())
->setParam('columnId', $column->getId());
$response->setStatusCode(SwooleResponse::STATUS_CODE_CREATED);
return $column;
}
protected function updateColumn(
string $databaseId,
string $tableId,
string $key,
Database $dbForProject,
Event $queueForEvents,
string $type,
int $size = null,
string $filter = null,
string|bool|int|float $default = null,
bool $required = null,
int|float|null $min = null,
int|float|null $max = null,
array $elements = null,
array $options = [],
string $newKey = null,
): Document {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId);
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key);
if ($column->isEmpty()) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
}
if ($column->getAttribute('status') !== 'available') {
throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE);
}
if ($column->getAttribute(('type') !== $type)) {
throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID);
}
if ($column->getAttribute('type') === Database::VAR_STRING && $column->getAttribute(('filter') !== $filter)) {
throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID);
}
if ($required && isset($default)) {
throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required column');
}
if ($column->getAttribute('array', false) && isset($default)) {
throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array columns');
}
$tableId = 'database_' . $db->getInternalId() . '_collection_' . $table->getInternalId();
$column
->setAttribute('default', $default)
->setAttribute('required', $required);
if (!empty($size)) {
$column->setAttribute('size', $size);
}
switch ($column->getAttribute('format')) {
case APP_DATABASE_ATTRIBUTE_INT_RANGE:
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE:
$min ??= $column->getAttribute('formatOptions')['min'];
$max ??= $column->getAttribute('formatOptions')['max'];
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
}
if ($column->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) {
$validator = new Range($min, $max, Database::VAR_INTEGER);
} else {
$validator = new Range($min, $max, Database::VAR_FLOAT);
if (!is_null($default)) {
$default = \floatval($default);
}
}
if (!is_null($default) && !$validator->isValid($default)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
}
$options = [
'min' => $min,
'max' => $max
];
$column->setAttribute('formatOptions', $options);
break;
case APP_DATABASE_ATTRIBUTE_ENUM:
if (empty($elements)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Enum elements must not be empty');
}
foreach ($elements as $element) {
if (\strlen($element) === 0) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Each enum element must not be empty');
}
}
if (!is_null($default) && !in_array($default, $elements)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements');
}
$options = [
'elements' => $elements
];
$column->setAttribute('formatOptions', $options);
break;
}
if ($type === Database::VAR_RELATIONSHIP) {
$primaryRowOptions = \array_merge($column->getAttribute('options', []), $options);
$column->setAttribute('options', $primaryRowOptions);
try {
$dbForProject->updateRelationship(
collection: $tableId,
id: $key,
newKey: $newKey,
onDelete: $primaryRowOptions['onDelete'],
);
} catch (NotFoundException) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
}
if ($primaryRowOptions['twoWay']) {
$relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $primaryRowOptions['relatedCollection']);
$relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey']);
if (!empty($newKey) && $newKey !== $key) {
$options['twoWayKey'] = $newKey;
}
$relatedOptions = \array_merge($relatedColumn->getAttribute('options'), $options);
$relatedColumn->setAttribute('options', $relatedOptions);
$dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $primaryRowOptions['twoWayKey'], $relatedColumn);
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $relatedTable->getId());
}
} else {
try {
$dbForProject->updateAttribute(
collection: $tableId,
id: $key,
size: $size,
required: $required,
default: $default,
formatOptions: $options,
newKey: $newKey ?? null
);
} catch (TruncateException) {
throw new Exception(Exception::ATTRIBUTE_INVALID_RESIZE);
} catch (NotFoundException) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (IndexException $e) {
throw new Exception(Exception::INDEX_INVALID, $e->getMessage());
}
}
if (!empty($newKey) && $key !== $newKey) {
$originalUid = $column->getId();
$column
->setAttribute('$id', ID::custom($db->getInternalId() . '_' . $table->getInternalId() . '_' . $newKey))
->setAttribute('key', $newKey);
$dbForProject->updateDocument('attributes', $originalUid, $column);
/**
* @var Document $index
*/
foreach ($table->getAttribute('indexes') as $index) {
/**
* @var string[] $columns
*/
$columns = $index->getAttribute('attributes', []);
$found = \array_search($key, $columns);
if ($found !== false) {
$columns[$found] = $newKey;
$index->setAttribute('attributes', $columns);
$dbForProject->updateDocument('indexes', $index->getId(), $index);
}
}
} else {
$column = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key, $column);
}
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $table->getId());
$queueForEvents
->setContext('table', $table)
->setContext('database', $db)
->setParam('databaseId', $databaseId)
->setParam('tableId', $table->getId())
->setParam('columnId', $column->getId());
return $column;
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Boolean;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createBooleanColumn';
}
public function __construct()
{
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean')
->desc('Create boolean column')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createBooleanColumn',
description: '/docs/references/databases/create-boolean-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Boolean(), 'Default value for column when not provided. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$column = $this->createColumn($databaseId, $tableId, new Document([
'key' => $key,
'type' => Database::VAR_BOOLEAN,
'size' => 0,
'required' => $required,
'default' => $default,
'array' => $array,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN);
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Boolean;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateBooleanColumn';
}
public function __construct()
{
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/boolean/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/boolean/:key')
->desc('Update boolean column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateBooleanColumn',
description: '/docs/references/databases/update-boolean-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(string $databaseId, string $tableId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_BOOLEAN,
default: $default,
required: $required,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN);
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Datetime;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createDatetimeColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime')
->desc('Create datetime column')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createDatetimeColumn',
description: '/docs/references/databases/create-datetime-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_DATETIME,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject'])
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
bool $array,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$filters = ['datetime'];
$column = $this->createColumn(
$databaseId,
$tableId,
new Document([
'key' => $key,
'type' => Database::VAR_DATETIME,
'size' => 0,
'required' => $required,
'default' => $default,
'array' => $array,
'filters' => $filters,
]),
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_DATETIME);
}
}

View file

@ -0,0 +1,97 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Datetime;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateDatetimeColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/datetime/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/datetime/:key')
->desc('Update dateTime column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateDatetimeColumn',
description: '/docs/references/databases/update-datetime-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_DATETIME,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for column when not provided. Cannot be set when column is required.', injections: ['dbForProject'])
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_DATETIME,
default: $default,
required: $required,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_DATETIME);
}
}

View file

@ -0,0 +1,164 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
class Delete extends Action
{
use HTTP;
public static function getName(): string
{
return 'deleteColumn';
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_DELETE)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key')
->desc('Delete column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.delete')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'deleteColumn',
description: '/docs/references/databases/delete-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_NOCONTENT,
model: UtopiaResponse::MODEL_NONE,
)
],
contentType: ContentType::NONE
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents,
): void {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$table = $dbForProject->getDocument('database_' . $db->getInternalId(), $tableId);
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$column = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $table->getInternalId() . '_' . $key);
if ($column->isEmpty()) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
}
$validator = new IndexDependencyValidator(
$table->getAttribute('indexes'),
$dbForProject->getAdapter()->getSupportForCastIndexArray(),
);
if (!$validator->isValid($column)) {
throw new Exception(Exception::INDEX_DEPENDENCY);
}
if ($column->getAttribute('status') === 'available') {
$column = $dbForProject->updateDocument('attributes', $column->getId(), $column->setAttribute('status', 'deleting'));
}
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $tableId);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $table->getInternalId());
if ($column->getAttribute('type') === Database::VAR_RELATIONSHIP) {
$options = $column->getAttribute('options');
if ($options['twoWay']) {
$relatedTable = $dbForProject->getDocument('database_' . $db->getInternalId(), $options['relatedCollection']);
if ($relatedTable->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$relatedColumn = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $relatedTable->getInternalId() . '_' . $options['twoWayKey']);
if ($relatedColumn->isEmpty()) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
}
if ($relatedColumn->getAttribute('status') === 'available') {
$dbForProject->updateDocument('attributes', $relatedColumn->getId(), $relatedColumn->setAttribute('status', 'deleting'));
}
$dbForProject->purgeCachedDocument('database_' . $db->getInternalId(), $options['relatedCollection']);
$dbForProject->purgeCachedCollection('database_' . $db->getInternalId() . '_collection_' . $relatedTable->getInternalId());
}
}
$queueForDatabase
->setType(DATABASE_TYPE_DELETE_ATTRIBUTE)
->setTable($table)
->setDatabase($db)
->setRow($column);
$type = $column->getAttribute('type');
$format = $column->getAttribute('format');
$model = match ($type) {
Database::VAR_BOOLEAN => UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN,
Database::VAR_INTEGER => UtopiaResponse::MODEL_ATTRIBUTE_INTEGER,
Database::VAR_FLOAT => UtopiaResponse::MODEL_ATTRIBUTE_FLOAT,
Database::VAR_DATETIME => UtopiaResponse::MODEL_ATTRIBUTE_DATETIME,
Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP,
Database::VAR_STRING => match ($format) {
APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_ATTRIBUTE_EMAIL,
APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_ATTRIBUTE_ENUM,
APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_ATTRIBUTE_IP,
APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_ATTRIBUTE_URL,
default => UtopiaResponse::MODEL_ATTRIBUTE_STRING,
},
default => UtopiaResponse::MODEL_ATTRIBUTE,
};
$queueForEvents
->setParam('databaseId', $databaseId)
->setParam('tableId', $table->getId())
->setParam('columnId', $column->getId())
->setContext('table', $table)
->setContext('database', $db)
->setPayload($response->output($column, $model));
$response->noContent();
}
}

View file

@ -0,0 +1,104 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Email;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\Email;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createEmailColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/email')
->desc('Create email column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createEmailColumn',
description: '/docs/references/databases/create-email-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_EMAIL,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Email(), 'Default value for column when not provided. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
bool $array,
\Appwrite\Utopia\Response $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$column = $this->createColumn(
$databaseId,
$tableId,
new Document([
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 254,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_EMAIL,
]),
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_EMAIL);
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Email;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\Email;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateEmailColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/email/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/email/:key')
->desc('Update email column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateEmailColumn',
description: '/docs/references/databases/update-email-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_EMAIL,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Nullable(new Email()), 'Default value for column when not provided. Cannot be set when column is required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_EMAIL,
default: $default,
required: $required,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_EMAIL);
}
}

View file

@ -0,0 +1,113 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Enum;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createEnumColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/enum')
->desc('Create enum column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createEnumColumn',
description: '/docs/references/databases/create-attribute-enum.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_ENUM,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('elements', [], new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of enum values.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Text(0), 'Default value for column when not provided. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
array $elements,
?bool $required,
?string $default,
bool $array,
\Appwrite\Utopia\Response $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
if (!is_null($default) && !in_array($default, $elements, true)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements');
}
$column = $this->createColumn(
$databaseId,
$tableId,
new Document([
'key' => $key,
'type' => Database::VAR_STRING,
'size' => Database::LENGTH_KEY,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_ENUM,
'formatOptions' => ['elements' => $elements],
]),
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_ENUM);
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Enum;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
use Utopia\Validator\Text;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateEnumColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/enum/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/enum/:key')
->desc('Update enum column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateEnumColumn',
description: '/docs/references/databases/update-enum-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_ENUM,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('elements', null, new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Updated list of enum values.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Nullable(new Text(0)), 'Default value for column when not provided. Cannot be set when column is required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?array $elements,
?bool $required,
?string $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_ENUM,
default: $default,
required: $required,
elements: $elements,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_ENUM);
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Float;
use const INF;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\FloatValidator;
use Utopia\Validator\Range;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createFloatColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/float')
->desc('Create float column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createFloatColumn',
description: '/docs/references/databases/create-float-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_FLOAT,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new FloatValidator(), 'Minimum value', true)
->param('max', null, new FloatValidator(), 'Maximum value', true)
->param('default', null, new FloatValidator(), 'Default value. Cannot be set when required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?float $min,
?float $max,
?float $default,
bool $array,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$min ??= -INF;
$max ??= INF;
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum must be less than or equal to maximum');
}
$validator = new Range($min, $max, Database::VAR_FLOAT);
if (!\is_null($default) && !$validator->isValid($default)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
}
$column = $this->createColumn($databaseId, $tableId, new Document([
'key' => $key,
'type' => Database::VAR_FLOAT,
'size' => 0,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$formatOptions = $column->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$column->setAttribute('min', \floatval($formatOptions['min']));
$column->setAttribute('max', \floatval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT);
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Float;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\FloatValidator;
use Utopia\Validator\Nullable;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateFloatColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/float/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/float/:key')
->desc('Update float column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateFloatColumn',
description: '/docs/references/databases/update-float-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_FLOAT,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new FloatValidator(), 'Minimum value', true)
->param('max', null, new FloatValidator(), 'Maximum value', true)
->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?float $min,
?float $max,
?float $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_FLOAT,
default: $default,
required: $required,
min: $min,
max: $max,
newKey: $newKey
);
$formatOptions = $column->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$column->setAttribute('min', \floatval($formatOptions['min']));
$column->setAttribute('max', \floatval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_FLOAT);
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
class Get extends Action
{
use HTTP;
public static function getName(): string
{
return 'getColumn';
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_GET)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key')
->desc('Get column')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'getColumn',
description: '/docs/references/databases/get-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: [
UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN,
UtopiaResponse::MODEL_ATTRIBUTE_INTEGER,
UtopiaResponse::MODEL_ATTRIBUTE_FLOAT,
UtopiaResponse::MODEL_ATTRIBUTE_EMAIL,
UtopiaResponse::MODEL_ATTRIBUTE_ENUM,
UtopiaResponse::MODEL_ATTRIBUTE_URL,
UtopiaResponse::MODEL_ATTRIBUTE_IP,
UtopiaResponse::MODEL_ATTRIBUTE_DATETIME,
UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP,
UtopiaResponse::MODEL_ATTRIBUTE_STRING,
]
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
}
public function action(string $databaseId, string $tableId, string $key, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId);
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$column = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $table->getInternalId() . '_' . $key);
if ($column->isEmpty()) {
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
}
$type = $column->getAttribute('type');
$format = $column->getAttribute('format');
$options = $column->getAttribute('options', []);
foreach ($options as $optKey => $optValue) {
$column->setAttribute($optKey, $optValue);
}
$model = match ($type) {
Database::VAR_BOOLEAN => UtopiaResponse::MODEL_ATTRIBUTE_BOOLEAN,
Database::VAR_INTEGER => UtopiaResponse::MODEL_ATTRIBUTE_INTEGER,
Database::VAR_FLOAT => UtopiaResponse::MODEL_ATTRIBUTE_FLOAT,
Database::VAR_DATETIME => UtopiaResponse::MODEL_ATTRIBUTE_DATETIME,
Database::VAR_RELATIONSHIP => UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP,
Database::VAR_STRING => match ($format) {
APP_DATABASE_ATTRIBUTE_EMAIL => UtopiaResponse::MODEL_ATTRIBUTE_EMAIL,
APP_DATABASE_ATTRIBUTE_ENUM => UtopiaResponse::MODEL_ATTRIBUTE_ENUM,
APP_DATABASE_ATTRIBUTE_IP => UtopiaResponse::MODEL_ATTRIBUTE_IP,
APP_DATABASE_ATTRIBUTE_URL => UtopiaResponse::MODEL_ATTRIBUTE_URL,
default => UtopiaResponse::MODEL_ATTRIBUTE_STRING,
},
default => UtopiaResponse::MODEL_ATTRIBUTE,
};
$response->dynamic($column, $model);
}
}

View file

@ -0,0 +1,96 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\IP;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\IP;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createIpColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/ip')
->desc('Create IP address column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createIpColumn',
description: '/docs/references/databases/create-ip-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_IP,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new IP(), 'Default value. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
bool $array,
\Appwrite\Utopia\Response $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$column = $this->createColumn($databaseId, $tableId, new Document([
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 39,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_IP,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_IP);
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\IP;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\IP;
use Utopia\Validator\Nullable;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateIpColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/ip/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/ip/:key')
->desc('Update IP address column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateIpColumn',
description: '/docs/references/databases/update-ip-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_IP,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when column is required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_IP,
default: $default,
required: $required,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_IP);
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Integer;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Range;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createIntegerColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/integer')
->desc('Create integer column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createIntegerColumn',
description: '/docs/references/databases/create-integer-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_INTEGER,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Integer(), 'Minimum value', true)
->param('max', null, new Integer(), 'Maximum value', true)
->param('default', null, new Integer(), 'Default value. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?int $min,
?int $max,
?int $default,
bool $array,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$min ??= \PHP_INT_MIN;
$max ??= \PHP_INT_MAX;
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
}
$validator = new Range($min, $max, Database::VAR_INTEGER);
if (!\is_null($default) && !$validator->isValid($default)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
}
$size = $max > 2147483647 ? 8 : 4;
$column = $this->createColumn($databaseId, $tableId, new Document([
'key' => $key,
'type' => Database::VAR_INTEGER,
'size' => $size,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$formatOptions = $column->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$column->setAttribute('min', \intval($formatOptions['min']));
$column->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER);
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Integer;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateIntegerColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/integer/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/integer/:key')
->desc('Update integer column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateIntegerColumn',
description: '/docs/references/databases/update-integer-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_INTEGER,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Integer(), 'Minimum value', true)
->param('max', null, new Integer(), 'Maximum value', true)
->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when column is required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?int $min,
?int $max,
?int $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_INTEGER,
default: $default,
required: $required,
min: $min,
max: $max,
newKey: $newKey
);
$formatOptions = $column->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$column->setAttribute('min', \intval($formatOptions['min']));
$column->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_INTEGER);
}
}

View file

@ -0,0 +1,168 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Relationship;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\WhiteList;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createRelationshipColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/relationship')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/relationship')
->desc('Create relationship column')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createRelationshipColumn',
description: '/docs/references/databases/create-relationship-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('relatedTableId', '', new UID(), 'Related Table ID.')
->param('type', '', new WhiteList([
Database::RELATION_ONE_TO_ONE,
Database::RELATION_MANY_TO_ONE,
Database::RELATION_MANY_TO_MANY,
Database::RELATION_ONE_TO_MANY
], true), 'Relation type')
->param('twoWay', false, new Boolean(), 'Is Two Way?', true)
->param('key', null, new Key(), 'Column Key.', true)
->param('twoWayKey', null, new Key(), 'Two Way Column Key.', true)
->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([
Database::RELATION_MUTATE_CASCADE,
Database::RELATION_MUTATE_RESTRICT,
Database::RELATION_MUTATE_SET_NULL
], true), 'Constraints option', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $relatedTableId,
string $type,
bool $twoWay,
?string $key,
?string $twoWayKey,
string $onDelete,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$key ??= $relatedTableId;
$twoWayKey ??= $tableId;
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId);
$table = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $table->getInternalId());
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$relatedTableDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedTableId);
$relatedTable = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedTableDocument->getInternalId());
if ($relatedTable->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$columns = $table->getAttribute('attributes', []);
foreach ($columns as $column) {
if ($column->getAttribute('type') !== Database::VAR_RELATIONSHIP) {
continue;
}
if (\strtolower($column->getId()) === \strtolower($key)) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
}
if (
\strtolower($column->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) &&
$column->getAttribute('options')['relatedCollection'] === $relatedTable->getId()
) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique.');
}
if (
$type === Database::RELATION_MANY_TO_MANY &&
$column->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY &&
$column->getAttribute('options')['relatedCollection'] === $relatedTable->getId()
) {
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Only one "manyToMany" relationship per table is allowed.');
}
}
$column = $this->createColumn($databaseId, $tableId, new Document([
'key' => $key,
'type' => Database::VAR_RELATIONSHIP,
'size' => 0,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
'options' => [
'relatedCollection' => $relatedTableId,
'relationType' => $type,
'twoWay' => $twoWay,
'twoWayKey' => $twoWayKey,
'onDelete' => $onDelete,
]
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
foreach ($column->getAttribute('options', []) as $k => $option) {
$column->setAttribute($k, $option);
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP);
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\Relationship;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\WhiteList;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateRelationshipColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/:key/relationship')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/:key/relationship')
->desc('Update relationship column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateRelationshipColumn',
description: '/docs/references/databases/update-relationship-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('onDelete', null, new WhiteList([
Database::RELATION_MUTATE_CASCADE,
Database::RELATION_MUTATE_RESTRICT,
Database::RELATION_MUTATE_SET_NULL
], true), 'Constraints option', true)
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?string $onDelete,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
$databaseId,
$tableId,
$key,
$dbForProject,
$queueForEvents,
type: Database::VAR_RELATIONSHIP,
required: false,
options: [
'onDelete' => $onDelete
],
newKey: $newKey
);
foreach ($column->getAttribute('options', []) as $k => $option) {
$column->setAttribute($k, $option);
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_RELATIONSHIP);
}
}

View file

@ -0,0 +1,122 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\String;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator;
use Utopia\Validator\Boolean;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createStringColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/string')
->desc('Create string column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createStringColumn',
description: '/docs/references/databases/create-string-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_STRING
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Column Key.')
->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Text(0, 0), 'Default value for column when not provided. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?int $size,
?bool $required,
?string $default,
bool $array,
bool $encrypt,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
// Ensure default fits in the given size
$validator = new Text($size, 0);
if (!is_null($default) && !$validator->isValid($default)) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
}
$filters = [];
if ($encrypt) {
$filters[] = 'encrypt';
}
$column = $this->createColumn(
$databaseId,
$tableId,
new Document([
'key' => $key,
'type' => Database::VAR_STRING,
'size' => $size,
'required' => $required,
'default' => $default,
'array' => $array,
'filters' => $filters,
]),
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_STRING);
}
}

View file

@ -0,0 +1,101 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\String;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateStringColumn';
}
public function __construct()
{
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/string/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/string/:key')
->desc('Update string column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateStringColumn',
description: '/docs/references/databases/update-string-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_STRING,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.')
->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string attribute.', true)
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
?int $size,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
databaseId: $databaseId,
tableId: $tableId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
type: Database::VAR_STRING,
size: $size,
default: $default,
required: $required,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_STRING);
}
}

View file

@ -0,0 +1,96 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\URL;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\URL;
class Create extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'createUrlColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url')
->desc('Create URL column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'createUrlColumn',
description: '/docs/references/databases/create-url-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: UtopiaResponse::MODEL_ATTRIBUTE_URL,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new URL(), 'Default value for column when not provided. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
bool $array,
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
): void {
$column = $this->createColumn($databaseId, $tableId, new Document([
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 2000,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_URL,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_URL);
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns\URL;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Columns\Action as ColumnAction;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
use Utopia\Validator\URL;
class Update extends ColumnAction
{
use HTTP;
public static function getName(): string
{
return 'updateUrlColumn';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns/url/:key')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes/url/:key')
->desc('Update URL column')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'updateUrlColumn',
description: '/docs/references/databases/update-url-attribute.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_URL,
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('key', '', new Key(), 'Column Key.')
->param('required', null, new Boolean(), 'Is column required?')
->param('default', null, new Nullable(new URL()), 'Default value for column when not provided. Cannot be set when column is required.')
->param('newKey', null, new Key(), 'New Column Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
}
public function action(
string $databaseId,
string $tableId,
string $key,
?bool $required,
?string $default,
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
): void {
$column = $this->updateColumn(
$databaseId,
$tableId,
$key,
$dbForProject,
$queueForEvents,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_URL,
default: $default,
required: $required,
newKey: $newKey
);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($column, UtopiaResponse::MODEL_ATTRIBUTE_URL);
}
}

View file

@ -0,0 +1,125 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Columns;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\Queries\Attributes;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Response as SwooleResponse;
class XList extends Action
{
use HTTP;
public static function getName(): string
{
return 'listColumns';
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_GET)
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/columns')
->httpAlias('/v1/databases/:databaseId/collections/:tableId/attributes')
->desc('List columns')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'columns',
name: 'listColumns',
description: '/docs/references/databases/list-attributes.md',
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: UtopiaResponse::MODEL_ATTRIBUTE_LIST
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('tableId', '', new UID(), 'Table ID.')
->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
}
public function action(string $databaseId, string $tableId, array $queries, UtopiaResponse $response, Database $dbForProject): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$table = $dbForProject->getDocument('database_' . $database->getInternalId(), $tableId);
if ($table->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
\array_push(
$queries,
Query::equal('databaseInternalId', [$database->getInternalId()]),
Query::equal('collectionInternalId', [$table->getInternalId()])
);
$cursor = \array_filter(
$queries,
fn ($query) => \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE])
);
$cursor = \reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$columnId = $cursor->getValue();
$cursorDocument = Authorization::skip(
fn () => $dbForProject->find('attributes', [
Query::equal('databaseInternalId', [$database->getInternalId()]),
Query::equal('collectionInternalId', [$table->getInternalId()]),
Query::equal('key', [$columnId]),
Query::limit(1),
])
);
if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Column '{$columnId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument[0]);
}
$filters = Query::groupByType($queries)['filters'];
try {
$columns = $dbForProject->find('attributes', $queries);
$total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null.");
}
$response->dynamic(new Document([
'attributes' => $columns,
'total' => $total,
]), UtopiaResponse::MODEL_ATTRIBUTE_LIST);
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Appwrite\Platform\Modules\Databases;
use Appwrite\Platform\Modules\Databases\Services\Http;
use Appwrite\Platform\Modules\Databases\Services\Workers;
use Utopia\Platform;
class Module extends Platform\Module
{
public function __construct()
{
$this->addService('http', new Http());
$this->addService('workers', new Workers());
}
}

View file

@ -0,0 +1,110 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Services;
use Appwrite\Platform\Modules\Databases\Http\Columns\Boolean\Create as CreateBoolean;
use Appwrite\Platform\Modules\Databases\Http\Columns\Boolean\Update as UpdateBoolean;
use Appwrite\Platform\Modules\Databases\Http\Columns\Datetime\Create as CreateDatetime;
use Appwrite\Platform\Modules\Databases\Http\Columns\Datetime\Update as UpdateDatetime;
use Appwrite\Platform\Modules\Databases\Http\Columns\Delete as DeleteColumn;
use Appwrite\Platform\Modules\Databases\Http\Columns\Email\Create as CreateEmail;
use Appwrite\Platform\Modules\Databases\Http\Columns\Email\Update as UpdateEmail;
use Appwrite\Platform\Modules\Databases\Http\Columns\Enum\Create as CreateEnum;
use Appwrite\Platform\Modules\Databases\Http\Columns\Enum\Update as UpdateEnum;
use Appwrite\Platform\Modules\Databases\Http\Columns\Float\Create as CreateFloat;
use Appwrite\Platform\Modules\Databases\Http\Columns\Float\Update as UpdateFloat;
use Appwrite\Platform\Modules\Databases\Http\Columns\Get as GetColumn;
use Appwrite\Platform\Modules\Databases\Http\Columns\Integer\Create as CreateInteger;
use Appwrite\Platform\Modules\Databases\Http\Columns\Integer\Update as UpdateInteger;
use Appwrite\Platform\Modules\Databases\Http\Columns\IP\Create as CreateIP;
use Appwrite\Platform\Modules\Databases\Http\Columns\IP\Update as UpdateIP;
use Appwrite\Platform\Modules\Databases\Http\Columns\Relationship\Create as CreateRelationship;
use Appwrite\Platform\Modules\Databases\Http\Columns\Relationship\Update as UpdateRelationship;
use Appwrite\Platform\Modules\Databases\Http\Columns\String\Create as CreateString;
use Appwrite\Platform\Modules\Databases\Http\Columns\String\Update as UpdateString;
use Appwrite\Platform\Modules\Databases\Http\Columns\URL\Create as CreateURL;
use Appwrite\Platform\Modules\Databases\Http\Columns\URL\Update as UpdateURL;
use Appwrite\Platform\Modules\Databases\Http\Columns\XList as ListColumns;
use Utopia\Platform\Service;
class Http extends Service
{
public function __construct()
{
$this->type = Service::TYPE_HTTP;
$this->registerDatabaseActions();
$this->registerTableActions();
$this->registerColumnActions();
$this->registerIndexActions();
$this->registerRowActions();
}
private function registerDatabaseActions()
{
}
private function registerTableActions()
{
}
private function registerColumnActions(): void
{
// Column top level actions
$this->addAction(GetColumn::getName(), new GetColumn());
$this->addAction(DeleteColumn::getName(), new DeleteColumn());
$this->addAction(ListColumns::getName(), new ListColumns());
// Column: Boolean
$this->addAction(CreateBoolean::getName(), new CreateBoolean());
$this->addAction(UpdateBoolean::getName(), new UpdateBoolean());
// Column: Datetime
$this->addAction(CreateDatetime::getName(), new CreateDatetime());
$this->addAction(UpdateDatetime::getName(), new UpdateDatetime());
// Column: Email
$this->addAction(CreateEmail::getName(), new CreateEmail());
$this->addAction(UpdateEmail::getName(), new UpdateEmail());
// Column: Enum
$this->addAction(CreateEnum::getName(), new CreateEnum());
$this->addAction(UpdateEnum::getName(), new UpdateEnum());
// Column: Float
$this->addAction(CreateFloat::getName(), new CreateFloat());
$this->addAction(UpdateFloat::getName(), new UpdateFloat());
// Column: Integer
$this->addAction(CreateInteger::getName(), new CreateInteger());
$this->addAction(UpdateInteger::getName(), new UpdateInteger());
// Column: IP
$this->addAction(CreateIP::getName(), new CreateIP());
$this->addAction(UpdateIP::getName(), new UpdateIP());
// Column: Relationship
$this->addAction(CreateRelationship::getName(), new CreateRelationship());
$this->addAction(UpdateRelationship::getName(), new UpdateRelationship());
// Column: String
$this->addAction(CreateString::getName(), new CreateString());
$this->addAction(UpdateString::getName(), new UpdateString());
// Column: URL
$this->addAction(CreateURL::getName(), new CreateURL());
$this->addAction(UpdateURL::getName(), new UpdateURL());
}
private function registerIndexActions()
{
}
private function registerRowActions()
{
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Services;
use Appwrite\Platform\Modules\Databases\Workers\Databases;
use Utopia\Platform\Service;
class Workers extends Service
{
public function __construct()
{
$this->type = Service::TYPE_WORKER;
$this->addAction(Databases::getName(), new Databases());
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Appwrite\Platform\Workers;
namespace Appwrite\Platform\Modules\Databases\Workers;
use Appwrite\Event\Realtime;
use Exception;

View file

@ -4,7 +4,6 @@ namespace Appwrite\Platform\Services;
use Appwrite\Platform\Workers\Audits;
use Appwrite\Platform\Workers\Certificates;
use Appwrite\Platform\Workers\Databases;
use Appwrite\Platform\Workers\Deletes;
use Appwrite\Platform\Workers\Functions;
use Appwrite\Platform\Workers\Mails;
@ -23,7 +22,6 @@ class Workers extends Service
$this
->addAction(Audits::getName(), new Audits())
->addAction(Certificates::getName(), new Certificates())
->addAction(Databases::getName(), new Databases())
->addAction(Deletes::getName(), new Deletes())
->addAction(Functions::getName(), new Functions())
->addAction(Mails::getName(), new Mails())