2019-05-09 06:54:39 +00:00
< ? php
2023-06-15 05:29:03 +00:00
use Appwrite\Auth\Auth ;
use Appwrite\Detector\Detector ;
use Appwrite\Event\Database as EventDatabase ;
use Appwrite\Event\Event ;
2025-01-30 04:53:53 +00:00
use Appwrite\Event\StatsUsage ;
2022-02-06 16:50:48 +00:00
use Appwrite\Extend\Exception ;
2023-06-15 05:29:03 +00:00
use Appwrite\Network\Validator\Email ;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\AuthType ;
use Appwrite\SDK\ContentType ;
use Appwrite\SDK\Method ;
2025-03-27 08:03:56 +00:00
use Appwrite\SDK\Parameter ;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\Response as SDKResponse ;
2023-06-15 05:29:03 +00:00
use Appwrite\Utopia\Database\Validator\CustomId ;
2023-07-20 18:50:53 +00:00
use Appwrite\Utopia\Database\Validator\Queries\Attributes ;
2023-06-15 05:29:03 +00:00
use Appwrite\Utopia\Database\Validator\Queries\Collections ;
use Appwrite\Utopia\Database\Validator\Queries\Databases ;
2023-07-21 11:10:44 +00:00
use Appwrite\Utopia\Database\Validator\Queries\Indexes ;
2023-10-19 03:19:40 +00:00
use Appwrite\Utopia\Request ;
2023-06-15 05:29:03 +00:00
use Appwrite\Utopia\Response ;
use MaxMind\Db\Reader ;
use Utopia\App ;
2021-08-15 21:09:40 +00:00
use Utopia\Audit\Audit ;
2023-06-15 05:29:03 +00:00
use Utopia\Config\Config ;
2021-08-16 23:21:00 +00:00
use Utopia\Database\Database ;
2025-02-25 05:51:11 +00:00
use Utopia\Database\DateTime ;
2021-08-16 23:21:00 +00:00
use Utopia\Database\Document ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Exception\Authorization as AuthorizationException ;
2023-10-19 04:06:57 +00:00
use Utopia\Database\Exception\Conflict as ConflictException ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Exception\Duplicate as DuplicateException ;
2025-02-10 08:04:51 +00:00
use Utopia\Database\Exception\Index as IndexException ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Exception\Limit as LimitException ;
2024-11-04 07:57:08 +00:00
use Utopia\Database\Exception\NotFound as NotFoundException ;
2025-04-16 11:33:14 +00:00
use Utopia\Database\Exception\Order as OrderException ;
2024-09-09 07:17:47 +00:00
use Utopia\Database\Exception\Query as QueryException ;
2025-05-15 11:33:00 +00:00
use Utopia\Database\Exception\Relationship as RelationshipException ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Exception\Restricted as RestrictedException ;
use Utopia\Database\Exception\Structure as StructureException ;
2024-08-30 10:18:45 +00:00
use Utopia\Database\Exception\Truncate as TruncateException ;
2025-06-10 03:56:30 +00:00
use Utopia\Database\Exception\Type as TypeException ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2021-08-16 23:21:00 +00:00
use Utopia\Database\Query ;
2021-08-12 01:05:19 +00:00
use Utopia\Database\Validator\Authorization ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Validator\Datetime as DatetimeValidator ;
use Utopia\Database\Validator\Index as IndexValidator ;
2025-01-16 15:20:30 +00:00
use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator ;
2021-06-22 19:34:42 +00:00
use Utopia\Database\Validator\Key ;
2021-07-05 20:27:20 +00:00
use Utopia\Database\Validator\Permissions ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Validator\Queries ;
2024-05-09 16:54:28 +00:00
use Utopia\Database\Validator\Query\Cursor ;
2023-06-15 05:29:03 +00:00
use Utopia\Database\Validator\Query\Limit ;
use Utopia\Database\Validator\Query\Offset ;
2021-07-23 14:59:55 +00:00
use Utopia\Database\Validator\Structure ;
2021-07-05 20:27:20 +00:00
use Utopia\Database\Validator\UID ;
2022-05-25 12:10:10 +00:00
use Utopia\Locale\Locale ;
2023-06-15 05:29:03 +00:00
use Utopia\Validator\ArrayList ;
use Utopia\Validator\Boolean ;
use Utopia\Validator\FloatValidator ;
use Utopia\Validator\Integer ;
2024-03-06 17:34:21 +00:00
use Utopia\Validator\IP ;
2023-06-15 05:29:03 +00:00
use Utopia\Validator\JSON ;
2023-03-01 12:00:36 +00:00
use Utopia\Validator\Nullable ;
2025-06-10 03:56:30 +00:00
use Utopia\Validator\Numeric ;
2023-06-15 05:29:03 +00:00
use Utopia\Validator\Range ;
use Utopia\Validator\Text ;
2023-01-13 15:28:04 +00:00
use Utopia\Validator\URL ;
2023-06-15 05:29:03 +00:00
use Utopia\Validator\WhiteList ;
2021-07-27 01:00:36 +00:00
2021-08-16 23:21:00 +00:00
/**
2023-10-01 17:39:26 +00:00
* * Create attribute of varying type
2021-08-16 23:21:00 +00:00
*
2023-10-01 17:39:26 +00:00
* @ param string $databaseId
* @ param string $collectionId
* @ param Document $attribute
* @ param Response $response
* @ param Database $dbForProject
* @ param EventDatabase $queueForDatabase
* @ param Event $queueForEvents
2021-08-16 23:21:00 +00:00
* @ return Document Newly created attribute document
2023-10-01 17:39:26 +00:00
* @ throws AuthorizationException
2022-06-15 12:57:06 +00:00
* @ throws Exception
2023-10-01 17:39:26 +00:00
* @ throws LimitException
* @ throws RestrictedException
* @ throws StructureException
* @ throws \Utopia\Database\Exception
2023-10-19 04:06:57 +00:00
* @ throws ConflictException
2023-10-05 13:15:39 +00:00
* @ throws Exception
2021-08-16 23:21:00 +00:00
*/
2023-10-01 17:39:26 +00:00
function createAttribute ( string $databaseId , string $collectionId , Document $attribute , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) : Document
2021-08-16 23:21:00 +00:00
{
2021-12-16 15:04:30 +00:00
$key = $attribute -> getAttribute ( 'key' );
2021-07-27 01:00:36 +00:00
$type = $attribute -> getAttribute ( 'type' , '' );
$size = $attribute -> getAttribute ( 'size' , 0 );
$required = $attribute -> getAttribute ( 'required' , true );
2022-05-23 14:54:50 +00:00
$signed = $attribute -> getAttribute ( 'signed' , true ); // integers are signed by default
2021-07-27 01:00:36 +00:00
$array = $attribute -> getAttribute ( 'array' , false );
2021-08-21 21:48:07 +00:00
$format = $attribute -> getAttribute ( 'format' , '' );
$formatOptions = $attribute -> getAttribute ( 'formatOptions' , []);
2022-05-23 14:54:50 +00:00
$filters = $attribute -> getAttribute ( 'filters' , []); // filters are hidden from the endpoint
2021-12-16 15:04:30 +00:00
$default = $attribute -> getAttribute ( 'default' );
2023-03-16 07:31:48 +00:00
$options = $attribute -> getAttribute ( 'options' , []);
2021-07-27 01:00:36 +00:00
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-07-27 01:00:36 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-07-27 01:00:36 +00:00
}
2021-08-21 21:48:07 +00:00
if ( ! empty ( $format )) {
if ( ! Structure :: hasFormat ( $format , $type )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_FORMAT_UNSUPPORTED , " Format { $format } not available for { $type } attributes. " );
2021-07-27 18:19:37 +00:00
}
2021-07-27 01:00:36 +00:00
}
2021-12-27 12:45:23 +00:00
// Must throw here since dbForProject->createAttribute is performed by db worker
2022-09-30 09:27:19 +00:00
if ( $required && isset ( $default )) {
2022-08-14 06:56:12 +00:00
throw new Exception ( Exception :: ATTRIBUTE_DEFAULT_UNSUPPORTED , 'Cannot set default value for required attribute' );
2021-08-25 20:43:15 +00:00
}
2022-09-30 09:27:19 +00:00
if ( $array && isset ( $default )) {
2022-08-14 08:05:11 +00:00
throw new Exception ( Exception :: ATTRIBUTE_DEFAULT_UNSUPPORTED , 'Cannot set default value for array attributes' );
2021-10-26 20:14:25 +00:00
}
2023-03-13 08:22:01 +00:00
if ( $type === Database :: VAR_RELATIONSHIP ) {
2023-03-30 07:41:18 +00:00
$options [ 'side' ] = Database :: RELATION_SIDE_PARENT ;
2025-05-26 05:42:11 +00:00
$relatedCollection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $options [ 'relatedCollection' ] ? ? '' );
2023-03-13 08:22:01 +00:00
if ( $relatedCollection -> isEmpty ()) {
2023-04-05 06:19:40 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND , 'The related collection was not found.' );
2023-03-13 08:22:01 +00:00
}
}
2021-08-21 04:48:28 +00:00
try {
2021-10-05 13:57:57 +00:00
$attribute = new Document ([
2025-05-26 05:42:11 +00:00
'$id' => ID :: custom ( $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key ),
2021-12-16 15:04:30 +00:00
'key' => $key ,
2025-05-26 05:42:11 +00:00
'databaseInternalId' => $database -> getSequence (),
2025-05-15 11:33:00 +00:00
'databaseId' => $database -> getId (),
2025-05-26 05:42:11 +00:00
'collectionInternalId' => $collection -> getSequence (),
2022-08-15 11:24:31 +00:00
'collectionId' => $collectionId ,
2021-08-21 04:48:28 +00:00
'type' => $type ,
2021-10-05 00:23:15 +00:00
'status' => 'processing' , // processing, available, failed, deleting, stuck
2021-08-21 04:48:28 +00:00
'size' => $size ,
'required' => $required ,
'signed' => $signed ,
2021-08-27 17:12:16 +00:00
'default' => $default ,
2021-08-21 04:48:28 +00:00
'array' => $array ,
'format' => $format ,
2021-08-21 21:48:07 +00:00
'formatOptions' => $formatOptions ,
2021-11-02 00:47:07 +00:00
'filters' => $filters ,
2023-03-16 07:31:48 +00:00
'options' => $options ,
2021-10-05 13:57:57 +00:00
]);
2021-09-01 01:13:58 +00:00
2021-12-27 12:45:23 +00:00
$dbForProject -> checkAttribute ( $collection , $attribute );
$attribute = $dbForProject -> createDocument ( 'attributes' , $attribute );
2023-03-29 07:28:59 +00:00
} catch ( DuplicateException ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_ALREADY_EXISTS );
2023-03-29 07:28:59 +00:00
} catch ( LimitException ) {
2024-11-12 09:27:24 +00:00
throw new Exception ( Exception :: ATTRIBUTE_LIMIT_EXCEEDED );
2024-02-08 01:17:54 +00:00
} catch ( \Throwable $e ) {
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collectionId );
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ());
2023-04-05 06:48:57 +00:00
throw $e ;
2021-08-21 04:48:28 +00:00
}
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collectionId );
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ());
2021-07-27 01:00:36 +00:00
2023-03-29 07:28:59 +00:00
if ( $type === Database :: VAR_RELATIONSHIP && $options [ 'twoWay' ]) {
$twoWayKey = $options [ 'twoWayKey' ];
$options [ 'relatedCollection' ] = $collection -> getId ();
$options [ 'twoWayKey' ] = $key ;
2023-03-30 07:41:18 +00:00
$options [ 'side' ] = Database :: RELATION_SIDE_CHILD ;
2023-03-29 07:28:59 +00:00
try {
2025-05-15 11:33:00 +00:00
try {
$twoWayAttribute = new Document ([
2025-05-26 05:42:11 +00:00
'$id' => ID :: custom ( $database -> getSequence () . '_' . $relatedCollection -> getSequence () . '_' . $twoWayKey ),
2025-05-15 11:33:00 +00:00
'key' => $twoWayKey ,
2025-05-26 05:42:11 +00:00
'databaseInternalId' => $database -> getSequence (),
2025-05-15 11:33:00 +00:00
'databaseId' => $database -> getId (),
2025-05-26 05:42:11 +00:00
'collectionInternalId' => $relatedCollection -> getSequence (),
2025-05-15 11:33:00 +00:00
'collectionId' => $relatedCollection -> 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 ,
]);
2023-03-29 07:28:59 +00:00
2025-05-15 11:33:00 +00:00
$dbForProject -> checkAttribute ( $relatedCollection , $twoWayAttribute );
$dbForProject -> createDocument ( 'attributes' , $twoWayAttribute );
} catch ( DuplicateException ) {
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
} catch ( LimitException ) {
throw new Exception ( Exception :: ATTRIBUTE_LIMIT_EXCEEDED );
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
}
2024-02-08 01:17:54 +00:00
} catch ( \Throwable $e ) {
2025-05-15 11:33:00 +00:00
$dbForProject -> deleteDocument ( 'attributes' , $attribute -> getId ());
2023-04-05 06:48:57 +00:00
throw $e ;
2025-05-15 11:33:00 +00:00
} finally {
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collectionId );
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ());
2023-03-29 06:26:58 +00:00
}
2023-03-29 07:28:59 +00:00
2025-05-15 11:33:00 +00:00
// If operation succeeded, purge the cache for the related collection too
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $relatedCollection -> getId ());
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence ());
2023-03-29 06:26:58 +00:00
}
2023-06-04 16:25:56 +00:00
$queueForDatabase
2022-04-13 12:39:31 +00:00
-> setType ( DATABASE_TYPE_CREATE_ATTRIBUTE )
2025-05-15 11:33:00 +00:00
-> setDatabase ( $database )
2022-04-13 12:39:31 +00:00
-> setCollection ( $collection )
2023-07-18 11:16:33 +00:00
-> setDocument ( $attribute );
2021-07-27 01:00:36 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setContext ( 'collection' , $collection )
2025-05-15 11:33:00 +00:00
-> setContext ( 'database' , $database )
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
2023-07-18 11:16:33 +00:00
-> setParam ( 'attributeId' , $attribute -> getId ());
2021-08-15 11:08:32 +00:00
2021-07-27 01:00:36 +00:00
$response -> setStatusCode ( Response :: STATUS_CODE_CREATED );
2021-08-16 23:21:00 +00:00
return $attribute ;
2022-05-30 17:41:25 +00:00
}
2021-07-27 01:00:36 +00:00
2023-02-26 12:33:18 +00:00
function updateAttribute (
string $databaseId ,
string $collectionId ,
string $key ,
Database $dbForProject ,
2023-10-03 18:56:36 +00:00
Event $queueForEvents ,
2023-02-26 12:33:18 +00:00
string $type ,
2024-09-05 03:45:13 +00:00
int $size = null ,
2023-02-26 12:33:18 +00:00
string $filter = null ,
string | bool | int | float $default = null ,
bool $required = null ,
2025-02-26 17:35:06 +00:00
int | float | null $min = null ,
int | float | null $max = null ,
2023-03-22 15:49:08 +00:00
array $elements = null ,
2024-08-16 06:18:52 +00:00
array $options = [],
2024-09-05 03:45:13 +00:00
string $newKey = null ,
2023-02-26 12:33:18 +00:00
) : Document {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
2023-02-26 12:33:18 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2023-02-26 12:33:18 +00:00
if ( $collection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$attribute = $dbForProject -> getDocument ( 'attributes' , $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key );
2023-02-26 12:33:18 +00:00
if ( $attribute -> isEmpty ()) {
throw new Exception ( Exception :: ATTRIBUTE_NOT_FOUND );
}
if ( $attribute -> getAttribute ( 'status' ) !== 'available' ) {
throw new Exception ( Exception :: ATTRIBUTE_NOT_AVAILABLE );
}
2023-02-27 07:45:11 +00:00
if ( $attribute -> getAttribute (( 'type' ) !== $type )) {
throw new Exception ( Exception :: ATTRIBUTE_TYPE_INVALID );
}
if ( $attribute -> getAttribute ( 'type' ) === Database :: VAR_STRING && $attribute -> getAttribute (( 'filter' ) !== $filter )) {
2023-02-26 12:33:18 +00:00
throw new Exception ( Exception :: ATTRIBUTE_TYPE_INVALID );
}
2023-02-26 15:44:22 +00:00
if ( $required && isset ( $default )) {
throw new Exception ( Exception :: ATTRIBUTE_DEFAULT_UNSUPPORTED , 'Cannot set default value for required attribute' );
2023-02-26 12:33:18 +00:00
}
2023-02-26 15:44:22 +00:00
if ( $attribute -> getAttribute ( 'array' , false ) && isset ( $default )) {
throw new Exception ( Exception :: ATTRIBUTE_DEFAULT_UNSUPPORTED , 'Cannot set default value for array attributes' );
2023-02-26 12:33:18 +00:00
}
2023-02-26 15:44:22 +00:00
2025-05-26 05:42:11 +00:00
$collectionId = 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ();
2023-02-26 15:44:22 +00:00
2023-03-10 10:06:10 +00:00
$attribute
-> setAttribute ( 'default' , $default )
-> setAttribute ( 'required' , $required );
2023-02-26 15:44:22 +00:00
2024-08-30 09:22:17 +00:00
if ( ! empty ( $size )) {
$attribute -> setAttribute ( 'size' , $size );
}
2023-02-26 15:44:22 +00:00
switch ( $attribute -> getAttribute ( 'format' )) {
case APP_DATABASE_ATTRIBUTE_INT_RANGE :
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE :
2025-02-26 16:41:26 +00:00
$min ? ? = $attribute -> getAttribute ( 'formatOptions' )[ 'min' ];
$max ? ? = $attribute -> getAttribute ( 'formatOptions' )[ 'max' ];
2023-02-26 15:44:22 +00:00
if ( $min > $max ) {
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , 'Minimum value must be lesser than maximum value' );
}
2023-03-20 09:13:23 +00:00
if ( $attribute -> getAttribute ( 'format' ) === APP_DATABASE_ATTRIBUTE_INT_RANGE ) {
$validator = new Range ( $min , $max , Database :: VAR_INTEGER );
} else {
$validator = new Range ( $min , $max , Database :: VAR_FLOAT );
2023-02-26 15:44:22 +00:00
2023-03-20 09:13:23 +00:00
if ( ! is_null ( $default )) {
$default = \floatval ( $default );
}
}
2023-02-26 15:44:22 +00:00
if ( ! is_null ( $default ) && ! $validator -> isValid ( $default )) {
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , $validator -> getDescription ());
}
$options = [
'min' => $min ,
'max' => $max
];
$attribute -> setAttribute ( 'formatOptions' , $options );
break ;
case APP_DATABASE_ATTRIBUTE_ENUM :
if ( empty ( $elements )) {
2023-02-27 07:45:11 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , 'Enum elements must not be empty' );
2023-02-26 15:44:22 +00:00
}
foreach ( $elements as $element ) {
2023-03-20 09:13:23 +00:00
if ( \strlen ( $element ) === 0 ) {
2023-02-26 15:44:22 +00:00
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' );
}
2023-03-10 10:06:10 +00:00
2023-02-26 15:44:22 +00:00
$options = [
'elements' => $elements
];
2023-03-20 09:13:23 +00:00
2023-02-26 15:44:22 +00:00
$attribute -> setAttribute ( 'formatOptions' , $options );
break ;
}
2023-03-22 15:49:08 +00:00
if ( $type === Database :: VAR_RELATIONSHIP ) {
2023-07-07 18:20:06 +00:00
$primaryDocumentOptions = \array_merge ( $attribute -> getAttribute ( 'options' , []), $options );
$attribute -> setAttribute ( 'options' , $primaryDocumentOptions );
2024-11-04 07:57:08 +00:00
try {
$dbForProject -> updateRelationship (
collection : $collectionId ,
id : $key ,
newKey : $newKey ,
onDelete : $primaryDocumentOptions [ 'onDelete' ],
);
2025-05-15 11:33:00 +00:00
} catch ( IndexException ) {
throw new Exception ( Exception :: INDEX_INVALID );
} catch ( LimitException ) {
throw new Exception ( Exception :: ATTRIBUTE_LIMIT_EXCEEDED );
} catch ( RelationshipException $e ) {
throw new Exception ( Exception :: RELATIONSHIP_VALUE_INVALID , $e -> getMessage ());
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
2024-11-04 07:57:08 +00:00
}
2023-03-31 05:38:37 +00:00
2023-07-07 18:20:06 +00:00
if ( $primaryDocumentOptions [ 'twoWay' ]) {
2025-05-26 05:42:11 +00:00
$relatedCollection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $primaryDocumentOptions [ 'relatedCollection' ]);
$relatedAttribute = $dbForProject -> getDocument ( 'attributes' , $database -> getSequence () . '_' . $relatedCollection -> getSequence () . '_' . $primaryDocumentOptions [ 'twoWayKey' ]);
2024-08-16 06:18:52 +00:00
if ( ! empty ( $newKey ) && $newKey !== $key ) {
$options [ 'twoWayKey' ] = $newKey ;
}
2023-03-31 05:38:37 +00:00
$relatedOptions = \array_merge ( $relatedAttribute -> getAttribute ( 'options' ), $options );
$relatedAttribute -> setAttribute ( 'options' , $relatedOptions );
2024-08-16 06:18:52 +00:00
2025-05-15 11:33:00 +00:00
2025-05-26 05:42:11 +00:00
$dbForProject -> updateDocument ( 'attributes' , $database -> getSequence () . '_' . $relatedCollection -> getSequence () . '_' . $primaryDocumentOptions [ 'twoWayKey' ], $relatedAttribute );
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $relatedCollection -> getId ());
2023-03-31 05:38:37 +00:00
}
2023-03-22 15:49:08 +00:00
} else {
2024-08-30 10:18:45 +00:00
try {
$dbForProject -> updateAttribute (
collection : $collectionId ,
id : $key ,
2024-09-05 03:45:13 +00:00
size : $size ,
2024-08-30 10:18:45 +00:00
required : $required ,
default : $default ,
2024-11-27 16:02:37 +00:00
formatOptions : $options ,
2024-09-05 03:31:04 +00:00
newKey : $newKey ? ? null
2024-08-30 10:18:45 +00:00
);
2025-05-15 11:33:00 +00:00
} catch ( DuplicateException ) {
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
2025-02-10 08:55:37 +00:00
} catch ( IndexException $e ) {
2025-02-10 08:04:51 +00:00
throw new Exception ( Exception :: INDEX_INVALID , $e -> getMessage ());
2025-05-15 11:33:00 +00:00
} catch ( LimitException ) {
throw new Exception ( Exception :: ATTRIBUTE_LIMIT_EXCEEDED );
} catch ( TruncateException ) {
throw new Exception ( Exception :: ATTRIBUTE_INVALID_RESIZE );
2024-08-30 10:18:45 +00:00
}
2023-03-22 15:49:08 +00:00
}
2023-03-10 10:06:10 +00:00
2024-08-16 06:18:52 +00:00
if ( ! empty ( $newKey ) && $key !== $newKey ) {
2024-11-11 16:46:47 +00:00
$originalUid = $attribute -> getId ();
2024-09-05 08:41:50 +00:00
2024-09-05 03:45:28 +00:00
$attribute
2025-05-26 05:42:11 +00:00
-> setAttribute ( '$id' , ID :: custom ( $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $newKey ))
2024-09-05 03:45:28 +00:00
-> setAttribute ( 'key' , $newKey );
2025-05-15 11:33:00 +00:00
try {
$dbForProject -> updateDocument ( 'attributes' , $originalUid , $attribute );
} catch ( DuplicateException ) {
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
}
2024-11-11 16:46:47 +00:00
/**
* @ var Document $index
*/
foreach ( $collection -> getAttribute ( 'indexes' ) as $index ) {
/**
* @ var string [] $attributes
*/
$attributes = $index -> getAttribute ( 'attributes' , []);
$found = \array_search ( $key , $attributes );
if ( $found !== false ) {
$attributes [ $found ] = $newKey ;
$index -> setAttribute ( 'attributes' , $attributes );
$dbForProject -> updateDocument ( 'indexes' , $index -> getId (), $index );
}
2024-09-05 08:41:50 +00:00
}
2024-08-16 06:18:52 +00:00
} else {
2025-05-26 05:42:11 +00:00
$attribute = $dbForProject -> updateDocument ( 'attributes' , $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key , $attribute );
2023-03-22 15:49:08 +00:00
}
2023-03-10 10:06:10 +00:00
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collection -> getId ());
2023-02-26 12:33:18 +00:00
2023-10-03 18:56:36 +00:00
$queueForEvents
2023-02-26 15:44:22 +00:00
-> setContext ( 'collection' , $collection )
2025-05-15 11:33:00 +00:00
-> setContext ( 'database' , $database )
2023-02-26 15:44:22 +00:00
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collection -> getId ())
2023-03-10 10:06:10 +00:00
-> setParam ( 'attributeId' , $attribute -> getId ());
2023-02-26 15:44:22 +00:00
2023-02-26 12:33:18 +00:00
return $attribute ;
}
2023-10-19 03:19:40 +00:00
App :: init ()
-> groups ([ 'api' , 'database' ])
-> inject ( 'request' )
-> inject ( 'dbForProject' )
-> action ( function ( Request $request , Database $dbForProject ) {
$timeout = \intval ( $request -> getHeader ( 'x-appwrite-timeout' ));
if ( ! empty ( $timeout ) && App :: isDevelopment ()) {
$dbForProject -> setTimeout ( $timeout );
}
});
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create database' )
2022-06-22 10:51:49 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'event' , 'databases.[databaseId].create' )
-> label ( 'scope' , 'databases.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'database.create' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{response.$id}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'databases' ,
2025-01-17 04:31:39 +00:00
name : 'create' ,
description : '/docs/references/databases/create.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_DATABASE ,
)
],
contentType : ContentType :: JSON
))
2023-01-20 22:22:16 +00:00
-> param ( 'databaseId' , '' , new CustomId (), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.' )
2023-05-18 13:25:22 +00:00
-> param ( 'name' , '' , new Text ( 128 ), 'Database name. Max length: 128 chars.' )
2023-08-22 20:11:33 +00:00
-> param ( 'enabled' , true , new Boolean (), 'Is the database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.' , true )
2022-06-22 10:51:49 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-05-15 12:14:09 +00:00
-> action ( function ( string $databaseId , string $name , bool $enabled , Response $response , Database $dbForProject , Event $queueForEvents ) {
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
$databaseId = $databaseId === 'unique()'
? ID :: unique ()
: $databaseId ;
2022-06-22 10:51:49 +00:00
try {
$dbForProject -> createDocument ( 'databases' , new Document ([
2022-08-14 14:22:38 +00:00
'$id' => $databaseId ,
2022-06-22 10:51:49 +00:00
'name' => $name ,
2023-05-18 13:25:22 +00:00
'enabled' => $enabled ,
2022-06-22 10:51:49 +00:00
'search' => implode ( ' ' , [ $databaseId , $name ]),
]));
2025-05-15 11:33:00 +00:00
} catch ( DuplicateException ) {
throw new Exception ( Exception :: DATABASE_ALREADY_EXISTS );
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
}
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
$collections = ( Config :: getParam ( 'collections' , [])[ 'databases' ] ? ? [])[ 'collections' ] ? ? [];
if ( empty ( $collections )) {
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'The "collections" collection is not configured.' );
}
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
$attributes = [];
foreach ( $collections [ 'attributes' ] as $attribute ) {
$attributes [] = new Document ( $attribute );
}
$indexes = [];
foreach ( $collections [ 'indexes' ] as $index ) {
$indexes [] = new Document ( $index );
}
try {
2025-05-26 05:42:11 +00:00
$dbForProject -> createCollection ( 'database_' . $database -> getSequence (), $attributes , $indexes );
2023-06-15 05:29:03 +00:00
} catch ( DuplicateException ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_ALREADY_EXISTS );
2025-05-15 11:33:00 +00:00
} catch ( IndexException ) {
throw new Exception ( Exception :: INDEX_INVALID );
} catch ( LimitException ) {
throw new Exception ( Exception :: COLLECTION_LIMIT_EXCEEDED );
2022-06-22 10:51:49 +00:00
}
2022-12-20 16:11:30 +00:00
$queueForEvents -> setParam ( 'databaseId' , $database -> getId ());
2022-06-22 10:51:49 +00:00
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $database , Response :: MODEL_DATABASE );
2022-06-22 10:51:49 +00:00
});
App :: get ( '/v1/databases' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List databases' )
2022-06-22 10:51:49 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'databases.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'databases' ,
2025-01-17 04:31:39 +00:00
name : 'list' ,
description : '/docs/references/databases/list.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DATABASE_LIST ,
)
],
contentType : ContentType :: JSON
))
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Databases (), '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 ( ', ' , Databases :: ALLOWED_ATTRIBUTES ), true )
2022-06-22 10:51:49 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-08-25 09:59:28 +00:00
-> action ( function ( array $queries , string $search , Response $response , Database $dbForProject ) {
2024-11-04 07:57:08 +00:00
$queries = Query :: parseQueries ( $queries );
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 09:40:17 +00:00
$queries [] = Query :: search ( 'search' , $search );
2022-08-11 23:53:52 +00:00
}
2024-02-12 09:55:45 +00:00
/**
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2025-05-15 11:33:00 +00:00
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2024-10-17 05:41:24 +00:00
/** @var Query $cursor */
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 09:40:17 +00:00
$databaseId = $cursor -> getValue ();
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
$cursorDocument = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 09:40:17 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Database ' { $databaseId } ' for the 'cursor' value not found. " );
2022-06-22 10:51:49 +00:00
}
2022-08-23 09:40:17 +00:00
$cursor -> setValue ( $cursorDocument );
2022-06-22 10:51:49 +00:00
}
2025-04-16 11:33:14 +00:00
try {
2025-04-17 04:46:26 +00:00
$databases = $dbForProject -> find ( 'databases' , $queries );
2025-05-15 11:33:00 +00:00
$total = $dbForProject -> count ( 'databases' , $queries , APP_LIMIT_COUNT );
} catch ( OrderException ) {
throw new Exception ( Exception :: DATABASE_QUERY_ORDER_NULL );
} catch ( QueryException ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID );
2025-04-16 11:33:14 +00:00
}
2025-05-15 11:33:00 +00:00
2022-06-22 10:51:49 +00:00
$response -> dynamic ( new Document ([
2025-04-17 04:46:26 +00:00
'databases' => $databases ,
'total' => $total ,
2022-06-22 10:51:49 +00:00
]), Response :: MODEL_DATABASE_LIST );
});
App :: get ( '/v1/databases/:databaseId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get database' )
2022-06-22 10:51:49 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'databases.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'databases' ,
2025-01-17 04:31:39 +00:00
name : 'get' ,
description : '/docs/references/databases/get.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DATABASE ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-08-10 02:18:18 +00:00
-> action ( function ( string $databaseId , Response $response , Database $dbForProject ) {
2023-05-24 10:09:08 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
$response -> dynamic ( $database , Response :: MODEL_DATABASE );
});
App :: get ( '/v1/databases/:databaseId/logs' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List database logs' )
2022-06-22 10:51:49 +00:00
-> groups ([ 'api' , 'database' ])
2022-08-19 14:14:39 +00:00
-> label ( 'scope' , 'databases.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'logs' ,
2025-01-17 04:31:39 +00:00
name : 'listLogs' ,
description : '/docs/references/databases/get-logs.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_LOG_LIST ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2023-05-16 12:56:20 +00:00
-> param ( 'queries' , [], new Queries ([ new Limit (), new Offset ()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset' , true )
2022-06-22 10:51:49 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'locale' )
-> inject ( 'geodb' )
2022-08-23 13:03:38 +00:00
-> action ( function ( string $databaseId , array $queries , Response $response , Database $dbForProject , Locale $locale , Reader $geodb ) {
2022-06-22 10:51:49 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2025-02-25 08:03:37 +00:00
// Temp fix for logs
$queries [] = Query :: or ([
2025-02-26 01:00:16 +00:00
Query :: greaterThan ( '$createdAt' , DateTime :: format ( new \DateTime ( '2025-02-26T01:30+00:00' ))),
2025-02-25 08:03:37 +00:00
Query :: lessThan ( '$createdAt' , DateTime :: format ( new \DateTime ( '2025-02-13T00:00+00:00' ))),
]);
2025-02-25 05:51:11 +00:00
2022-06-22 10:51:49 +00:00
$audit = new Audit ( $dbForProject );
$resource = 'database/' . $databaseId ;
2025-02-25 07:26:00 +00:00
$logs = $audit -> getLogsByResource ( $resource , $queries );
2022-08-23 13:10:27 +00:00
2022-08-23 13:03:38 +00:00
$output = [];
2022-06-22 10:51:49 +00:00
foreach ( $logs as $i => & $log ) {
$log [ 'userAgent' ] = ( ! empty ( $log [ 'userAgent' ])) ? $log [ 'userAgent' ] : 'UNKNOWN' ;
$detector = new Detector ( $log [ 'userAgent' ]);
$detector -> skipBotDetection (); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$os = $detector -> getOS ();
$client = $detector -> getClient ();
$device = $detector -> getDevice ();
$output [ $i ] = new Document ([
'event' => $log [ 'event' ],
2023-07-12 17:27:57 +00:00
'userId' => ID :: custom ( $log [ 'data' ][ 'userId' ]),
2022-06-22 10:51:49 +00:00
'userEmail' => $log [ 'data' ][ 'userEmail' ] ? ? null ,
'userName' => $log [ 'data' ][ 'userName' ] ? ? null ,
'mode' => $log [ 'data' ][ 'mode' ] ? ? null ,
'ip' => $log [ 'ip' ],
'time' => $log [ 'time' ],
'osCode' => $os [ 'osCode' ],
'osName' => $os [ 'osName' ],
'osVersion' => $os [ 'osVersion' ],
'clientType' => $client [ 'clientType' ],
'clientCode' => $client [ 'clientCode' ],
'clientName' => $client [ 'clientName' ],
'clientVersion' => $client [ 'clientVersion' ],
'clientEngine' => $client [ 'clientEngine' ],
'clientEngineVersion' => $client [ 'clientEngineVersion' ],
'deviceName' => $device [ 'deviceName' ],
'deviceBrand' => $device [ 'deviceBrand' ],
'deviceModel' => $device [ 'deviceModel' ]
]);
$record = $geodb -> get ( $log [ 'ip' ]);
if ( $record ) {
$output [ $i ][ 'countryCode' ] = $locale -> getText ( 'countries.' . strtolower ( $record [ 'country' ][ 'iso_code' ]), false ) ? \strtolower ( $record [ 'country' ][ 'iso_code' ]) : '--' ;
$output [ $i ][ 'countryName' ] = $locale -> getText ( 'countries.' . strtolower ( $record [ 'country' ][ 'iso_code' ]), $locale -> getText ( 'locale.country.unknown' ));
} else {
$output [ $i ][ 'countryCode' ] = '--' ;
$output [ $i ][ 'countryName' ] = $locale -> getText ( 'locale.country.unknown' );
}
}
$response -> dynamic ( new Document ([
2025-02-25 07:26:00 +00:00
'total' => $audit -> countLogsByResource ( $resource , $queries ),
2025-02-25 06:21:35 +00:00
'logs' => $output ,
2022-06-22 10:51:49 +00:00
]), Response :: MODEL_LOG_LIST );
});
App :: put ( '/v1/databases/:databaseId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update database' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'scope' , 'databases.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].update' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'database.update' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{response.$id}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'databases' ,
2025-01-17 04:31:39 +00:00
name : 'update' ,
description : '/docs/references/databases/update.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DATABASE ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-07-08 05:43:20 +00:00
-> param ( 'name' , null , new Text ( 128 ), 'Database name. Max length: 128 chars.' )
2023-08-22 20:11:33 +00:00
-> param ( 'enabled' , true , new Boolean (), 'Is database enabled? When set to \'disabled\', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled.' , true )
2022-06-22 13:30:35 +00:00
-> inject ( 'response' )
2022-06-22 10:51:49 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2023-09-27 15:51:17 +00:00
-> action ( function ( string $databaseId , string $name , bool $enabled , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-05-24 10:09:08 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2025-05-15 11:33:00 +00:00
$database
2024-03-11 12:03:30 +00:00
-> setAttribute ( 'name' , $name )
-> setAttribute ( 'enabled' , $enabled )
2025-05-15 11:33:00 +00:00
-> setAttribute ( 'search' , implode ( ' ' , [ $databaseId , $name ]));
$database = $dbForProject -> updateDocument ( 'databases' , $databaseId , $database );
2022-06-22 10:51:49 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents -> setParam ( 'databaseId' , $database -> getId ());
2022-06-22 10:51:49 +00:00
$response -> dynamic ( $database , Response :: MODEL_DATABASE );
});
App :: delete ( '/v1/databases/:databaseId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete database' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'scope' , 'databases.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'database.delete' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'databases' ,
2025-01-17 04:31:39 +00:00
name : 'delete' ,
description : '/docs/references/databases/delete.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-16 23:18:51 +00:00
-> inject ( 'queueForDatabase' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-05-15 12:14:09 +00:00
-> action ( function ( string $databaseId , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2022-06-22 10:51:49 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
if ( ! $dbForProject -> deleteDocument ( 'databases' , $databaseId )) {
2025-05-15 11:33:00 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove database from database' );
2022-06-22 10:51:49 +00:00
}
2023-12-14 13:32:06 +00:00
$dbForProject -> purgeCachedDocument ( 'databases' , $database -> getId ());
2025-06-10 19:55:54 +00:00
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence ());
2022-06-22 10:51:49 +00:00
2023-10-16 23:18:51 +00:00
$queueForDatabase
-> setType ( DATABASE_TYPE_DELETE_DATABASE )
-> setDatabase ( $database );
2022-06-22 10:51:49 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $database -> getId ())
2023-07-18 11:16:33 +00:00
-> setPayload ( $response -> output ( $database , Response :: MODEL_DATABASE ));
2022-06-22 10:51:49 +00:00
$response -> noContent ();
});
App :: post ( '/v1/databases/:databaseId/collections' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create collection' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].create' )
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'collection.create' )
2022-08-08 15:44:11 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{response.$id}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'createCollection' ,
description : '/docs/references/databases/create-collection.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_COLLECTION ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2023-01-20 22:22:16 +00:00
-> param ( 'collectionId' , '' , new CustomId (), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.' )
2021-07-29 08:09:24 +00:00
-> param ( 'name' , '' , new Text ( 128 ), 'Collection name. Max length: 128 chars.' )
2023-10-13 13:43:23 +00:00
-> param ( 'permissions' , null , new Permissions ( APP_LIMIT_ARRAY_PARAMS_SIZE ), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
-> param ( 'documentSecurity' , false , new Boolean ( true ), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
2023-08-22 20:11:33 +00:00
-> param ( 'enabled' , true , new Boolean (), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.' , true )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2023-05-24 12:28:40 +00:00
-> inject ( 'mode' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2023-09-27 15:51:17 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $name , ? array $permissions , bool $documentSecurity , bool $enabled , Response $response , Database $dbForProject , string $mode , Event $queueForEvents ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2021-06-15 13:38:24 +00:00
2025-05-15 11:33:00 +00:00
$collectionId = $collectionId === 'unique()'
? ID :: unique ()
: $collectionId ;
2021-07-05 20:27:20 +00:00
2022-08-23 01:42:25 +00:00
// Map aggregate permissions into the multiple permissions they represent.
2024-11-06 07:36:27 +00:00
$permissions = Permission :: aggregate ( $permissions ) ? ? [];
2022-08-16 08:33:06 +00:00
2021-08-16 17:28:33 +00:00
try {
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> createDocument ( 'database_' . $database -> getSequence (), new Document ([
2022-08-14 14:22:38 +00:00
'$id' => $collectionId ,
2025-05-26 05:42:11 +00:00
'databaseInternalId' => $database -> getSequence (),
2022-06-22 10:51:49 +00:00
'databaseId' => $databaseId ,
2024-11-06 07:36:27 +00:00
'$permissions' => $permissions ,
2022-08-04 04:20:11 +00:00
'documentSecurity' => $documentSecurity ,
2023-05-18 13:25:22 +00:00
'enabled' => $enabled ,
2021-08-16 17:28:33 +00:00
'name' => $name ,
'search' => implode ( ' ' , [ $collectionId , $name ]),
]));
2025-05-15 11:33:00 +00:00
} catch ( DuplicateException ) {
throw new Exception ( Exception :: COLLECTION_ALREADY_EXISTS );
} catch ( LimitException ) {
throw new Exception ( Exception :: COLLECTION_LIMIT_EXCEEDED );
} catch ( NotFoundException ) {
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2022-01-24 08:20:02 +00:00
2025-05-15 11:33:00 +00:00
try {
$dbForProject -> createCollection (
2025-05-26 05:42:11 +00:00
id : 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-05-15 11:33:00 +00:00
permissions : $permissions ,
documentSecurity : $documentSecurity
);
2022-08-24 08:50:05 +00:00
} catch ( DuplicateException ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_ALREADY_EXISTS );
2025-05-15 11:33:00 +00:00
} catch ( IndexException ) {
throw new Exception ( Exception :: INDEX_INVALID );
2022-08-24 08:50:05 +00:00
} catch ( LimitException ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_LIMIT_EXCEEDED );
2021-08-16 17:28:33 +00:00
}
2019-05-09 06:54:39 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setContext ( 'database' , $database )
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collection -> getId ());
2022-09-07 11:11:10 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $collection , Response :: MODEL_COLLECTION );
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List collections' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2020-01-31 22:34:07 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'listCollections' ,
description : '/docs/references/databases/list-collections.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_COLLECTION_LIST ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Collections (), '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 ( ', ' , Collections :: ALLOWED_ATTRIBUTES ), true )
2021-07-05 19:19:18 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2025-05-15 12:14:09 +00:00
-> action ( function ( string $databaseId , array $queries , string $search , Response $response , Database $dbForProject ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2020-06-29 21:43:34 +00:00
2025-05-15 11:33:00 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2021-08-16 17:28:33 +00:00
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 12:30:28 +00:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-16 17:28:33 +00:00
}
2020-06-29 21:43:34 +00:00
2024-02-12 09:55:45 +00:00
/**
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2022-08-23 12:30:28 +00:00
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 12:30:28 +00:00
$collectionId = $cursor -> getValue ();
2025-05-15 11:33:00 +00:00
2025-05-26 05:42:11 +00:00
$cursorDocument = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-08-06 12:35:57 +00:00
2022-08-11 23:53:52 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 12:30:28 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Collection ' { $collectionId } ' for the 'cursor' value not found. " );
2022-08-11 23:53:52 +00:00
}
2022-08-23 12:30:28 +00:00
$cursor -> setValue ( $cursorDocument );
2021-08-06 12:35:57 +00:00
}
2025-05-26 05:42:11 +00:00
$collectionId = 'database_' . $database -> getSequence ();
2022-08-23 12:30:28 +00:00
2025-04-16 11:33:14 +00:00
try {
2025-05-15 11:33:00 +00:00
$collections = $dbForProject -> find ( $collectionId , $queries );
$total = $dbForProject -> count ( $collectionId , $queries , APP_LIMIT_COUNT );
} catch ( OrderException ) {
throw new Exception ( Exception :: DATABASE_QUERY_ORDER_NULL );
} catch ( QueryException ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID );
2025-04-16 11:33:14 +00:00
}
2025-05-15 11:33:00 +00:00
2021-07-25 14:47:18 +00:00
$response -> dynamic ( new Document ([
2025-04-17 04:46:26 +00:00
'collections' => $collections ,
'total' => $total ,
2020-10-30 19:53:27 +00:00
]), Response :: MODEL_COLLECTION_LIST );
2020-12-26 15:05:04 +00:00
});
2020-01-31 22:34:07 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get collection' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2020-01-31 22:34:07 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'getCollection' ,
description : '/docs/references/databases/get-collection.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_COLLECTION ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2021-12-10 12:27:11 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2025-05-15 12:14:09 +00:00
-> action ( function ( string $databaseId , string $collectionId , Response $response , Database $dbForProject ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2021-10-27 19:57:20 +00:00
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2021-08-20 10:04:57 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-08-20 11:38:57 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-08-20 11:38:57 +00:00
}
2021-12-28 15:48:36 +00:00
2022-06-22 10:51:49 +00:00
$response -> dynamic ( $collection , Response :: MODEL_COLLECTION );
2021-08-20 11:38:57 +00:00
});
2021-08-20 08:24:45 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/logs' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/logs' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List collection logs' )
2021-08-10 18:03:32 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'listCollectionLogs' ,
description : '/docs/references/databases/get-collection-logs.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_LOG_LIST ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2021-12-10 12:27:11 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
2023-05-16 12:56:20 +00:00
-> param ( 'queries' , [], new Queries ([ new Limit (), new Offset ()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset' , true )
2021-08-10 18:03:32 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2021-08-10 18:03:32 +00:00
-> inject ( 'locale' )
-> inject ( 'geodb' )
2022-08-23 13:03:38 +00:00
-> action ( function ( string $databaseId , string $collectionId , array $queries , Response $response , Database $dbForProject , Locale $locale , Reader $geodb ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2023-12-13 09:45:01 +00:00
2025-05-26 05:42:11 +00:00
$collectionDocument = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
$collection = $dbForProject -> getCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collectionDocument -> getSequence ());
2021-08-10 18:03:32 +00:00
2025-05-15 11:33:00 +00:00
if ( $collectionDocument -> isEmpty () || $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-08-10 18:03:32 +00:00
}
2025-02-25 05:51:11 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2025-02-25 08:03:37 +00:00
// Temp fix for logs
$queries [] = Query :: or ([
2025-02-26 01:00:16 +00:00
Query :: greaterThan ( '$createdAt' , DateTime :: format ( new \DateTime ( '2025-02-26T01:30+00:00' ))),
2025-02-25 08:03:37 +00:00
Query :: lessThan ( '$createdAt' , DateTime :: format ( new \DateTime ( '2025-02-13T00:00+00:00' ))),
]);
2025-02-25 05:51:11 +00:00
2021-12-27 12:45:23 +00:00
$audit = new Audit ( $dbForProject );
2022-06-22 10:51:49 +00:00
$resource = 'database/' . $databaseId . '/collection/' . $collectionId ;
2025-02-25 07:26:00 +00:00
$logs = $audit -> getLogsByResource ( $resource , $queries );
2021-08-10 18:03:32 +00:00
$output = [];
foreach ( $logs as $i => & $log ) {
$log [ 'userAgent' ] = ( ! empty ( $log [ 'userAgent' ])) ? $log [ 'userAgent' ] : 'UNKNOWN' ;
2021-12-12 17:58:17 +00:00
$detector = new Detector ( $log [ 'userAgent' ]);
$detector -> skipBotDetection (); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
2021-08-10 18:03:32 +00:00
2021-12-12 17:58:17 +00:00
$os = $detector -> getOS ();
$client = $detector -> getClient ();
$device = $detector -> getDevice ();
2021-08-10 18:03:32 +00:00
$output [ $i ] = new Document ([
'event' => $log [ 'event' ],
2023-07-12 17:27:57 +00:00
'userId' => $log [ 'data' ][ 'userId' ],
2021-08-14 10:13:24 +00:00
'userEmail' => $log [ 'data' ][ 'userEmail' ] ? ? null ,
'userName' => $log [ 'data' ][ 'userName' ] ? ? null ,
'mode' => $log [ 'data' ][ 'mode' ] ? ? null ,
2021-08-10 18:03:32 +00:00
'ip' => $log [ 'ip' ],
'time' => $log [ 'time' ],
2021-12-12 17:58:17 +00:00
'osCode' => $os [ 'osCode' ],
'osName' => $os [ 'osName' ],
'osVersion' => $os [ 'osVersion' ],
'clientType' => $client [ 'clientType' ],
'clientCode' => $client [ 'clientCode' ],
'clientName' => $client [ 'clientName' ],
'clientVersion' => $client [ 'clientVersion' ],
'clientEngine' => $client [ 'clientEngine' ],
'clientEngineVersion' => $client [ 'clientEngineVersion' ],
'deviceName' => $device [ 'deviceName' ],
'deviceBrand' => $device [ 'deviceBrand' ],
2021-12-12 17:59:12 +00:00
'deviceModel' => $device [ 'deviceModel' ]
2021-08-10 18:03:32 +00:00
]);
$record = $geodb -> get ( $log [ 'ip' ]);
if ( $record ) {
2022-05-23 14:54:50 +00:00
$output [ $i ][ 'countryCode' ] = $locale -> getText ( 'countries.' . strtolower ( $record [ 'country' ][ 'iso_code' ]), false ) ? \strtolower ( $record [ 'country' ][ 'iso_code' ]) : '--' ;
$output [ $i ][ 'countryName' ] = $locale -> getText ( 'countries.' . strtolower ( $record [ 'country' ][ 'iso_code' ]), $locale -> getText ( 'locale.country.unknown' ));
2021-08-10 18:03:32 +00:00
} else {
$output [ $i ][ 'countryCode' ] = '--' ;
$output [ $i ][ 'countryName' ] = $locale -> getText ( 'locale.country.unknown' );
}
}
2021-11-16 14:54:29 +00:00
$response -> dynamic ( new Document ([
2025-02-25 07:26:00 +00:00
'total' => $audit -> countLogsByResource ( $resource , $queries ),
2025-02-25 06:21:35 +00:00
'logs' => $output ,
2021-11-16 14:54:29 +00:00
]), Response :: MODEL_LOG_LIST );
2021-08-10 18:03:32 +00:00
});
2022-06-22 10:51:49 +00:00
App :: put ( '/v1/databases/:databaseId/collections/:collectionId' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update collection' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].update' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'collection.update' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'updateCollection' ,
description : '/docs/references/databases/update-collection.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_COLLECTION ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2021-12-10 12:27:11 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
2021-07-29 08:09:24 +00:00
-> param ( 'name' , null , new Text ( 128 ), 'Collection name. Max length: 128 chars.' )
2023-10-13 13:43:23 +00:00
-> param ( 'permissions' , null , new Permissions ( APP_LIMIT_ARRAY_PARAMS_SIZE ), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
-> param ( 'documentSecurity' , false , new Boolean ( true ), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
2023-08-22 20:11:33 +00:00
-> param ( 'enabled' , true , new Boolean (), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.' , true )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2023-05-26 11:38:09 +00:00
-> inject ( 'mode' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2023-09-27 15:51:17 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $name , ? array $permissions , bool $documentSecurity , bool $enabled , Response $response , Database $dbForProject , string $mode , Event $queueForEvents ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2019-10-01 04:57:41 +00:00
2021-06-16 20:36:18 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2020-06-29 21:43:34 +00:00
}
2019-10-01 04:57:41 +00:00
2022-08-04 00:08:16 +00:00
$permissions ? ? = $collection -> getPermissions () ? ? [];
2022-08-16 08:33:06 +00:00
2022-08-23 01:42:25 +00:00
// Map aggregate permissions into the multiple permissions they represent.
$permissions = Permission :: aggregate ( $permissions );
2022-08-16 08:33:06 +00:00
2021-12-13 12:42:04 +00:00
$enabled ? ? = $collection -> getAttribute ( 'enabled' , true );
2019-05-09 06:54:39 +00:00
2025-05-15 11:33:00 +00:00
$collection
2024-03-11 11:54:42 +00:00
-> setAttribute ( 'name' , $name )
-> setAttribute ( '$permissions' , $permissions )
-> setAttribute ( 'documentSecurity' , $documentSecurity )
-> setAttribute ( 'enabled' , $enabled )
2025-05-15 11:33:00 +00:00
-> setAttribute ( 'search' , \implode ( ' ' , [ $collectionId , $name ]));
$collection = $dbForProject -> updateDocument (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence (),
2025-05-15 11:33:00 +00:00
$collectionId ,
$collection
2024-11-04 07:57:08 +00:00
);
2024-03-11 11:54:42 +00:00
2025-05-26 05:42:11 +00:00
$dbForProject -> updateCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $permissions , $documentSecurity );
2020-01-04 15:45:28 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setContext ( 'database' , $database )
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collection -> getId ());
2022-04-13 12:39:31 +00:00
2021-07-25 14:47:18 +00:00
$response -> dynamic ( $collection , Response :: MODEL_COLLECTION );
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2022-06-22 10:51:49 +00:00
App :: delete ( '/v1/databases/:databaseId/collections/:collectionId' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete collection' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'collection.delete' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'deleteCollection' ,
description : '/docs/references/databases/delete-collection.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2021-12-10 12:27:11 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2023-10-16 23:18:51 +00:00
-> inject ( 'queueForDatabase' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-05-15 12:14:09 +00:00
-> action ( function ( string $databaseId , string $collectionId , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2019-05-09 06:54:39 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2019-05-09 06:54:39 +00:00
2021-06-11 14:25:52 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2020-06-29 21:43:34 +00:00
}
2020-01-04 15:45:28 +00:00
2025-05-26 05:42:11 +00:00
if ( ! $dbForProject -> deleteDocument ( 'database_' . $database -> getSequence (), $collectionId )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove collection from DB' );
2021-08-16 17:28:33 +00:00
}
2020-12-04 20:47:02 +00:00
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ());
2021-11-02 00:48:38 +00:00
2023-10-16 23:18:51 +00:00
$queueForDatabase
-> setType ( DATABASE_TYPE_DELETE_COLLECTION )
-> setDatabase ( $database )
-> setCollection ( $collection );
2021-09-02 16:45:03 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setContext ( 'database' , $database )
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
2023-05-24 10:09:08 +00:00
-> setPayload ( $response -> output ( $collection , Response :: MODEL_COLLECTION ));
2019-05-09 06:54:39 +00:00
2020-06-29 21:43:34 +00:00
$response -> noContent ();
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/string' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/string' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create string attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createStringAttribute' ,
description : '/docs/references/databases/create-string-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_STRING
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-10-26 20:51:40 +00:00
-> param ( 'size' , null , new Range ( 1 , APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH , Range :: TYPE_INTEGER ), 'Attribute size for text attributes, in number of characters.' )
2021-06-09 21:11:51 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-06-21 09:52:04 +00:00
-> param ( 'default' , null , new Text ( 0 , 0 ), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
2021-06-11 18:07:05 +00:00
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
2023-07-11 13:46:22 +00:00
-> param ( 'encrypt' , false , new Boolean (), 'Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.' , true )
2021-03-24 15:40:33 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
2025-05-26 05:36:55 +00:00
-> inject ( 'plan' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? int $size , ? bool $required , ? string $default , bool $array , bool $encrypt , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents , array $plan ) {
2025-06-12 16:20:21 +00:00
if ( ! App :: isDevelopment () && $encrypt && ! empty ( $plan ) && ! ( $plan [ 'databasesAllowEncrypt' ] ? ? false )) {
2025-05-26 05:36:55 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Encrypted string attributes are not available on your plan. Please upgrade to create encrypted string attributes.' );
}
2025-05-23 15:27:35 +00:00
if ( $encrypt && $size < APP_DATABASE_ENCRYPT_SIZE_MIN ) {
throw new Exception (
Exception :: GENERAL_BAD_REQUEST ,
" Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters. "
);
}
2021-08-16 23:21:00 +00:00
// Ensure attribute default is within required size
2023-05-30 02:52:59 +00:00
$validator = new Text ( $size , 0 );
2021-08-16 23:21:00 +00:00
if ( ! is_null ( $default ) && ! $validator -> isValid ( $default )) {
2022-07-26 14:56:59 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , $validator -> getDescription ());
2021-08-16 23:21:00 +00:00
}
2023-05-30 07:00:10 +00:00
$filters = [];
2023-05-30 07:17:55 +00:00
if ( $encrypt ) {
$filters [] = 'encrypt' ;
2023-05-30 07:00:10 +00:00
}
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_STRING ,
'size' => $size ,
'required' => $required ,
'default' => $default ,
'array' => $array ,
2023-05-30 07:00:10 +00:00
'filters' => $filters ,
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2025-05-26 05:36:55 +00:00
$attribute -> setAttribute ( 'encrypt' , $encrypt );
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_STRING );
2021-07-27 01:00:36 +00:00
});
2021-06-10 13:15:00 +00:00
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/email' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/email' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create email attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createEmailAttribute' ,
description : '/docs/references/databases/create-email-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_EMAIL ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-07-27 01:00:36 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2021-08-24 13:46:41 +00:00
-> param ( 'default' , null , new Email (), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
2021-07-27 01:00:36 +00:00
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_STRING ,
'size' => 254 ,
'required' => $required ,
'default' => $default ,
'array' => $array ,
2021-08-23 18:29:05 +00:00
'format' => APP_DATABASE_ATTRIBUTE_EMAIL ,
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-08-16 23:21:00 +00:00
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_EMAIL );
2021-07-27 01:00:36 +00:00
});
2021-07-21 13:23:12 +00:00
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/enum' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/enum' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create enum attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-09-10 20:09:11 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createEnumAttribute' ,
description : '/docs/references/databases/create-attribute-enum.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_ENUM ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-11-17 15:29:09 +00:00
-> param ( 'elements' , [], new ArrayList ( new Text ( DATABASE :: LENGTH_KEY ), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE :: LENGTH_KEY . ' characters long.' )
2021-09-10 20:09:11 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
-> param ( 'default' , null , new Text ( 0 ), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , array $elements , ? bool $required , ? string $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2021-12-16 10:15:55 +00:00
if ( ! is_null ( $default ) && ! in_array ( $default , $elements )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , 'Default value not found in elements' );
2021-12-16 10:15:55 +00:00
}
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-09-10 20:09:11 +00:00
'type' => Database :: VAR_STRING ,
2023-11-17 13:39:31 +00:00
'size' => Database :: LENGTH_KEY ,
2021-09-10 20:09:11 +00:00
'required' => $required ,
'default' => $default ,
'array' => $array ,
'format' => APP_DATABASE_ATTRIBUTE_ENUM ,
'formatOptions' => [ 'elements' => $elements ],
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-09-10 20:09:11 +00:00
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_ENUM );
2021-09-10 20:09:11 +00:00
});
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/ip' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/ip' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create IP address attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createIpAttribute' ,
description : '/docs/references/databases/create-ip-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_IP ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-07-27 01:00:36 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2021-08-24 13:46:41 +00:00
-> param ( 'default' , null , new IP (), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
2021-07-27 01:00:36 +00:00
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_STRING ,
'size' => 39 ,
'required' => $required ,
'default' => $default ,
'array' => $array ,
2021-08-23 18:29:05 +00:00
'format' => APP_DATABASE_ATTRIBUTE_IP ,
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-08-16 23:21:00 +00:00
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_IP );
2021-07-27 01:00:36 +00:00
});
2021-06-23 14:21:32 +00:00
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/url' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/url' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create URL attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createUrlAttribute' ,
description : '/docs/references/databases/create-url-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_URL ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-07-27 01:00:36 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2021-08-24 13:46:41 +00:00
-> param ( 'default' , null , new URL (), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
2021-07-27 01:00:36 +00:00
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_STRING ,
2021-08-22 12:59:44 +00:00
'size' => 2000 ,
2021-06-09 21:11:51 +00:00
'required' => $required ,
2021-07-02 17:29:03 +00:00
'default' => $default ,
2021-06-09 21:11:51 +00:00
'array' => $array ,
2021-08-23 18:29:05 +00:00
'format' => APP_DATABASE_ATTRIBUTE_URL ,
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-08-16 23:21:00 +00:00
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_URL );
2021-07-21 13:23:12 +00:00
});
2021-03-24 15:40:33 +00:00
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/integer' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/integer' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create integer attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createIntegerAttribute' ,
description : '/docs/references/databases/create-integer-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_INTEGER ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-07-21 13:23:12 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2021-07-23 21:38:14 +00:00
-> param ( 'min' , null , new Integer (), 'Minimum value to enforce on new documents' , true )
-> param ( 'max' , null , new Integer (), 'Maximum value to enforce on new documents' , true )
2021-07-21 13:23:12 +00:00
-> param ( 'default' , null , new Integer (), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? int $min , ? int $max , ? int $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2021-08-16 23:21:00 +00:00
// Ensure attribute default is within range
2025-02-26 16:41:26 +00:00
$min ? ? = PHP_INT_MIN ;
$max ? ? = PHP_INT_MAX ;
2021-10-25 20:10:39 +00:00
if ( $min > $max ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , 'Minimum value must be lesser than maximum value' );
2021-10-25 20:10:39 +00:00
}
2021-08-16 23:21:00 +00:00
$validator = new Range ( $min , $max , Database :: VAR_INTEGER );
if ( ! is_null ( $default ) && ! $validator -> isValid ( $default )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , $validator -> getDescription ());
2021-08-16 23:21:00 +00:00
}
2021-12-29 14:50:21 +00:00
$size = $max > 2147483647 ? 8 : 4 ; // Automatically create BigInt depending on max value
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_INTEGER ,
2021-12-29 14:50:21 +00:00
'size' => $size ,
2021-07-21 13:23:12 +00:00
'required' => $required ,
'default' => $default ,
'array' => $array ,
2021-08-23 18:29:05 +00:00
'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE ,
2021-08-21 21:48:07 +00:00
'formatOptions' => [
2021-07-27 18:19:37 +00:00
'min' => $min ,
'max' => $max ,
2021-08-21 21:48:07 +00:00
],
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-08-16 23:21:00 +00:00
2021-08-24 18:57:34 +00:00
$formatOptions = $attribute -> getAttribute ( 'formatOptions' , []);
if ( ! empty ( $formatOptions )) {
$attribute -> setAttribute ( 'min' , \intval ( $formatOptions [ 'min' ]));
$attribute -> setAttribute ( 'max' , \intval ( $formatOptions [ 'max' ]));
}
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_INTEGER );
2021-07-21 13:23:12 +00:00
});
2021-03-24 15:40:33 +00:00
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/float' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/float' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create float attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createFloatAttribute' ,
description : '/docs/references/databases/create-float-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_FLOAT ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-07-21 13:23:12 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2021-07-23 21:38:14 +00:00
-> param ( 'min' , null , new FloatValidator (), 'Minimum value to enforce on new documents' , true )
-> param ( 'max' , null , new FloatValidator (), 'Maximum value to enforce on new documents' , true )
2021-07-21 14:26:08 +00:00
-> param ( 'default' , null , new FloatValidator (), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
2021-07-21 13:23:12 +00:00
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? float $min , ? float $max , ? float $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2021-08-16 23:21:00 +00:00
// Ensure attribute default is within range
2025-02-26 16:41:26 +00:00
$min ? ? = - PHP_FLOAT_MAX ;
$max ? ? = PHP_FLOAT_MAX ;
2021-10-25 20:10:39 +00:00
if ( $min > $max ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , 'Minimum value must be lesser than maximum value' );
2021-10-25 20:10:39 +00:00
}
2021-08-16 23:21:00 +00:00
$validator = new Range ( $min , $max , Database :: VAR_FLOAT );
2024-11-04 07:57:08 +00:00
if ( ! \is_null ( $default ) && ! $validator -> isValid ( $default )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_VALUE_INVALID , $validator -> getDescription ());
2021-08-16 23:21:00 +00:00
}
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_FLOAT ,
2021-07-21 13:23:12 +00:00
'required' => $required ,
2021-07-27 01:00:36 +00:00
'size' => 0 ,
2021-07-21 13:23:12 +00:00
'default' => $default ,
2021-07-27 01:00:36 +00:00
'array' => $array ,
2021-08-23 18:29:05 +00:00
'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE ,
2021-08-21 21:48:07 +00:00
'formatOptions' => [
2021-07-27 18:19:37 +00:00
'min' => $min ,
'max' => $max ,
2021-08-21 21:48:07 +00:00
],
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-08-16 23:21:00 +00:00
2021-08-24 18:57:34 +00:00
$formatOptions = $attribute -> getAttribute ( 'formatOptions' , []);
if ( ! empty ( $formatOptions )) {
$attribute -> setAttribute ( 'min' , \floatval ( $formatOptions [ 'min' ]));
$attribute -> setAttribute ( 'max' , \floatval ( $formatOptions [ 'max' ]));
}
2022-09-07 11:02:36 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_FLOAT );
2021-07-21 13:23:12 +00:00
});
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/boolean' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/boolean' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create boolean attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createBooleanAttribute' ,
description : '/docs/references/databases/create-boolean-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_BOOLEAN ,
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-07-21 13:23:12 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
-> param ( 'default' , null , new Boolean (), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , true )
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? bool $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2022-06-22 10:51:49 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
2021-12-16 15:04:30 +00:00
'key' => $key ,
2021-07-27 01:00:36 +00:00
'type' => Database :: VAR_BOOLEAN ,
'size' => 0 ,
2021-06-09 21:11:51 +00:00
'required' => $required ,
2021-07-02 17:29:03 +00:00
'default' => $default ,
2021-06-09 21:11:51 +00:00
'array' => $array ,
2022-12-20 16:11:30 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2021-08-16 23:21:00 +00:00
2022-09-07 11:11:10 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_BOOLEAN );
2021-03-24 15:40:33 +00:00
});
2022-07-25 08:53:41 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/datetime' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/datetime' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create datetime attribute' )
2022-07-25 08:53:41 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.create' )
2022-08-19 05:05:39 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createDatetimeAttribute' ,
description : '/docs/references/databases/create-datetime-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_DATETIME ,
)
]
))
2022-07-25 08:53:41 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2022-07-25 08:53:41 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2024-11-04 09:13:38 +00:00
-> 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 attribute is required.' , true , [ 'dbForProject' ])
2022-07-25 08:53:41 +00:00
-> param ( 'array' , false , new Boolean (), 'Is attribute an array?' , true )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , bool $array , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2023-05-30 07:00:10 +00:00
$filters [] = 'datetime' ;
2022-07-25 08:53:41 +00:00
$attribute = createAttribute ( $databaseId , $collectionId , new Document ([
'key' => $key ,
'type' => Database :: VAR_DATETIME ,
'size' => 0 ,
'required' => $required ,
'default' => $default ,
'array' => $array ,
2023-05-30 07:00:10 +00:00
'filters' => $filters ,
2023-10-01 17:39:26 +00:00
]), $response , $dbForProject , $queueForDatabase , $queueForEvents );
2022-07-25 08:53:41 +00:00
2022-09-07 11:11:10 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_DATETIME );
2022-07-25 08:53:41 +00:00
});
2023-03-12 15:06:56 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/attributes/relationship' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/relationship' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create relationship attribute' )
2023-03-12 14:04:18 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create' )
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-03-12 14:04:18 +00:00
-> label ( 'audits.event' , 'attribute.create' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'createRelationshipAttribute' ,
description : '/docs/references/databases/create-relationship-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_ATTRIBUTE_RELATIONSHIP ,
)
]
))
2023-03-12 14:04:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2023-03-29 00:37:03 +00:00
-> param ( 'relatedCollectionId' , '' , new UID (), 'Related Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2023-03-28 13:24:18 +00:00
-> 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' )
2023-03-12 14:04:18 +00:00
-> param ( 'twoWay' , false , new Boolean (), 'Is Two Way?' , true )
2023-03-24 03:57:56 +00:00
-> param ( 'key' , null , new Key (), 'Attribute Key.' , true )
2023-03-24 05:08:50 +00:00
-> param ( 'twoWayKey' , null , new Key (), 'Two Way Attribute Key.' , true )
2023-03-28 13:24:18 +00:00
-> 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 )
2023-03-12 14:04:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-09-27 15:51:17 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
2023-03-12 14:04:18 +00:00
-> action ( function (
string $databaseId ,
string $collectionId ,
string $relatedCollectionId ,
string $type ,
bool $twoWay ,
2023-03-24 03:57:56 +00:00
? string $key ,
2023-03-23 03:58:41 +00:00
? string $twoWayKey ,
2023-03-12 14:04:18 +00:00
string $onDelete ,
Response $response ,
Database $dbForProject ,
2023-09-27 15:51:17 +00:00
EventDatabase $queueForDatabase ,
Event $queueForEvents
2023-05-24 10:09:08 +00:00
) {
2023-03-24 03:57:56 +00:00
$key ? ? = $relatedCollectionId ;
2023-03-23 03:58:41 +00:00
$twoWayKey ? ? = $collectionId ;
2023-03-22 15:49:08 +00:00
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2023-11-16 13:56:52 +00:00
2024-02-25 08:26:02 +00:00
if ( $database -> isEmpty ()) {
2023-11-16 13:56:52 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
$collection = $dbForProject -> getCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ());
2023-11-16 13:56:52 +00:00
if ( $collection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$relatedCollectionDocument = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId );
$relatedCollection = $dbForProject -> getCollection ( 'database_' . $database -> getSequence () . '_collection_' . $relatedCollectionDocument -> getSequence ());
2023-11-16 13:56:52 +00:00
if ( $relatedCollection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
$attributes = $collection -> getAttribute ( 'attributes' , []);
2025-05-15 11:33:00 +00:00
2023-11-16 13:56:52 +00:00
/** @var Document[] $attributes */
foreach ( $attributes as $attribute ) {
2024-02-25 08:26:02 +00:00
if ( $attribute -> getAttribute ( 'type' ) !== Database :: VAR_RELATIONSHIP ) {
continue ;
}
2023-11-16 13:56:52 +00:00
if ( \strtolower ( $attribute -> getId ()) === \strtolower ( $key )) {
throw new Exception ( Exception :: ATTRIBUTE_ALREADY_EXISTS );
}
if (
\strtolower ( $attribute -> getAttribute ( 'options' )[ 'twoWayKey' ]) === \strtolower ( $twoWayKey ) &&
$attribute -> getAttribute ( 'options' )[ 'relatedCollection' ] === $relatedCollection -> getId ()
) {
2024-02-25 08:26:02 +00:00
// Console should provide a unique twoWayKey input!
2024-02-13 10:29:36 +00:00
throw new Exception ( Exception :: ATTRIBUTE_ALREADY_EXISTS , 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.' );
2023-11-16 13:56:52 +00:00
}
if (
$type === Database :: RELATION_MANY_TO_MANY &&
$attribute -> getAttribute ( 'options' )[ 'relationType' ] === Database :: RELATION_MANY_TO_MANY &&
$attribute -> getAttribute ( 'options' )[ 'relatedCollection' ] === $relatedCollection -> getId ()
) {
throw new Exception ( Exception :: ATTRIBUTE_ALREADY_EXISTS , 'Creating more than one "manyToMany" relationship on the same collection is currently not permitted.' );
}
}
2023-03-22 15:49:08 +00:00
$attribute = createAttribute (
$databaseId ,
$collectionId ,
new Document ([
'key' => $key ,
'type' => Database :: VAR_RELATIONSHIP ,
'size' => 0 ,
'required' => false ,
'default' => null ,
'array' => false ,
'filters' => [],
'options' => [
'relatedCollection' => $relatedCollectionId ,
'relationType' => $type ,
'twoWay' => $twoWay ,
'twoWayKey' => $twoWayKey ,
'onDelete' => $onDelete ,
]
]),
$response ,
$dbForProject ,
2023-09-27 15:51:17 +00:00
$queueForDatabase ,
$queueForEvents
2023-03-22 15:49:08 +00:00
);
$options = $attribute -> getAttribute ( 'options' , []);
foreach ( $options as $key => $option ) {
$attribute -> setAttribute ( $key , $option );
}
$response
2023-03-23 03:58:41 +00:00
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
2023-03-22 15:49:08 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_RELATIONSHIP );
2023-03-12 14:04:18 +00:00
});
2022-07-25 08:53:41 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/attributes' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List attributes' )
2021-03-25 19:52:57 +00:00
-> groups ([ 'api' , 'database' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'listAttributes' ,
description : '/docs/references/databases/list-attributes.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_LIST
)
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2023-07-20 18:56:21 +00:00
-> 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 )
2021-03-25 19:52:57 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2023-07-20 18:50:53 +00:00
-> action ( function ( string $databaseId , string $collectionId , array $queries , Response $response , Database $dbForProject ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2023-03-25 03:28:07 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-06-11 14:25:52 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2025-05-15 11:33:00 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2023-08-03 12:04:28 +00:00
2023-08-22 03:25:55 +00:00
\array_push (
$queries ,
2025-05-26 05:42:11 +00:00
Query :: equal ( 'databaseInternalId' , [ $database -> getSequence ()]),
Query :: equal ( 'collectionInternalId' , [ $collection -> getSequence ()]),
2023-08-22 03:25:55 +00:00
);
2021-06-17 15:53:06 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2024-11-04 07:57:08 +00:00
2023-08-22 03:25:55 +00:00
$cursor = \reset ( $cursor );
2023-07-20 18:50:53 +00:00
if ( $cursor ) {
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2023-07-20 18:50:53 +00:00
$attributeId = $cursor -> getValue ();
2023-08-22 03:25:55 +00:00
2025-05-15 11:33:00 +00:00
try {
$cursorDocument = $dbForProject -> findOne ( 'attributes' , [
2025-05-26 05:42:11 +00:00
Query :: equal ( 'databaseInternalId' , [ $database -> getSequence ()]),
Query :: equal ( 'collectionInternalId' , [ $collection -> getSequence ()]),
2025-05-15 11:33:00 +00:00
Query :: equal ( 'key' , [ $attributeId ]),
]);
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
if ( $cursorDocument -> isEmpty ()) {
2023-07-20 18:50:53 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Attribute ' { $attributeId } ' for the 'cursor' value not found. " );
}
2023-08-22 03:25:55 +00:00
2025-05-15 11:33:00 +00:00
$cursor -> setValue ( $cursorDocument );
2023-07-20 18:50:53 +00:00
}
2023-08-03 12:09:13 +00:00
2025-04-16 11:33:14 +00:00
try {
$attributes = $dbForProject -> find ( 'attributes' , $queries );
2025-05-15 11:33:00 +00:00
$total = $dbForProject -> count ( 'attributes' , $queries , APP_LIMIT_COUNT );
} catch ( OrderException ) {
throw new Exception ( Exception :: DATABASE_QUERY_ORDER_NULL );
} catch ( QueryException ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID );
2025-04-16 11:33:14 +00:00
}
2021-06-17 15:53:06 +00:00
2025-05-26 05:36:55 +00:00
foreach ( $attributes as $attribute ) {
if ( $attribute -> getAttribute ( 'type' ) === Database :: VAR_STRING ) {
$filters = $attribute -> getAttribute ( 'filters' , []);
$attribute -> setAttribute ( 'encrypt' , in_array ( 'encrypt' , $filters ));
}
2025-04-16 11:33:14 +00:00
}
2021-06-17 15:53:06 +00:00
2021-07-25 14:47:18 +00:00
$response -> dynamic ( new Document ([
2023-08-22 03:25:55 +00:00
'attributes' => $attributes ,
'total' => $total ,
2021-06-08 20:12:14 +00:00
]), Response :: MODEL_ATTRIBUTE_LIST );
2021-03-25 19:52:57 +00:00
});
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/attributes/:key' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get attribute' )
2021-03-25 19:52:57 +00:00
-> groups ([ 'api' , 'database' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'getAttribute' ,
description : '/docs/references/databases/get-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : [
Response :: MODEL_ATTRIBUTE_BOOLEAN ,
Response :: MODEL_ATTRIBUTE_INTEGER ,
Response :: MODEL_ATTRIBUTE_FLOAT ,
Response :: MODEL_ATTRIBUTE_EMAIL ,
Response :: MODEL_ATTRIBUTE_ENUM ,
Response :: MODEL_ATTRIBUTE_URL ,
Response :: MODEL_ATTRIBUTE_IP ,
Response :: MODEL_ATTRIBUTE_DATETIME ,
Response :: MODEL_ATTRIBUTE_RELATIONSHIP ,
Response :: MODEL_ATTRIBUTE_STRING
]
),
]
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-03-25 19:52:57 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-08-10 02:18:18 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , Response $response , Database $dbForProject ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2021-06-08 20:12:14 +00:00
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-03-25 19:52:57 +00:00
2021-12-27 17:07:35 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2025-05-26 05:42:11 +00:00
$attribute = $dbForProject -> getDocument ( 'attributes' , $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key );
2021-08-25 19:37:07 +00:00
2021-12-27 17:07:35 +00:00
if ( $attribute -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_NOT_FOUND );
2021-06-09 21:11:51 +00:00
}
2021-08-25 19:37:07 +00:00
// Select response model based on type and format
2021-08-16 23:21:00 +00:00
$type = $attribute -> getAttribute ( 'type' );
2021-08-24 18:01:53 +00:00
$format = $attribute -> getAttribute ( 'format' );
2023-03-20 11:20:24 +00:00
$options = $attribute -> getAttribute ( 'options' , []);
2025-05-26 05:36:55 +00:00
$filters = $attribute -> getAttribute ( 'filters' , []);
2023-03-20 11:20:24 +00:00
foreach ( $options as $key => $option ) {
$attribute -> setAttribute ( $key , $option );
}
2021-08-16 23:21:00 +00:00
2022-05-23 14:54:50 +00:00
$model = match ( $type ) {
2021-08-16 23:21:00 +00:00
Database :: VAR_BOOLEAN => Response :: MODEL_ATTRIBUTE_BOOLEAN ,
Database :: VAR_INTEGER => Response :: MODEL_ATTRIBUTE_INTEGER ,
Database :: VAR_FLOAT => Response :: MODEL_ATTRIBUTE_FLOAT ,
2023-03-29 00:37:51 +00:00
Database :: VAR_DATETIME => Response :: MODEL_ATTRIBUTE_DATETIME ,
Database :: VAR_RELATIONSHIP => Response :: MODEL_ATTRIBUTE_RELATIONSHIP ,
2022-05-23 14:54:50 +00:00
Database :: VAR_STRING => match ( $format ) {
2021-08-25 19:37:07 +00:00
APP_DATABASE_ATTRIBUTE_EMAIL => Response :: MODEL_ATTRIBUTE_EMAIL ,
2021-09-10 20:14:12 +00:00
APP_DATABASE_ATTRIBUTE_ENUM => Response :: MODEL_ATTRIBUTE_ENUM ,
2021-08-16 23:21:00 +00:00
APP_DATABASE_ATTRIBUTE_IP => Response :: MODEL_ATTRIBUTE_IP ,
APP_DATABASE_ATTRIBUTE_URL => Response :: MODEL_ATTRIBUTE_URL ,
default => Response :: MODEL_ATTRIBUTE_STRING ,
},
default => Response :: MODEL_ATTRIBUTE ,
};
2025-05-26 05:36:55 +00:00
$attribute -> setAttribute ( 'encrypt' , in_array ( 'encrypt' , $filters ));
2021-08-16 23:21:00 +00:00
$response -> dynamic ( $attribute , $model );
2021-03-25 19:52:57 +00:00
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update string attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateStringAttribute' ,
description : '/docs/references/databases/update-string-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_STRING ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-27 07:45:11 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-05-30 02:52:59 +00:00
-> param ( 'default' , null , new Nullable ( new Text ( 0 , 0 )), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-10-29 06:04:37 +00:00
-> param ( 'size' , null , new Range ( 1 , APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH , Range :: TYPE_INTEGER ), 'Maximum size of the string attribute.' , true )
2024-09-05 03:45:13 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2021-03-24 15:40:33 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2024-09-05 03:45:13 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , ? int $size , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_STRING ,
2024-09-05 03:45:13 +00:00
size : $size ,
2023-02-26 12:33:18 +00:00
default : $default ,
2024-08-16 06:18:52 +00:00
required : $required ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
2021-03-24 15:40:33 +00:00
2023-02-26 12:33:18 +00:00
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_STRING );
});
2021-03-24 15:40:33 +00:00
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update email attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateEmailAttribute' ,
description : '/docs/references/databases/update-email-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_EMAIL ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new Email ()), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_STRING ,
filter : APP_DATABASE_ATTRIBUTE_EMAIL ,
default : $default ,
2024-08-16 06:18:52 +00:00
required : $required ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
2021-06-10 13:15:00 +00:00
2023-02-26 12:33:18 +00:00
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_EMAIL );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update enum attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateEnumAttribute' ,
description : '/docs/references/databases/update-enum-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_ENUM ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-11-17 15:29:09 +00:00
-> param ( 'elements' , null , new ArrayList ( new Text ( DATABASE :: LENGTH_KEY ), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . DATABASE :: LENGTH_KEY . ' characters long.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new Text ( 0 )), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? array $elements , ? bool $required , ? string $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_STRING ,
filter : APP_DATABASE_ATTRIBUTE_ENUM ,
default : $default ,
2023-02-27 07:45:11 +00:00
required : $required ,
2024-08-16 06:18:52 +00:00
elements : $elements ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_ENUM );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update IP address attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateIpAttribute' ,
description : '/docs/references/databases/update-ip-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_IP ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new IP ()), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_STRING ,
filter : APP_DATABASE_ATTRIBUTE_IP ,
default : $default ,
2024-08-16 06:18:52 +00:00
required : $required ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_IP );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update URL attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateUrlAttribute' ,
description : '/docs/references/databases/update-url-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_URL ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new URL ()), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_STRING ,
filter : APP_DATABASE_ATTRIBUTE_URL ,
default : $default ,
2024-08-16 06:18:52 +00:00
required : $required ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_URL );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update integer attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateIntegerAttribute' ,
description : '/docs/references/databases/update-integer-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_INTEGER ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2025-02-20 10:33:58 +00:00
-> param ( 'min' , null , new Integer (), 'Minimum value to enforce on new documents' , true )
-> param ( 'max' , null , new Integer (), 'Maximum value to enforce on new documents' , true )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new Integer ()), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? int $min , ? int $max , ? int $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_INTEGER ,
default : $default ,
required : $required ,
min : $min ,
2024-08-16 06:18:52 +00:00
max : $max ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
2023-03-10 13:06:34 +00:00
$formatOptions = $attribute -> getAttribute ( 'formatOptions' , []);
if ( ! empty ( $formatOptions )) {
$attribute -> setAttribute ( 'min' , \intval ( $formatOptions [ 'min' ]));
$attribute -> setAttribute ( 'max' , \intval ( $formatOptions [ 'max' ]));
}
2023-02-26 12:33:18 +00:00
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_INTEGER );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update float attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateFloatAttribute' ,
description : '/docs/references/databases/update-float-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_FLOAT ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2025-02-20 10:33:58 +00:00
-> param ( 'min' , null , new FloatValidator (), 'Minimum value to enforce on new documents' , true )
-> param ( 'max' , null , new FloatValidator (), 'Maximum value to enforce on new documents' , true )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new FloatValidator ()), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? float $min , ? float $max , ? float $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_FLOAT ,
default : $default ,
required : $required ,
min : $min ,
2024-08-16 06:18:52 +00:00
max : $max ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
2023-03-10 13:06:34 +00:00
$formatOptions = $attribute -> getAttribute ( 'formatOptions' , []);
if ( ! empty ( $formatOptions )) {
$attribute -> setAttribute ( 'min' , \floatval ( $formatOptions [ 'min' ]));
$attribute -> setAttribute ( 'max' , \floatval ( $formatOptions [ 'max' ]));
}
2023-02-26 12:33:18 +00:00
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_FLOAT );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update boolean attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateBooleanAttribute' ,
description : '/docs/references/databases/update-boolean-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_BOOLEAN ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2023-03-01 12:00:36 +00:00
-> param ( 'default' , null , new Nullable ( new Boolean ()), 'Default value for attribute when not provided. Cannot be set when attribute is required.' )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? bool $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_BOOLEAN ,
default : $default ,
2024-08-16 06:18:52 +00:00
required : $required ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_BOOLEAN );
});
2023-03-23 10:25:40 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update dateTime attribute' )
2023-02-26 12:33:18 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-02-26 12:33:18 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateDatetimeAttribute' ,
description : '/docs/references/databases/update-datetime-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_DATETIME ,
)
],
contentType : ContentType :: JSON
))
2023-02-26 12:33:18 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-02-26 15:44:22 +00:00
-> param ( 'required' , null , new Boolean (), 'Is attribute required?' )
2024-11-04 09:13:38 +00:00
-> param ( 'default' , null , fn ( Database $dbForProject ) => new Nullable ( new DatetimeValidator ( $dbForProject -> getAdapter () -> getMinDateTime (), $dbForProject -> getAdapter () -> getMaxDateTime ())), 'Default value for attribute when not provided. Cannot be set when attribute is required.' , injections : [ 'dbForProject' ])
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-02-26 12:33:18 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2024-08-16 06:18:52 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , ? bool $required , ? string $default , ? string $newKey , Response $response , Database $dbForProject , Event $queueForEvents ) {
2023-02-26 12:33:18 +00:00
$attribute = updateAttribute (
databaseId : $databaseId ,
collectionId : $collectionId ,
key : $key ,
dbForProject : $dbForProject ,
2023-10-03 18:56:36 +00:00
queueForEvents : $queueForEvents ,
2023-02-26 12:33:18 +00:00
type : Database :: VAR_DATETIME ,
default : $default ,
2024-08-16 06:18:52 +00:00
required : $required ,
newKey : $newKey
2023-02-26 12:33:18 +00:00
);
$response
2023-03-02 06:45:10 +00:00
-> setStatusCode ( Response :: STATUS_CODE_OK )
2023-02-26 12:33:18 +00:00
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_DATETIME );
});
2023-03-23 03:58:41 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update relationship attribute' )
2023-03-23 03:58:41 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-03-23 03:58:41 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
-> label ( 'audits.event' , 'attribute.update' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'updateRelationshipAttribute' ,
description : '/docs/references/databases/update-relationship-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_ATTRIBUTE_RELATIONSHIP ,
)
],
contentType : ContentType :: JSON
))
2023-03-23 03:58:41 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2023-03-28 13:24:18 +00:00
-> param ( 'onDelete' , null , new WhiteList ([ Database :: RELATION_MUTATE_CASCADE , Database :: RELATION_MUTATE_RESTRICT , Database :: RELATION_MUTATE_SET_NULL ], true ), 'Constraints option' , true )
2024-08-16 06:18:52 +00:00
-> param ( 'newKey' , null , new Key (), 'New attribute key.' , true )
2023-03-23 03:58:41 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-10-03 18:56:36 +00:00
-> inject ( 'queueForEvents' )
2023-03-23 03:58:41 +00:00
-> action ( function (
string $databaseId ,
string $collectionId ,
string $key ,
? string $onDelete ,
2024-08-16 06:18:52 +00:00
? string $newKey ,
2023-03-23 03:58:41 +00:00
Response $response ,
Database $dbForProject ,
2023-10-03 18:56:36 +00:00
Event $queueForEvents
2023-03-23 03:58:41 +00:00
) {
$attribute = updateAttribute (
$databaseId ,
$collectionId ,
$key ,
$dbForProject ,
2023-10-03 18:56:36 +00:00
$queueForEvents ,
2023-03-23 03:58:41 +00:00
type : Database :: VAR_RELATIONSHIP ,
required : false ,
2023-05-24 10:09:08 +00:00
options : [
2023-03-23 03:58:41 +00:00
'onDelete' => $onDelete
2024-08-16 06:18:52 +00:00
],
newKey : $newKey
2023-03-23 03:58:41 +00:00
);
$options = $attribute -> getAttribute ( 'options' , []);
foreach ( $options as $key => $option ) {
$attribute -> setAttribute ( $key , $option );
}
$response
-> setStatusCode ( Response :: STATUS_CODE_OK )
-> dynamic ( $attribute , Response :: MODEL_ATTRIBUTE_RELATIONSHIP );
});
2022-06-22 10:51:49 +00:00
App :: delete ( '/v1/databases/:databaseId/collections/:collectionId/attributes/:key' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/attributes/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete attribute' )
2022-07-08 05:43:20 +00:00
-> groups ([ 'api' , 'database' , 'schema' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-10-18 16:30:47 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'attribute.delete' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'attributes' ,
2025-01-17 04:31:39 +00:00
name : 'deleteAttribute' ,
description : '/docs/references/databases/delete-attribute.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Attribute Key.' )
2021-03-24 15:40:33 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2023-09-27 15:51:17 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
2025-05-15 11:33:00 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2025-05-15 11:33:00 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-03-24 15:40:33 +00:00
2021-06-11 14:25:52 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2025-05-26 05:42:11 +00:00
$attribute = $dbForProject -> getDocument ( 'attributes' , $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key );
2021-08-08 22:07:59 +00:00
2021-12-27 17:07:35 +00:00
if ( $attribute -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_NOT_FOUND );
2021-06-09 21:11:51 +00:00
}
2025-01-16 15:20:30 +00:00
/**
* Check index dependency
*/
$validator = new IndexDependencyValidator (
$collection -> getAttribute ( 'indexes' ),
$dbForProject -> getAdapter () -> getSupportForCastIndexArray (),
);
if ( ! $validator -> isValid ( $attribute )) {
2025-01-16 15:33:51 +00:00
throw new Exception ( Exception :: INDEX_DEPENDENCY );
2025-01-16 15:20:30 +00:00
}
2021-10-05 00:23:15 +00:00
// Only update status if removing available attribute
2023-02-26 12:33:18 +00:00
if ( $attribute -> getAttribute ( 'status' ) === 'available' ) {
2021-12-27 12:45:23 +00:00
$attribute = $dbForProject -> updateDocument ( 'attributes' , $attribute -> getId (), $attribute -> setAttribute ( 'status' , 'deleting' ));
2021-10-05 00:23:15 +00:00
}
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collectionId );
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence ());
2021-08-20 17:02:44 +00:00
2023-03-30 17:36:24 +00:00
if ( $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP ) {
$options = $attribute -> getAttribute ( 'options' );
if ( $options [ 'twoWay' ]) {
2025-05-26 05:42:11 +00:00
$relatedCollection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $options [ 'relatedCollection' ]);
2023-03-30 17:36:24 +00:00
if ( $relatedCollection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$relatedAttribute = $dbForProject -> getDocument ( 'attributes' , $database -> getSequence () . '_' . $relatedCollection -> getSequence () . '_' . $options [ 'twoWayKey' ]);
2023-03-30 17:36:24 +00:00
if ( $relatedAttribute -> isEmpty ()) {
throw new Exception ( Exception :: ATTRIBUTE_NOT_FOUND );
}
if ( $relatedAttribute -> getAttribute ( 'status' ) === 'available' ) {
2023-04-05 12:54:44 +00:00
$dbForProject -> updateDocument ( 'attributes' , $relatedAttribute -> getId (), $relatedAttribute -> setAttribute ( 'status' , 'deleting' ));
2023-03-30 17:36:24 +00:00
}
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $options [ 'relatedCollection' ]);
$dbForProject -> purgeCachedCollection ( 'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence ());
2023-03-30 17:36:24 +00:00
}
}
2022-12-20 16:11:30 +00:00
$queueForDatabase
2022-04-13 12:39:31 +00:00
-> setType ( DATABASE_TYPE_DELETE_ATTRIBUTE )
-> setCollection ( $collection )
2025-05-15 11:33:00 +00:00
-> setDatabase ( $database )
2023-10-16 23:18:51 +00:00
-> setDocument ( $attribute );
2021-06-08 20:12:14 +00:00
2021-10-26 01:12:27 +00:00
// Select response model based on type and format
$type = $attribute -> getAttribute ( 'type' );
$format = $attribute -> getAttribute ( 'format' );
2022-05-23 14:54:50 +00:00
$model = match ( $type ) {
2021-10-26 01:12:27 +00:00
Database :: VAR_BOOLEAN => Response :: MODEL_ATTRIBUTE_BOOLEAN ,
Database :: VAR_INTEGER => Response :: MODEL_ATTRIBUTE_INTEGER ,
Database :: VAR_FLOAT => Response :: MODEL_ATTRIBUTE_FLOAT ,
2023-03-29 00:37:51 +00:00
Database :: VAR_DATETIME => Response :: MODEL_ATTRIBUTE_DATETIME ,
Database :: VAR_RELATIONSHIP => Response :: MODEL_ATTRIBUTE_RELATIONSHIP ,
2022-05-23 14:54:50 +00:00
Database :: VAR_STRING => match ( $format ) {
2021-10-26 01:12:27 +00:00
APP_DATABASE_ATTRIBUTE_EMAIL => Response :: MODEL_ATTRIBUTE_EMAIL ,
APP_DATABASE_ATTRIBUTE_ENUM => Response :: MODEL_ATTRIBUTE_ENUM ,
APP_DATABASE_ATTRIBUTE_IP => Response :: MODEL_ATTRIBUTE_IP ,
APP_DATABASE_ATTRIBUTE_URL => Response :: MODEL_ATTRIBUTE_URL ,
default => Response :: MODEL_ATTRIBUTE_STRING ,
},
default => Response :: MODEL_ATTRIBUTE ,
};
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
-> setParam ( 'attributeId' , $attribute -> getId ())
2022-06-22 10:51:49 +00:00
-> setContext ( 'collection' , $collection )
2025-05-15 11:33:00 +00:00
-> setContext ( 'database' , $database )
2023-05-24 10:09:08 +00:00
-> setPayload ( $response -> output ( $attribute , $model ));
2021-03-24 15:40:33 +00:00
$response -> noContent ();
});
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/indexes' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/indexes' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create index' )
2021-03-23 21:19:19 +00:00
-> groups ([ 'api' , 'database' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create' )
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'index.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'collections' ,
2025-01-17 04:31:39 +00:00
name : 'createIndex' ,
description : '/docs/references/databases/create-index.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_ACCEPTED ,
model : Response :: MODEL_INDEX ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , null , new Key (), 'Index Key.' )
2024-02-26 02:38:58 +00:00
-> param ( 'type' , null , new WhiteList ([ Database :: INDEX_KEY , Database :: INDEX_FULLTEXT , Database :: INDEX_UNIQUE ]), 'Index type.' )
2022-06-20 14:22:27 +00:00
-> param ( 'attributes' , null , new ArrayList ( new Key ( true ), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.' )
2022-05-01 07:54:58 +00:00
-> param ( 'orders' , [], new ArrayList ( new WhiteList ([ 'ASC' , 'DESC' ], false , Database :: VAR_STRING ), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.' , true )
2025-06-10 19:44:40 +00:00
-> param ( 'lengths' , [], new ArrayList ( new Nullable ( new Integer ()), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE , optional : true )
2021-03-23 21:19:19 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
2025-05-13 10:09:13 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , string $type , array $attributes , array $orders , array $lengths , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2025-05-15 11:33:00 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2022-06-22 10:51:49 +00:00
2025-05-15 11:33:00 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2023-06-15 05:29:03 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-06-10 13:15:00 +00:00
2021-06-11 14:25:52 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2025-05-15 11:33:00 +00:00
$limit = $dbForProject -> getLimitForIndexes ();
2021-12-27 12:45:23 +00:00
$count = $dbForProject -> count ( 'indexes' , [
2025-05-26 05:42:11 +00:00
Query :: equal ( 'collectionInternalId' , [ $collection -> getSequence ()]),
Query :: equal ( 'databaseInternalId' , [ $database -> getSequence ()])
2025-05-15 11:33:00 +00:00
], max : $limit );
2021-08-24 23:35:32 +00:00
if ( $count >= $limit ) {
2022-08-14 08:05:11 +00:00
throw new Exception ( Exception :: INDEX_LIMIT_EXCEEDED , 'Index limit exceeded' );
2021-08-24 23:35:32 +00:00
}
2025-05-15 11:33:00 +00:00
// Convert Document array to array of attribute metadata
2024-03-06 17:34:21 +00:00
$oldAttributes = \array_map ( fn ( $a ) => $a -> getArrayCopy (), $collection -> getAttribute ( 'attributes' ));
2021-07-02 22:23:58 +00:00
2022-06-20 14:22:27 +00:00
$oldAttributes [] = [
'key' => '$id' ,
2022-09-08 22:18:36 +00:00
'type' => Database :: VAR_STRING ,
2022-06-20 14:22:27 +00:00
'status' => 'available' ,
'required' => true ,
'array' => false ,
'default' => null ,
2024-07-10 07:00:10 +00:00
'size' => Database :: LENGTH_KEY
2022-06-20 14:22:27 +00:00
];
2022-06-22 09:46:37 +00:00
$oldAttributes [] = [
'key' => '$createdAt' ,
2022-09-08 22:18:36 +00:00
'type' => Database :: VAR_DATETIME ,
2022-06-22 09:46:37 +00:00
'status' => 'available' ,
'signed' => false ,
'required' => false ,
'array' => false ,
'default' => null ,
'size' => 0
];
$oldAttributes [] = [
'key' => '$updatedAt' ,
2022-09-08 22:18:36 +00:00
'type' => Database :: VAR_DATETIME ,
2022-06-22 09:46:37 +00:00
'status' => 'available' ,
'signed' => false ,
'required' => false ,
'array' => false ,
'default' => null ,
'size' => 0
];
2021-07-02 22:23:58 +00:00
2021-12-16 15:04:30 +00:00
foreach ( $attributes as $i => $attribute ) {
2025-05-15 11:33:00 +00:00
// Find attribute metadata in collection document
2021-08-22 14:06:59 +00:00
$attributeIndex = \array_search ( $attribute , array_column ( $oldAttributes , 'key' ));
2021-07-02 22:23:58 +00:00
if ( $attributeIndex === false ) {
2024-12-12 21:27:04 +00:00
throw new Exception ( Exception :: ATTRIBUTE_UNKNOWN , 'Unknown attribute: ' . $attribute . '. Verify the attribute name or create the attribute.' );
2021-07-02 22:23:58 +00:00
}
2021-10-05 00:43:27 +00:00
$attributeStatus = $oldAttributes [ $attributeIndex ][ 'status' ];
2021-07-03 00:20:09 +00:00
$attributeType = $oldAttributes [ $attributeIndex ][ 'type' ];
2024-02-08 13:01:20 +00:00
$attributeArray = $oldAttributes [ $attributeIndex ][ 'array' ] ? ? false ;
2021-07-02 22:23:58 +00:00
2023-03-31 05:06:50 +00:00
if ( $attributeType === Database :: VAR_RELATIONSHIP ) {
throw new Exception ( Exception :: ATTRIBUTE_TYPE_INVALID , 'Cannot create an index for a relationship attribute: ' . $oldAttributes [ $attributeIndex ][ 'key' ]);
}
2021-10-05 00:43:27 +00:00
// ensure attribute is available
if ( $attributeStatus !== 'available' ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: ATTRIBUTE_NOT_AVAILABLE , 'Attribute not available: ' . $oldAttributes [ $attributeIndex ][ 'key' ]);
2021-10-05 00:43:27 +00:00
}
2025-05-13 10:09:13 +00:00
$lengths [ $i ] ? ? = null ;
2024-02-08 13:01:20 +00:00
if ( $attributeArray === true ) {
$lengths [ $i ] = Database :: ARRAY_INDEX_LENGTH ;
$orders [ $i ] = null ;
}
2021-07-02 22:23:58 +00:00
}
2023-06-15 05:29:03 +00:00
$index = new Document ([
2025-05-26 05:42:11 +00:00
'$id' => ID :: custom ( $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key ),
2023-06-15 05:29:03 +00:00
'key' => $key ,
'status' => 'processing' , // processing, available, failed, deleting, stuck
2025-05-26 05:42:11 +00:00
'databaseInternalId' => $database -> getSequence (),
2023-06-15 05:29:03 +00:00
'databaseId' => $databaseId ,
2025-05-26 05:42:11 +00:00
'collectionInternalId' => $collection -> getSequence (),
2023-06-15 05:29:03 +00:00
'collectionId' => $collectionId ,
'type' => $type ,
'attributes' => $attributes ,
'lengths' => $lengths ,
'orders' => $orders ,
]);
2023-10-19 01:26:44 +00:00
$validator = new IndexValidator (
$collection -> getAttribute ( 'attributes' ),
2024-11-12 09:27:24 +00:00
$dbForProject -> getAdapter () -> getMaxIndexLength (),
$dbForProject -> getAdapter () -> getInternalIndexesKeys (),
2023-10-19 01:26:44 +00:00
);
2025-05-15 11:33:00 +00:00
2023-10-19 01:26:44 +00:00
if ( ! $validator -> isValid ( $index )) {
2023-06-15 05:29:03 +00:00
throw new Exception ( Exception :: INDEX_INVALID , $validator -> getDescription ());
}
2021-08-22 15:00:00 +00:00
try {
2023-06-15 05:29:03 +00:00
$index = $dbForProject -> createDocument ( 'indexes' , $index );
} catch ( DuplicateException ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: INDEX_ALREADY_EXISTS );
2021-08-22 15:00:00 +00:00
}
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-03-23 21:19:19 +00:00
2022-12-20 16:11:30 +00:00
$queueForDatabase
2022-04-13 12:39:31 +00:00
-> setType ( DATABASE_TYPE_CREATE_INDEX )
2025-05-15 11:33:00 +00:00
-> setDatabase ( $database )
2022-04-13 12:39:31 +00:00
-> setCollection ( $collection )
2023-05-24 10:09:08 +00:00
-> setDocument ( $index );
2021-06-18 16:13:37 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
-> setParam ( 'indexId' , $index -> getId ())
2022-09-07 11:02:36 +00:00
-> setContext ( 'collection' , $collection )
2025-05-15 11:33:00 +00:00
-> setContext ( 'database' , $database );
2022-04-13 12:39:31 +00:00
2022-09-07 11:11:10 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $index , Response :: MODEL_INDEX );
2021-03-23 21:19:19 +00:00
});
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/indexes' )
2024-11-04 07:57:08 +00:00
-> alias ( '/v1/database/collections/:collectionId/indexes' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List indexes' )
2021-03-24 15:40:33 +00:00
-> groups ([ 'api' , 'database' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'indexes' ,
2025-01-17 04:31:39 +00:00
name : 'listIndexes' ,
description : '/docs/references/databases/list-indexes.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_INDEX_LIST ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2023-07-21 11:10:44 +00:00
-> param ( 'queries' , [], new Indexes (), '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 ( ', ' , Indexes :: ALLOWED_ATTRIBUTES ), true )
2021-03-24 15:40:33 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2023-07-21 11:10:44 +00:00
-> action ( function ( string $databaseId , string $collectionId , array $queries , Response $response , Database $dbForProject ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2021-06-09 21:11:51 +00:00
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2023-07-24 18:33:58 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-03-24 15:40:33 +00:00
2021-06-11 14:25:52 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2024-11-04 07:57:08 +00:00
$queries = Query :: parseQueries ( $queries );
2024-02-12 16:02:04 +00:00
2024-11-04 07:57:08 +00:00
\array_push (
$queries ,
Query :: equal ( 'databaseId' , [ $databaseId ]),
Query :: equal ( 'collectionId' , [ $collectionId ]),
);
2021-03-24 15:40:33 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2024-03-06 17:34:21 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2024-03-06 17:34:21 +00:00
});
2025-05-15 11:33:00 +00:00
2024-03-06 17:34:21 +00:00
$cursor = reset ( $cursor );
2023-07-21 11:10:44 +00:00
if ( $cursor ) {
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2023-07-21 11:10:44 +00:00
$indexId = $cursor -> getValue ();
2024-03-06 17:34:21 +00:00
$cursorDocument = Authorization :: skip ( fn () => $dbForProject -> find ( 'indexes' , [
2025-05-26 05:42:11 +00:00
Query :: equal ( 'collectionInternalId' , [ $collection -> getSequence ()]),
Query :: equal ( 'databaseInternalId' , [ $database -> getSequence ()]),
2023-07-26 06:07:23 +00:00
Query :: equal ( 'key' , [ $indexId ]),
Query :: limit ( 1 )
]));
2021-06-10 13:15:00 +00:00
2023-07-21 11:10:44 +00:00
if ( empty ( $cursorDocument ) || $cursorDocument [ 0 ] -> isEmpty ()) {
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Index ' { $indexId } ' for the 'cursor' value not found. " );
}
2021-03-24 15:40:33 +00:00
2023-07-21 11:10:44 +00:00
$cursor -> setValue ( $cursorDocument [ 0 ]);
}
2021-03-24 15:40:33 +00:00
2025-04-16 11:33:14 +00:00
try {
2025-05-15 11:33:00 +00:00
$total = $dbForProject -> count ( 'indexes' , $queries , APP_LIMIT_COUNT );
2025-04-17 04:46:26 +00:00
$indexes = $dbForProject -> find ( 'indexes' , $queries );
2025-04-16 11:33:14 +00:00
} catch ( OrderException $e ) {
2025-04-17 04:46:26 +00:00
throw new Exception ( Exception :: DATABASE_QUERY_ORDER_NULL , " The order attribute ' { $e -> getAttribute () } ' had a null value. Cursor pagination requires all documents order attribute values are non-null. " );
2025-05-15 11:33:00 +00:00
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
2025-04-16 11:33:14 +00:00
}
2025-04-17 04:46:26 +00:00
2021-07-25 14:47:18 +00:00
$response -> dynamic ( new Document ([
2025-04-17 04:46:26 +00:00
'total' => $total ,
'indexes' => $indexes ,
2021-06-09 21:11:51 +00:00
]), Response :: MODEL_INDEX_LIST );
2021-03-24 15:40:33 +00:00
});
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/indexes/:key' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/indexes/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get index' )
2021-03-25 19:52:57 +00:00
-> groups ([ 'api' , 'database' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'indexes' ,
2025-01-17 04:31:39 +00:00
name : 'getIndex' ,
description : '/docs/references/databases/get-index.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_INDEX ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , null , new Key (), 'Index Key.' )
2021-03-25 19:52:57 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-08-10 02:18:18 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $key , Response $response , Database $dbForProject ) {
2024-03-06 17:34:21 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2021-03-25 19:52:57 +00:00
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2025-05-15 11:33:00 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-06-09 21:11:51 +00:00
2021-06-11 14:25:52 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2023-07-24 18:11:49 +00:00
$index = $collection -> find ( 'key' , $key , 'indexes' );
2025-05-15 11:33:00 +00:00
2023-07-20 09:22:05 +00:00
if ( empty ( $index )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: INDEX_NOT_FOUND );
2021-06-09 21:11:51 +00:00
}
2021-07-25 14:47:18 +00:00
$response -> dynamic ( $index , Response :: MODEL_INDEX );
2021-03-25 19:52:57 +00:00
});
2022-06-22 10:51:49 +00:00
App :: delete ( '/v1/databases/:databaseId/collections/:collectionId/indexes/:key' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/indexes/:key' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete index' )
2021-03-25 19:52:57 +00:00
-> groups ([ 'api' , 'database' ])
2021-08-14 10:16:00 +00:00
-> label ( 'scope' , 'collections.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2023-10-18 16:30:47 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'index.delete' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'indexes' ,
2025-01-17 04:31:39 +00:00
name : 'deleteIndex' ,
description : '/docs/references/databases/delete-index.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-09-19 10:05:42 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2021-12-16 15:04:30 +00:00
-> param ( 'key' , '' , new Key (), 'Index Key.' )
2021-03-25 19:52:57 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDatabase' )
-> inject ( 'queueForEvents' )
-> action ( function ( string $databaseId , string $collectionId , string $key , Response $response , Database $dbForProject , EventDatabase $queueForDatabase , Event $queueForEvents ) {
2025-05-15 11:33:00 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2021-03-25 19:52:57 +00:00
2025-05-15 11:33:00 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2025-05-15 11:33:00 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-06-09 21:11:51 +00:00
2021-06-11 18:07:05 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 13:15:00 +00:00
}
2025-05-26 05:42:11 +00:00
$index = $dbForProject -> getDocument ( 'indexes' , $database -> getSequence () . '_' . $collection -> getSequence () . '_' . $key );
2021-06-09 21:11:51 +00:00
2025-05-15 11:33:00 +00:00
if ( $index -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: INDEX_NOT_FOUND );
2021-03-25 19:52:57 +00:00
}
2021-10-05 00:23:15 +00:00
// Only update status if removing available index
if ( $index -> getAttribute ( 'status' ) === 'available' ) {
2021-12-27 12:45:23 +00:00
$index = $dbForProject -> updateDocument ( 'indexes' , $index -> getId (), $index -> setAttribute ( 'status' , 'deleting' ));
2021-10-05 00:23:15 +00:00
}
2025-05-26 05:42:11 +00:00
$dbForProject -> purgeCachedDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-08-23 04:06:53 +00:00
2022-12-20 16:11:30 +00:00
$queueForDatabase
2022-04-13 12:39:31 +00:00
-> setType ( DATABASE_TYPE_DELETE_INDEX )
2025-05-15 11:33:00 +00:00
-> setDatabase ( $database )
2022-04-13 12:39:31 +00:00
-> setCollection ( $collection )
2023-05-24 10:09:08 +00:00
-> setDocument ( $index );
2021-03-25 19:52:57 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
-> setParam ( 'indexId' , $index -> getId ())
2022-08-05 06:01:25 +00:00
-> setContext ( 'collection' , $collection )
2025-05-15 11:33:00 +00:00
-> setContext ( 'database' , $database )
2023-05-24 10:09:08 +00:00
-> setPayload ( $response -> output ( $index , Response :: MODEL_INDEX ));
2021-03-25 19:52:57 +00:00
$response -> noContent ();
});
2022-06-22 10:51:49 +00:00
App :: post ( '/v1/databases/:databaseId/collections/:collectionId/documents' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/documents' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create document' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2020-01-31 22:34:07 +00:00
-> label ( 'scope' , 'documents.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'document.create' )
2022-08-08 12:19:41 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2022-08-31 03:58:32 +00:00
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
2022-08-30 23:34:17 +00:00
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
2025-01-17 04:31:39 +00:00
-> label (
'sdk' ,
2025-05-07 09:23:16 +00:00
// Using multiple methods to abstract the complexity for SDK users
2025-01-17 04:31:39 +00:00
[
new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'documents' ,
2025-01-17 04:31:39 +00:00
name : 'createDocument' ,
description : '/docs/references/databases/create-document.md' ,
2025-06-10 19:50:18 +00:00
auth : [ AuthType :: ADMIN , AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
2025-01-17 04:31:39 +00:00
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_DOCUMENT ,
)
],
2025-01-15 03:54:49 +00:00
contentType : ContentType :: JSON ,
2025-01-09 17:07:56 +00:00
parameters : [
2025-03-27 08:03:56 +00:00
new Parameter ( 'databaseId' , optional : false ),
new Parameter ( 'collectionId' , optional : false ),
new Parameter ( 'documentId' , optional : false ),
new Parameter ( 'data' , optional : false ),
new Parameter ( 'permissions' , optional : true ),
2025-01-09 17:07:56 +00:00
]
),
2025-05-16 15:44:26 +00:00
new Method (
namespace : 'databases' ,
group : 'documents' ,
name : 'createDocuments' ,
description : '/docs/references/databases/create-documents.md' ,
2025-06-10 19:50:18 +00:00
auth : [ AuthType :: ADMIN , AuthType :: KEY ],
2025-05-16 15:44:26 +00:00
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_DOCUMENT_LIST ,
)
],
contentType : ContentType :: JSON ,
parameters : [
new Parameter ( 'databaseId' , optional : false ),
new Parameter ( 'collectionId' , optional : false ),
new Parameter ( 'documents' , optional : false ),
]
2025-01-17 04:31:39 +00:00
)
]
)
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2024-11-12 01:20:46 +00:00
-> param ( 'documentId' , '' , new CustomId (), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.' , true )
2022-09-19 10:05:42 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.' )
2024-11-12 01:20:46 +00:00
-> param ( 'data' , [], new JSON (), 'Document data as JSON object.' , true )
2023-10-13 13:43:23 +00:00
-> param ( 'permissions' , null , new Permissions ( APP_LIMIT_ARRAY_PARAMS_SIZE , [ Database :: PERMISSION_READ , Database :: PERMISSION_UPDATE , Database :: PERMISSION_DELETE , Database :: PERMISSION_WRITE ]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
2025-05-07 09:23:16 +00:00
-> param ( 'documents' , [], fn ( array $plan ) => new ArrayList ( new JSON (), $plan [ 'databasesBatchSize' ] ? ? APP_LIMIT_DATABASE_BATCH ), 'Array of documents data as JSON objects.' , true , [ 'plan' ])
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2021-03-21 22:17:20 +00:00
-> inject ( 'user' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-01-30 04:53:53 +00:00
-> inject ( 'queueForStatsUsage' )
2025-05-07 14:49:02 +00:00
-> action ( function ( string $databaseId , ? string $documentId , string $collectionId , string | array | null $data , ? array $permissions , ? array $documents , Response $response , Database $dbForProject , Document $user , Event $queueForEvents , StatsUsage $queueForStatsUsage ) {
2025-03-05 02:09:06 +00:00
$data = \is_string ( $data )
? \json_decode ( $data , true )
: $data ;
2021-10-05 10:30:33 +00:00
2025-05-07 09:23:16 +00:00
/**
* Determine which internal path to call , single or bulk
*/
2024-11-12 01:20:46 +00:00
if ( empty ( $data ) && empty ( $documents )) {
2025-03-27 10:00:28 +00:00
// No single or bulk documents provided
2023-06-07 01:51:24 +00:00
throw new Exception ( Exception :: DOCUMENT_MISSING_DATA );
2020-06-29 21:43:34 +00:00
}
2024-11-12 01:20:46 +00:00
if ( ! empty ( $data ) && ! empty ( $documents )) {
2025-03-27 10:00:28 +00:00
// Both single and bulk documents provided
2025-03-05 02:09:06 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'You can only send one of the following parameters: data, documents' );
2024-11-12 01:20:46 +00:00
}
if ( ! empty ( $data ) && empty ( $documentId )) {
2025-03-27 10:00:28 +00:00
// Single document provided without document ID
2024-11-13 01:00:40 +00:00
throw new Exception ( Exception :: DOCUMENT_MISSING_DATA , 'Document ID is required when creating a single document' );
2024-11-12 01:20:46 +00:00
}
if ( ! empty ( $documents ) && ! empty ( $documentId )) {
2025-03-27 10:00:28 +00:00
// Bulk documents provided with document ID
2025-03-05 02:09:06 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Param "documentId" is disallowed when creating multiple documents, set "$id" in each document instead' );
}
if ( ! empty ( $documents ) && ! empty ( $permissions )) {
2025-03-27 10:00:28 +00:00
// Bulk documents provided with permissions
2025-03-05 02:09:06 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Param "permissions" is disallowed when creating multiple documents, set "$permissions" in each document instead' );
2020-06-29 21:43:34 +00:00
}
2021-10-05 10:30:33 +00:00
2025-05-07 09:23:16 +00:00
$isBulk = true ;
2024-11-12 01:20:46 +00:00
if ( ! empty ( $data )) {
2025-03-27 10:00:28 +00:00
// Single document provided, convert to single item array
2025-04-30 00:44:15 +00:00
// But remember that it was single to respond with a single document
2024-11-12 01:20:46 +00:00
$isBulk = false ;
$documents = [ $data ];
2020-06-29 21:43:34 +00:00
}
2022-08-08 10:58:28 +00:00
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2025-05-15 11:33:00 +00:00
if ( $isBulk && ! $isAPIKey && ! $isPrivilegedUser ) {
throw new Exception ( Exception :: GENERAL_UNAUTHORIZED_SCOPE );
}
2025-05-08 09:08:23 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2023-08-16 21:58:25 +00:00
if ( $database -> isEmpty () || ( ! $database -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
2022-08-16 08:30:00 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-08-08 10:58:28 +00:00
}
2025-05-26 05:42:11 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2023-08-16 21:58:25 +00:00
if ( $collection -> isEmpty () || ( ! $collection -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2022-08-08 10:58:28 +00:00
}
2025-03-27 05:13:10 +00:00
$hasRelationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
);
2022-08-16 05:54:44 +00:00
2025-03-27 05:13:10 +00:00
if ( $isBulk && $hasRelationships ) {
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Bulk create is not supported for collections with relationship attributes' );
}
2022-08-23 01:42:25 +00:00
2025-03-27 10:00:28 +00:00
$setPermissions = function ( Document $document , ? array $permissions ) use ( $user , $isAPIKey , $isPrivilegedUser , $isBulk ) {
$allowedPermissions = [
Database :: PERMISSION_READ ,
Database :: PERMISSION_UPDATE ,
Database :: PERMISSION_DELETE ,
];
2022-08-16 05:54:44 +00:00
2025-05-08 06:09:37 +00:00
// If bulk, we need to validate permissions explicitly per document
2024-12-03 07:40:03 +00:00
if ( $isBulk ) {
$permissions = $document [ '$permissions' ] ? ? null ;
2025-05-08 06:09:37 +00:00
if ( ! empty ( $permissions )) {
$validator = new Permissions ();
if ( ! $validator -> isValid ( $permissions )) {
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , $validator -> getDescription ());
}
2022-08-15 12:56:19 +00:00
}
}
2022-08-14 05:24:50 +00:00
2024-12-03 07:40:03 +00:00
$permissions = Permission :: aggregate ( $permissions , $allowedPermissions );
// Add permissions for current the user if none were provided.
if ( \is_null ( $permissions )) {
$permissions = [];
if ( ! empty ( $user -> getId ())) {
foreach ( $allowedPermissions as $permission ) {
$permissions [] = ( new Permission ( $permission , 'user' , $user -> getId ())) -> toString ();
2022-08-15 12:56:19 +00:00
}
}
}
2020-01-31 22:34:07 +00:00
2024-12-03 07:40:03 +00:00
// Users can only manage their own roles, API keys and Admin users can manage any
if ( ! $isAPIKey && ! $isPrivilegedUser ) {
foreach ( Database :: PERMISSIONS as $type ) {
foreach ( $permissions as $permission ) {
$permission = Permission :: parse ( $permission );
if ( $permission -> getPermission () != $type ) {
continue ;
}
$role = ( new Role (
$permission -> getRole (),
$permission -> getIdentifier (),
$permission -> getDimension ()
)) -> toString ();
if ( ! Authorization :: isRole ( $role )) {
throw new Exception ( Exception :: USER_UNAUTHORIZED , 'Permissions must be one of: (' . \implode ( ', ' , Authorization :: getRoles ()) . ')' );
}
2022-08-15 12:56:19 +00:00
}
}
}
2024-12-03 07:40:03 +00:00
$document -> setAttribute ( '$permissions' , $permissions );
};
2022-08-08 10:58:28 +00:00
2025-05-06 10:05:59 +00:00
$operations = 0 ;
2025-01-06 16:51:39 +00:00
2025-01-06 09:28:26 +00:00
$checkPermissions = function ( Document $collection , Document $document , string $permission ) use ( & $checkPermissions , $dbForProject , $database , & $operations ) {
2025-01-06 16:51:39 +00:00
$operations ++ ;
2023-03-28 09:02:49 +00:00
$documentSecurity = $collection -> getAttribute ( 'documentSecurity' , false );
$validator = new Authorization ( $permission );
$valid = $validator -> isValid ( $collection -> getPermissionsByType ( $permission ));
if (( $permission === Database :: PERMISSION_UPDATE && ! $documentSecurity ) || ! $valid ) {
throw new Exception ( Exception :: USER_UNAUTHORIZED );
}
if ( $permission === Database :: PERMISSION_UPDATE ) {
$valid = $valid || $validator -> isValid ( $document -> getUpdate ());
if ( $documentSecurity && ! $valid ) {
throw new Exception ( Exception :: USER_UNAUTHORIZED );
}
}
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-28 09:02:49 +00:00
);
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
continue ;
}
2023-04-13 03:59:57 +00:00
2023-04-14 10:03:16 +00:00
$isList = \is_array ( $related ) && \array_values ( $related ) === $related ;
if ( $isList ) {
2023-04-13 03:59:57 +00:00
$relations = $related ;
} else {
$relations = [ $related ];
2023-03-29 01:33:53 +00:00
}
2023-03-28 09:02:49 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 07:56:14 +00:00
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2023-03-28 09:02:49 +00:00
);
2023-04-13 03:59:57 +00:00
foreach ( $relations as & $relation ) {
if (
2023-04-21 01:44:53 +00:00
\is_array ( $relation )
&& \array_values ( $relation ) !== $relation
2023-04-13 03:59:57 +00:00
&& ! isset ( $relation [ '$id' ])
) {
$relation [ '$id' ] = ID :: unique ();
$relation = new Document ( $relation );
}
2023-03-29 01:37:56 +00:00
if ( $relation instanceof Document ) {
2025-01-02 07:56:14 +00:00
$current = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence (), $relation -> getId ())
2023-03-28 09:02:49 +00:00
);
2023-03-31 04:33:21 +00:00
if ( $current -> isEmpty ()) {
$type = Database :: PERMISSION_CREATE ;
2023-04-13 03:59:57 +00:00
if ( isset ( $relation [ '$id' ]) && $relation [ '$id' ] === 'unique()' ) {
2023-03-31 04:33:21 +00:00
$relation [ '$id' ] = ID :: unique ();
}
} else {
2023-04-03 10:45:10 +00:00
$relation -> removeAttribute ( '$collectionId' );
$relation -> removeAttribute ( '$databaseId' );
2025-07-29 13:02:21 +00:00
$relation -> removeAttribute ( '$sequence' );
2023-04-03 10:45:10 +00:00
$relation -> setAttribute ( '$collection' , $relatedCollection -> getId ());
2023-03-31 04:33:21 +00:00
$type = Database :: PERMISSION_UPDATE ;
}
2023-03-28 09:02:49 +00:00
2023-03-29 01:37:56 +00:00
$checkPermissions ( $relatedCollection , $relation , $type );
2023-03-28 09:02:49 +00:00
}
}
2023-04-13 03:59:57 +00:00
2023-04-14 10:03:16 +00:00
if ( $isList ) {
2023-04-13 03:59:57 +00:00
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \array_values ( $relations ));
} else {
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \reset ( $relations ));
}
2023-03-28 09:02:49 +00:00
}
};
2025-03-27 10:00:28 +00:00
$documents = \array_map ( function ( $document ) use ( $collection , $permissions , $checkPermissions , $isBulk , $documentId , $setPermissions ) {
2024-11-12 01:20:46 +00:00
$document [ '$collection' ] = $collection -> getId ();
2025-03-27 10:00:28 +00:00
// Determine the source ID depending on whether it's a bulk operation.
2025-05-08 06:09:37 +00:00
$sourceId = $isBulk
? ( $document [ '$id' ] ? ? ID :: unique ())
: $documentId ;
2024-11-12 01:20:46 +00:00
2025-05-08 06:09:37 +00:00
// If bulk, we need to validate ID explicitly
if ( $isBulk ) {
$validator = new CustomId ();
if ( ! $validator -> isValid ( $sourceId )) {
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , $validator -> getDescription ());
}
2024-11-12 01:20:46 +00:00
}
2025-07-29 13:02:21 +00:00
// Remove sequence if set
unset ( $document [ '$sequence' ]);
2025-05-08 06:09:37 +00:00
// Assign a unique ID if needed, otherwise use the provided ID.
2025-03-27 10:00:28 +00:00
$document [ '$id' ] = $sourceId === 'unique()' ? ID :: unique () : $sourceId ;
2024-11-12 01:20:46 +00:00
$document = new Document ( $document );
2024-12-03 07:40:03 +00:00
$setPermissions ( $document , $permissions );
2024-11-12 01:20:46 +00:00
$checkPermissions ( $collection , $document , Database :: PERMISSION_CREATE );
return $document ;
}, $documents );
2023-03-28 09:02:49 +00:00
2024-03-06 17:34:21 +00:00
try {
2025-03-05 02:09:06 +00:00
$dbForProject -> createDocuments (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-04-30 00:07:56 +00:00
$documents
2025-03-05 02:09:06 +00:00
);
} catch ( DuplicateException ) {
2024-03-06 17:34:21 +00:00
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
2025-03-05 02:09:06 +00:00
} catch ( NotFoundException ) {
2024-11-04 07:57:08 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2025-05-15 11:33:00 +00:00
} catch ( RelationshipException $e ) {
throw new Exception ( Exception :: RELATIONSHIP_VALUE_INVALID , $e -> getMessage ());
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
2024-03-06 17:34:21 +00:00
}
2020-01-31 22:34:07 +00:00
2024-11-14 08:15:28 +00:00
$queueForEvents
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collection -> getId ())
-> setContext ( 'collection' , $collection )
-> setContext ( 'database' , $database );
2025-01-02 08:09:44 +00:00
2023-03-27 06:03:00 +00:00
// Add $collectionId and $databaseId for all documents
2025-01-06 15:19:39 +00:00
$processDocument = function ( Document $collection , Document $document ) use ( & $processDocument , $dbForProject , $database ) {
2024-11-22 02:51:17 +00:00
$document -> removeAttribute ( '$collection' );
2023-03-27 06:03:00 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-27 06:03:00 +00:00
);
2023-03-28 03:10:12 +00:00
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
2023-03-27 06:03:00 +00:00
continue ;
}
2023-03-28 03:10:12 +00:00
if ( ! \is_array ( $related )) {
$related = [ $related ];
}
2023-03-27 06:03:00 +00:00
2023-03-28 03:10:12 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 07:56:14 +00:00
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2023-03-28 03:10:12 +00:00
);
2023-03-27 06:03:00 +00:00
2023-03-29 01:37:56 +00:00
foreach ( $related as $relation ) {
2023-04-03 10:39:31 +00:00
if ( $relation instanceof Document ) {
$processDocument ( $relatedCollection , $relation );
}
2023-03-27 02:42:36 +00:00
}
}
};
2023-03-27 06:03:00 +00:00
2024-11-22 02:51:17 +00:00
foreach ( $documents as $document ) {
$processDocument ( $collection , $document );
}
2020-01-31 22:34:07 +00:00
2025-02-12 11:19:51 +00:00
$queueForStatsUsage
2025-05-06 10:05:59 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $operations ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $operations )); // per collection
2025-01-02 14:45:21 +00:00
2025-05-06 01:45:18 +00:00
$response -> setStatusCode ( Response :: STATUS_CODE_CREATED );
2025-01-06 15:19:39 +00:00
2024-11-22 02:51:17 +00:00
if ( $isBulk ) {
2025-03-05 02:09:06 +00:00
$response -> dynamic ( new Document ([
'total' => count ( $documents ),
'documents' => $documents
]), Response :: MODEL_DOCUMENT_LIST );
2024-07-11 15:03:28 +00:00
2025-03-27 10:00:28 +00:00
return ;
2024-11-12 01:20:46 +00:00
}
2024-07-11 15:03:28 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2025-03-27 10:00:28 +00:00
-> setParam ( 'documentId' , $documents [ 0 ] -> getId ())
-> setEvent ( 'databases.[databaseId].collections.[collectionId].documents.[documentId].create' );
$response -> dynamic (
$documents [ 0 ],
Response :: MODEL_DOCUMENT
);
2020-12-26 15:05:04 +00:00
});
2020-01-31 22:34:07 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/documents' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/documents' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List documents' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'documents.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'documents' ,
2025-01-17 04:31:39 +00:00
name : 'listDocuments' ,
description : '/docs/references/databases/list-documents.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT_LIST ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-08-25 08:14:27 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new ArrayList ( new Text ( APP_LIMIT_ARRAY_ELEMENT_SIZE ), APP_LIMIT_ARRAY_PARAMS_SIZE ), '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.' , true )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2025-01-30 04:53:53 +00:00
-> inject ( 'queueForStatsUsage' )
2025-02-17 12:49:51 +00:00
-> action ( function ( string $databaseId , string $collectionId , array $queries , Response $response , Database $dbForProject , StatsUsage $queueForStatsUsage ) {
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2019-05-09 06:54:39 +00:00
2025-05-15 11:33:00 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2023-08-16 21:58:25 +00:00
if ( $database -> isEmpty () || ( ! $database -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-26 05:42:11 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2023-08-16 21:58:25 +00:00
if ( $collection -> isEmpty () || ( ! $collection -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
2023-04-19 00:34:39 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2022-08-20 00:47:10 +00:00
}
2021-12-28 17:57:24 +00:00
2024-02-08 16:10:25 +00:00
try {
2024-02-12 16:02:04 +00:00
$queries = Query :: parseQueries ( $queries );
2024-02-08 16:10:25 +00:00
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2020-06-29 21:43:34 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
$cursor = \reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2024-05-09 16:54:28 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 15:18:59 +00:00
$documentId = $cursor -> getValue ();
2021-07-05 20:27:20 +00:00
2025-05-26 05:42:11 +00:00
$cursorDocument = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $documentId ));
2022-08-24 13:24:19 +00:00
2021-10-05 10:30:33 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 15:18:59 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Document ' { $documentId } ' for the 'cursor' value not found. " );
2021-08-05 19:01:00 +00:00
}
2022-08-11 23:53:52 +00:00
2022-08-23 15:18:59 +00:00
$cursor -> setValue ( $cursorDocument );
2022-08-11 23:53:52 +00:00
}
2025-04-16 11:33:14 +00:00
try {
2025-06-16 15:48:27 +00:00
$documents = $dbForProject -> find ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $queries );
2025-05-26 05:42:11 +00:00
$total = $dbForProject -> count ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $queries , APP_LIMIT_COUNT );
2025-04-16 11:33:14 +00:00
} catch ( OrderException $e ) {
2025-04-17 04:46:26 +00:00
throw new Exception ( Exception :: DATABASE_QUERY_ORDER_NULL , " The order attribute ' { $e -> getAttribute () } ' had a null value. Cursor pagination requires all documents order attribute values are non-null. " );
2025-05-15 11:33:00 +00:00
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
2025-04-16 11:33:14 +00:00
}
2021-08-12 01:26:31 +00:00
2025-01-06 16:01:44 +00:00
$operations = 0 ;
2025-01-02 08:09:44 +00:00
2023-03-27 06:03:00 +00:00
// Add $collectionId and $databaseId for all documents
2025-01-02 08:09:44 +00:00
$processDocument = ( function ( Document $collection , Document $document ) use ( & $processDocument , $dbForProject , $database , & $operations ) : bool {
2023-04-19 00:37:41 +00:00
if ( $document -> isEmpty ()) {
2023-03-28 09:02:49 +00:00
return false ;
}
2025-01-06 16:01:44 +00:00
$operations ++ ;
2023-03-27 06:03:00 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-27 06:03:00 +00:00
);
2023-03-28 03:10:12 +00:00
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
2025-01-06 16:51:39 +00:00
if ( \in_array ( \gettype ( $related ), [ 'array' , 'object' ])) {
$operations ++ ;
}
2023-03-27 06:03:00 +00:00
continue ;
}
2025-01-06 16:01:44 +00:00
2023-03-28 03:10:12 +00:00
if ( ! \is_array ( $related )) {
2023-03-28 09:02:49 +00:00
$relations = [ $related ];
} else {
$relations = $related ;
2023-03-28 03:10:12 +00:00
}
2023-03-27 06:03:00 +00:00
2023-03-28 03:10:12 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 08:09:44 +00:00
// todo: Use local cache for this getDocument
2025-05-26 05:42:11 +00:00
$relatedCollection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId ));
2023-03-27 06:03:00 +00:00
2023-03-28 09:02:49 +00:00
foreach ( $relations as $index => $doc ) {
2023-03-31 04:56:25 +00:00
if ( $doc instanceof Document ) {
if ( ! $processDocument ( $relatedCollection , $doc )) {
unset ( $relations [ $index ]);
}
2023-03-28 09:02:49 +00:00
}
}
if ( \is_array ( $related )) {
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \array_values ( $relations ));
} elseif ( empty ( $relations )) {
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), null );
2023-03-27 02:42:36 +00:00
}
}
2023-03-28 09:02:49 +00:00
return true ;
2023-08-16 21:58:25 +00:00
});
2023-03-27 02:42:36 +00:00
2023-08-16 21:58:25 +00:00
foreach ( $documents as $document ) {
$processDocument ( $collection , $document );
}
2021-12-27 10:45:24 +00:00
2025-02-12 11:19:51 +00:00
$queueForStatsUsage
2025-05-06 10:05:59 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_READS , \max ( 1 , $operations ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_READS ), \max ( 1 , $operations ));
2025-01-06 14:50:23 +00:00
2025-06-16 15:48:27 +00:00
$select = \array_reduce ( $queries , function ( $result , $query ) {
return $result || ( $query -> getMethod () === Query :: TYPE_SELECT );
}, false );
2024-02-24 10:55:33 +00:00
// Check if the SELECT query includes $databaseId and $collectionId
$hasDatabaseId = false ;
$hasCollectionId = false ;
2025-06-16 15:48:27 +00:00
if ( $select ) {
$hasDatabaseId = \array_reduce ( $queries , function ( $result , $query ) {
return $result || ( $query -> getMethod () === Query :: TYPE_SELECT && \in_array ( '$databaseId' , $query -> getValues ()));
}, false );
$hasCollectionId = \array_reduce ( $queries , function ( $result , $query ) {
return $result || ( $query -> getMethod () === Query :: TYPE_SELECT && \in_array ( '$collectionId' , $query -> getValues ()));
}, false );
}
if ( $select ) {
foreach ( $documents as $document ) {
if ( ! $hasDatabaseId ) {
$document -> removeAttribute ( '$databaseId' );
2025-06-09 14:15:48 +00:00
}
2025-06-16 15:48:27 +00:00
if ( ! $hasCollectionId ) {
$document -> removeAttribute ( '$collectionId' );
2024-02-24 10:55:33 +00:00
}
}
}
2021-07-25 14:47:18 +00:00
$response -> dynamic ( new Document ([
2022-02-27 09:57:09 +00:00
'total' => $total ,
2021-08-12 01:26:31 +00:00
'documents' => $documents ,
2021-06-14 19:55:36 +00:00
]), Response :: MODEL_DOCUMENT_LIST );
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/documents/:documentId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get document' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'documents.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'documents' ,
2025-01-17 04:31:39 +00:00
name : 'getDocument' ,
description : '/docs/references/databases/get-document.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-09-19 10:05:42 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'documentId' , '' , new UID (), 'Document ID.' )
2024-02-02 10:54:34 +00:00
-> param ( 'queries' , [], new ArrayList ( new Text ( APP_LIMIT_ARRAY_ELEMENT_SIZE ), APP_LIMIT_ARRAY_PARAMS_SIZE ), '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.' , true )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2025-01-30 04:53:53 +00:00
-> inject ( 'queueForStatsUsage' )
2025-02-17 12:49:51 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $documentId , array $queries , Response $response , Database $dbForProject , StatsUsage $queueForStatsUsage ) {
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2019-05-09 06:54:39 +00:00
2025-05-15 11:33:00 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2023-08-16 21:58:25 +00:00
if ( $database -> isEmpty () || ( ! $database -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-26 05:42:11 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2023-08-16 21:58:25 +00:00
if ( $collection -> isEmpty () || ( ! $collection -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-06-10 18:19:42 +00:00
}
2023-08-22 03:25:55 +00:00
try {
2024-02-12 16:02:04 +00:00
$queries = Query :: parseQueries ( $queries );
2023-08-22 03:25:55 +00:00
} catch ( QueryException $e ) {
2024-02-12 16:02:04 +00:00
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
2022-08-24 08:50:05 +00:00
}
2022-08-08 10:58:28 +00:00
2025-06-16 15:48:27 +00:00
try {
2025-06-09 08:53:04 +00:00
$document = $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $documentId , $queries );
2025-06-16 15:48:27 +00:00
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
2022-08-24 08:50:05 +00:00
}
2022-08-08 10:58:28 +00:00
if ( $document -> isEmpty ()) {
2022-08-16 09:07:30 +00:00
throw new Exception ( Exception :: DOCUMENT_NOT_FOUND );
2022-08-08 10:58:28 +00:00
}
2025-01-06 16:01:44 +00:00
$operations = 0 ;
2025-01-02 08:09:44 +00:00
2023-03-27 06:03:00 +00:00
// Add $collectionId and $databaseId for all documents
2025-01-02 08:09:44 +00:00
$processDocument = function ( Document $collection , Document $document ) use ( & $processDocument , $dbForProject , $database , & $operations ) {
2023-04-19 00:37:41 +00:00
if ( $document -> isEmpty ()) {
return ;
2023-03-28 09:02:49 +00:00
}
2025-01-06 16:01:44 +00:00
$operations ++ ;
2023-03-27 06:03:00 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-27 06:03:00 +00:00
);
2023-03-28 03:10:12 +00:00
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
2025-01-06 16:51:39 +00:00
if ( \in_array ( \gettype ( $related ), [ 'array' , 'object' ])) {
$operations ++ ;
}
2023-03-27 06:03:00 +00:00
continue ;
}
2025-01-06 16:01:44 +00:00
2023-03-28 03:10:12 +00:00
if ( ! \is_array ( $related )) {
$related = [ $related ];
}
2023-03-27 06:03:00 +00:00
2023-03-28 03:10:12 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 07:56:14 +00:00
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2023-03-28 03:10:12 +00:00
);
2023-03-27 02:42:36 +00:00
2023-03-29 01:37:56 +00:00
foreach ( $related as $relation ) {
2023-04-03 10:39:31 +00:00
if ( $relation instanceof Document ) {
$processDocument ( $relatedCollection , $relation );
}
2023-03-27 02:42:36 +00:00
}
}
};
2023-03-27 06:03:00 +00:00
2023-03-28 09:02:49 +00:00
$processDocument ( $collection , $document );
2021-12-27 10:45:24 +00:00
2025-02-12 11:19:51 +00:00
$queueForStatsUsage
2025-05-06 10:05:59 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_READS , \max ( 1 , $operations ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_READS ), \max ( 1 , $operations ));
2025-01-06 15:19:39 +00:00
2021-07-25 14:47:18 +00:00
$response -> dynamic ( $document , Response :: MODEL_DOCUMENT );
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/documents/:documentId/logs' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List document logs' )
2021-08-29 07:43:09 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'documents.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'logs' ,
2025-01-17 04:31:39 +00:00
name : 'listDocumentLogs' ,
description : '/docs/references/databases/get-document-logs.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_LOG_LIST ,
)
],
contentType : ContentType :: JSON ,
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2021-12-10 12:27:11 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
2022-09-19 10:05:42 +00:00
-> param ( 'documentId' , '' , new UID (), 'Document ID.' )
2023-05-16 12:56:20 +00:00
-> param ( 'queries' , [], new Queries ([ new Limit (), new Offset ()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset' , true )
2021-08-29 07:43:09 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2021-08-29 07:43:09 +00:00
-> inject ( 'locale' )
-> inject ( 'geodb' )
2022-08-23 13:03:38 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $documentId , array $queries , Response $response , Database $dbForProject , Locale $locale , Reader $geodb ) {
2024-03-06 17:34:21 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2022-06-22 10:51:49 +00:00
if ( $database -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2022-08-25 01:04:47 +00:00
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2021-08-29 07:43:09 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-08-29 07:43:09 +00:00
}
2025-05-26 05:42:11 +00:00
$document = $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $documentId );
2021-08-29 07:43:09 +00:00
if ( $document -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DOCUMENT_NOT_FOUND );
2021-08-29 07:43:09 +00:00
}
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2025-02-25 08:03:37 +00:00
// Temp fix for logs
$queries [] = Query :: or ([
2025-02-26 01:00:16 +00:00
Query :: greaterThan ( '$createdAt' , DateTime :: format ( new \DateTime ( '2025-02-26T01:30+00:00' ))),
2025-02-25 08:03:37 +00:00
Query :: lessThan ( '$createdAt' , DateTime :: format ( new \DateTime ( '2025-02-13T00:00+00:00' ))),
]);
2025-02-25 05:51:11 +00:00
2021-12-27 12:45:23 +00:00
$audit = new Audit ( $dbForProject );
2022-06-22 10:51:49 +00:00
$resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document -> getId ();
2025-02-25 07:26:00 +00:00
$logs = $audit -> getLogsByResource ( $resource , $queries );
2021-08-29 07:43:09 +00:00
$output = [];
foreach ( $logs as $i => & $log ) {
$log [ 'userAgent' ] = ( ! empty ( $log [ 'userAgent' ])) ? $log [ 'userAgent' ] : 'UNKNOWN' ;
2021-12-12 17:59:12 +00:00
$detector = new Detector ( $log [ 'userAgent' ]);
$detector -> skipBotDetection (); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
2021-08-29 07:43:09 +00:00
2021-12-12 17:59:12 +00:00
$os = $detector -> getOS ();
$client = $detector -> getClient ();
$device = $detector -> getDevice ();
2021-08-29 07:43:09 +00:00
$output [ $i ] = new Document ([
'event' => $log [ 'event' ],
2023-07-12 17:27:57 +00:00
'userId' => $log [ 'data' ][ 'userId' ],
2021-08-29 07:43:09 +00:00
'userEmail' => $log [ 'data' ][ 'userEmail' ] ? ? null ,
'userName' => $log [ 'data' ][ 'userName' ] ? ? null ,
'mode' => $log [ 'data' ][ 'mode' ] ? ? null ,
'ip' => $log [ 'ip' ],
'time' => $log [ 'time' ],
2021-12-12 17:59:12 +00:00
'osCode' => $os [ 'osCode' ],
'osName' => $os [ 'osName' ],
'osVersion' => $os [ 'osVersion' ],
'clientType' => $client [ 'clientType' ],
'clientCode' => $client [ 'clientCode' ],
'clientName' => $client [ 'clientName' ],
'clientVersion' => $client [ 'clientVersion' ],
'clientEngine' => $client [ 'clientEngine' ],
'clientEngineVersion' => $client [ 'clientEngineVersion' ],
'deviceName' => $device [ 'deviceName' ],
'deviceBrand' => $device [ 'deviceBrand' ],
'deviceModel' => $device [ 'deviceModel' ]
2021-08-29 07:43:09 +00:00
]);
$record = $geodb -> get ( $log [ 'ip' ]);
if ( $record ) {
2022-05-23 14:54:50 +00:00
$output [ $i ][ 'countryCode' ] = $locale -> getText ( 'countries.' . strtolower ( $record [ 'country' ][ 'iso_code' ]), false ) ? \strtolower ( $record [ 'country' ][ 'iso_code' ]) : '--' ;
$output [ $i ][ 'countryName' ] = $locale -> getText ( 'countries.' . strtolower ( $record [ 'country' ][ 'iso_code' ]), $locale -> getText ( 'locale.country.unknown' ));
2021-08-29 07:43:09 +00:00
} else {
$output [ $i ][ 'countryCode' ] = '--' ;
$output [ $i ][ 'countryName' ] = $locale -> getText ( 'locale.country.unknown' );
}
}
2025-01-06 16:57:35 +00:00
2021-11-18 10:33:42 +00:00
$response -> dynamic ( new Document ([
2025-02-25 07:26:00 +00:00
'total' => $audit -> countLogsByResource ( $resource , $queries ),
2025-02-25 06:21:35 +00:00
'logs' => $output ,
2021-11-18 10:33:42 +00:00
]), Response :: MODEL_LOG_LIST );
2021-08-29 07:43:09 +00:00
});
2022-06-22 10:51:49 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId' )
2024-11-04 09:13:38 +00:00
-> alias ( '/v1/database/collections/:collectionId/documents/:documentId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update document' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].documents.[documentId].update' )
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'documents.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'document.update' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}' )
2022-08-31 03:58:32 +00:00
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
2022-08-30 23:34:17 +00:00
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'documents' ,
2025-01-17 04:31:39 +00:00
name : 'updateDocument' ,
description : '/docs/references/databases/update-document.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT ,
)
],
contentType : ContentType :: JSON
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-09-19 10:05:42 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
-> param ( 'documentId' , '' , new UID (), 'Document ID.' )
2022-06-09 00:49:27 +00:00
-> param ( 'data' , [], new JSON (), 'Document data as JSON object. Include only attribute and value pairs to be updated.' , true )
2023-10-13 13:43:23 +00:00
-> param ( 'permissions' , null , new Permissions ( APP_LIMIT_ARRAY_PARAMS_SIZE , [ Database :: PERMISSION_READ , Database :: PERMISSION_UPDATE , Database :: PERMISSION_DELETE , Database :: PERMISSION_WRITE ]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
2023-01-20 00:36:17 +00:00
-> inject ( 'requestTimestamp' )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-01-30 04:53:53 +00:00
-> inject ( 'queueForStatsUsage' )
2025-02-17 12:49:51 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $documentId , string | array $data , ? array $permissions , ? \DateTime $requestTimestamp , Response $response , Database $dbForProject , Event $queueForEvents , StatsUsage $queueForStatsUsage ) {
2022-08-08 10:58:28 +00:00
$data = ( \is_string ( $data )) ? \json_decode ( $data , true ) : $data ; // Cast to JSON array
2022-08-14 05:21:33 +00:00
if ( empty ( $data ) && \is_null ( $permissions )) {
2022-08-16 09:07:30 +00:00
throw new Exception ( Exception :: DOCUMENT_MISSING_PAYLOAD );
2022-08-08 10:58:28 +00:00
}
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2025-05-15 11:33:00 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2023-08-16 21:58:25 +00:00
if ( $database -> isEmpty () || ( ! $database -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-26 05:42:11 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2023-08-16 21:58:25 +00:00
if ( $collection -> isEmpty () || ( ! $collection -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-08-12 01:05:19 +00:00
}
2022-08-25 01:04:47 +00:00
// Read permission should not be required for update
2025-05-26 05:42:11 +00:00
$document = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $documentId ));
2021-06-15 13:38:24 +00:00
if ( $document -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DOCUMENT_NOT_FOUND );
2020-06-29 21:43:34 +00:00
}
2019-05-09 06:54:39 +00:00
2022-08-25 03:51:21 +00:00
// Map aggregate permissions into the multiple permissions they represent.
$permissions = Permission :: aggregate ( $permissions , [
Database :: PERMISSION_READ ,
Database :: PERMISSION_UPDATE ,
Database :: PERMISSION_DELETE ,
]);
2022-08-15 12:56:19 +00:00
// Users can only manage their own roles, API keys and Admin users can manage any
2022-08-08 10:58:28 +00:00
$roles = Authorization :: getRoles ();
2023-08-16 21:58:25 +00:00
if ( ! $isAPIKey && ! $isPrivilegedUser && ! \is_null ( $permissions )) {
2022-08-15 12:56:19 +00:00
foreach ( Database :: PERMISSIONS as $type ) {
foreach ( $permissions as $permission ) {
2022-08-16 11:26:38 +00:00
$permission = Permission :: parse ( $permission );
if ( $permission -> getPermission () != $type ) {
2022-08-15 12:56:19 +00:00
continue ;
}
2022-08-16 11:26:38 +00:00
$role = ( new Role (
$permission -> getRole (),
$permission -> getIdentifier (),
$permission -> getDimension ()
)) -> toString ();
2022-08-15 12:56:19 +00:00
if ( ! Authorization :: isRole ( $role )) {
2022-08-25 03:51:21 +00:00
throw new Exception ( Exception :: USER_UNAUTHORIZED , 'Permissions must be one of: (' . \implode ( ', ' , $roles ) . ')' );
2022-08-15 12:56:19 +00:00
}
2022-08-08 10:58:28 +00:00
}
}
}
2022-08-02 09:18:49 +00:00
2022-08-25 11:46:56 +00:00
if ( \is_null ( $permissions )) {
$permissions = $document -> getPermissions () ? ? [];
}
2025-07-29 13:02:21 +00:00
// Remove sequence if set
unset ( $data [ '$sequence' ]);
2023-08-22 03:45:32 +00:00
$data [ '$id' ] = $documentId ;
2022-08-02 09:18:49 +00:00
$data [ '$permissions' ] = $permissions ;
2023-03-28 09:02:49 +00:00
$newDocument = new Document ( $data );
2020-10-30 19:53:27 +00:00
2025-01-06 16:51:39 +00:00
$operations = 0 ;
2025-01-06 16:01:44 +00:00
2025-01-06 09:28:26 +00:00
$setCollection = ( function ( Document $collection , Document $document ) use ( & $setCollection , $dbForProject , $database , & $operations ) {
2025-01-06 16:51:39 +00:00
$operations ++ ;
2023-03-28 09:02:49 +00:00
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-28 09:02:49 +00:00
);
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
continue ;
}
2023-04-13 03:59:57 +00:00
2023-04-14 10:03:16 +00:00
$isList = \is_array ( $related ) && \array_values ( $related ) === $related ;
if ( $isList ) {
2023-04-13 03:59:57 +00:00
$relations = $related ;
} else {
$relations = [ $related ];
2023-03-29 01:33:53 +00:00
}
2023-03-28 09:02:49 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 07:56:14 +00:00
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2025-01-02 07:56:14 +00:00
);
2023-03-28 09:02:49 +00:00
2023-04-19 00:37:41 +00:00
foreach ( $relations as & $relation ) {
2023-07-18 11:16:33 +00:00
// If the relation is an array it can be either update or create a child document.
2023-04-13 03:59:57 +00:00
if (
\is_array ( $relation )
&& \array_values ( $relation ) !== $relation
&& ! isset ( $relation [ '$id' ])
) {
$relation [ '$id' ] = ID :: unique ();
$relation = new Document ( $relation );
}
2023-03-29 01:37:56 +00:00
if ( $relation instanceof Document ) {
2025-01-02 07:56:14 +00:00
$oldDocument = Authorization :: skip ( fn () => $dbForProject -> getDocument (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence (),
2025-01-02 07:56:14 +00:00
$relation -> getId ()
));
2023-08-07 23:10:07 +00:00
$relation -> removeAttribute ( '$collectionId' );
$relation -> removeAttribute ( '$databaseId' );
2025-07-29 13:18:08 +00:00
$relation -> removeAttribute ( '$sequence' );
2023-08-07 23:10:07 +00:00
// Attribute $collection is required for Utopia.
2025-01-02 07:56:14 +00:00
$relation -> setAttribute (
'$collection' ,
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence ()
2025-01-02 07:56:14 +00:00
);
2023-03-31 04:33:21 +00:00
if ( $oldDocument -> isEmpty ()) {
2023-04-13 03:59:57 +00:00
if ( isset ( $relation [ '$id' ]) && $relation [ '$id' ] === 'unique()' ) {
2023-03-31 04:33:21 +00:00
$relation [ '$id' ] = ID :: unique ();
}
}
2023-08-07 23:10:07 +00:00
$setCollection ( $relatedCollection , $relation );
2023-03-28 09:02:49 +00:00
}
}
2023-04-13 03:59:57 +00:00
2023-04-14 10:03:16 +00:00
if ( $isList ) {
2023-04-13 03:59:57 +00:00
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \array_values ( $relations ));
} else {
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \reset ( $relations ));
}
2022-08-24 08:50:05 +00:00
}
2023-08-17 13:34:41 +00:00
});
2023-03-28 09:02:49 +00:00
2023-08-07 23:10:07 +00:00
$setCollection ( $collection , $newDocument );
2022-08-08 10:58:28 +00:00
2025-02-12 11:19:51 +00:00
$queueForStatsUsage
2025-05-06 10:05:59 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $operations ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $operations ));
2025-01-06 14:50:23 +00:00
2023-08-17 13:34:41 +00:00
try {
2025-04-30 05:40:47 +00:00
$document = $dbForProject -> updateDocument (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-04-30 05:40:47 +00:00
$document -> getId (),
$newDocument
2025-01-02 07:56:14 +00:00
);
2025-05-15 11:33:00 +00:00
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
2022-08-08 10:58:28 +00:00
} catch ( DuplicateException ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
2025-05-15 12:22:52 +00:00
} catch ( RelationshipException $e ) {
2025-05-15 11:33:00 +00:00
throw new Exception ( Exception :: RELATIONSHIP_VALUE_INVALID , $e -> getMessage ());
2024-11-04 07:57:08 +00:00
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
2021-08-15 21:09:40 +00:00
}
2021-12-16 18:12:06 +00:00
2023-03-27 06:03:00 +00:00
// Add $collectionId and $databaseId for all documents
2025-01-06 16:06:18 +00:00
$processDocument = function ( Document $collection , Document $document ) use ( & $processDocument , $dbForProject , $database ) {
2023-03-27 06:03:00 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
2023-03-27 02:42:36 +00:00
2023-03-27 06:03:00 +00:00
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-27 06:03:00 +00:00
);
2023-03-28 03:10:12 +00:00
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
2023-03-27 07:08:02 +00:00
continue ;
}
2023-03-28 03:10:12 +00:00
if ( ! \is_array ( $related )) {
$related = [ $related ];
}
2023-03-27 06:03:00 +00:00
2023-03-28 03:10:12 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 07:56:14 +00:00
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2025-01-02 07:56:14 +00:00
);
2023-03-27 07:08:02 +00:00
2023-03-29 01:37:56 +00:00
foreach ( $related as $relation ) {
2023-04-03 10:39:31 +00:00
if ( $relation instanceof Document ) {
$processDocument ( $relatedCollection , $relation );
}
2023-03-27 02:42:36 +00:00
}
}
};
2023-03-27 06:03:00 +00:00
2023-03-28 09:02:49 +00:00
$processDocument ( $collection , $document );
2023-03-27 02:42:36 +00:00
2025-05-20 14:37:10 +00:00
$relationships = \array_map (
fn ( $document ) => $document -> getAttribute ( 'key' ),
\array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
)
);
$queueForEvents
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collection -> getId ())
-> setParam ( 'documentId' , $document -> getId ())
-> setContext ( 'collection' , $collection )
-> setContext ( 'database' , $database )
-> setPayload ( $response -> getPayload (), sensitive : $relationships );
2024-07-11 15:03:28 +00:00
$response -> dynamic ( $document , Response :: MODEL_DOCUMENT );
2025-05-20 14:37:10 +00:00
});
App :: put ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId' )
-> desc ( 'Upsert document' )
-> groups ([ 'api' , 'database' ])
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].documents.[documentId].upsert' )
-> label ( 'scope' , 'documents.write' )
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
-> label ( 'audits.event' , 'document.upsert' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}' )
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
group : 'documents' ,
name : 'upsertDocument' ,
description : '/docs/references/databases/upsert-document.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT ,
)
],
contentType : ContentType :: JSON
))
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
-> param ( 'documentId' , '' , new CustomId (), 'Document ID.' )
-> param ( 'data' , [], new JSON (), 'Document data as JSON object. Include all required attributes of the document to be created or updated.' )
2025-07-09 16:51:49 +00:00
-> param ( 'permissions' , null , new Permissions ( APP_LIMIT_ARRAY_PARAMS_SIZE , [ Database :: PERMISSION_READ , Database :: PERMISSION_UPDATE , Database :: PERMISSION_DELETE , Database :: PERMISSION_WRITE ]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).' , true )
2025-05-20 14:37:10 +00:00
-> inject ( 'requestTimestamp' )
-> inject ( 'response' )
2025-07-21 14:02:27 +00:00
-> inject ( 'user' )
2025-05-20 14:37:10 +00:00
-> inject ( 'dbForProject' )
-> inject ( 'queueForEvents' )
-> inject ( 'queueForStatsUsage' )
2025-07-21 14:02:27 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $documentId , string | array $data , ? array $permissions , ? \DateTime $requestTimestamp , Response $response , Document $user , Database $dbForProject , Event $queueForEvents , StatsUsage $queueForStatsUsage ) {
2025-05-20 14:37:10 +00:00
$data = ( \is_string ( $data )) ? \json_decode ( $data , true ) : $data ; // Cast to JSON array
if ( empty ( $data ) && \is_null ( $permissions )) {
throw new Exception ( Exception :: DOCUMENT_MISSING_PAYLOAD );
}
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
if ( $database -> isEmpty () || ( ! $database -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2025-05-20 14:37:10 +00:00
if ( $collection -> isEmpty () || ( ! $collection -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
2025-07-21 14:02:27 +00:00
$allowedPermissions = [
2025-05-20 14:37:10 +00:00
Database :: PERMISSION_READ ,
Database :: PERMISSION_UPDATE ,
Database :: PERMISSION_DELETE ,
2025-07-21 14:02:27 +00:00
];
$permissions = Permission :: aggregate ( $permissions , $allowedPermissions );
2025-07-22 07:40:05 +00:00
// if no permission, upsert permission from the old document if present (update scenario) else add default permission (create scenario)
2025-07-21 14:02:27 +00:00
if ( \is_null ( $permissions )) {
2025-07-22 07:40:05 +00:00
$oldDocument = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $documentId ));
2025-07-21 14:02:27 +00:00
if ( $oldDocument -> isEmpty ()) {
if ( ! empty ( $user -> getId ())) {
$defaultPermissions = [];
foreach ( $allowedPermissions as $permission ) {
$defaultPermissions [] = ( new Permission ( $permission , 'user' , $user -> getId ())) -> toString ();
}
$permissions = $defaultPermissions ;
}
} else {
$permissions = $oldDocument -> getPermissions ();
}
}
2025-05-20 14:37:10 +00:00
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization :: getRoles ();
if ( ! $isAPIKey && ! $isPrivilegedUser && ! \is_null ( $permissions )) {
foreach ( Database :: PERMISSIONS as $type ) {
foreach ( $permissions as $permission ) {
$permission = Permission :: parse ( $permission );
if ( $permission -> getPermission () != $type ) {
continue ;
}
$role = ( new Role (
$permission -> getRole (),
$permission -> getIdentifier (),
$permission -> getDimension ()
)) -> toString ();
if ( ! Authorization :: isRole ( $role )) {
throw new Exception ( Exception :: USER_UNAUTHORIZED , 'Permissions must be one of: (' . \implode ( ', ' , $roles ) . ')' );
}
}
}
}
$data [ '$id' ] = $documentId ;
2025-07-09 16:51:49 +00:00
$data [ '$permissions' ] = $permissions ? ? [];
2025-05-20 14:37:10 +00:00
$newDocument = new Document ( $data );
$operations = 0 ;
$setCollection = ( function ( Document $collection , Document $document ) use ( & $setCollection , $dbForProject , $database , & $operations ) {
$operations ++ ;
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
);
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
continue ;
}
$isList = \is_array ( $related ) && \array_values ( $related ) === $related ;
if ( $isList ) {
$relations = $related ;
} else {
$relations = [ $related ];
}
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2025-05-20 14:37:10 +00:00
);
foreach ( $relations as & $relation ) {
// If the relation is an array it can be either update or create a child document.
if (
\is_array ( $relation )
&& \array_values ( $relation ) !== $relation
&& ! isset ( $relation [ '$id' ])
) {
$relation [ '$id' ] = ID :: unique ();
$relation = new Document ( $relation );
}
if ( $relation instanceof Document ) {
$oldDocument = Authorization :: skip ( fn () => $dbForProject -> getDocument (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence (),
2025-05-20 14:37:10 +00:00
$relation -> getId ()
));
$relation -> removeAttribute ( '$collectionId' );
$relation -> removeAttribute ( '$databaseId' );
// Attribute $collection is required for Utopia.
$relation -> setAttribute (
'$collection' ,
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $relatedCollection -> getSequence ()
2025-05-20 14:37:10 +00:00
);
if ( $oldDocument -> isEmpty ()) {
if ( isset ( $relation [ '$id' ]) && $relation [ '$id' ] === 'unique()' ) {
$relation [ '$id' ] = ID :: unique ();
}
}
$setCollection ( $relatedCollection , $relation );
}
}
if ( $isList ) {
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \array_values ( $relations ));
} else {
$document -> setAttribute ( $relationship -> getAttribute ( 'key' ), \reset ( $relations ));
}
}
});
$setCollection ( $collection , $newDocument );
$queueForStatsUsage
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $operations ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $operations ));
2025-05-20 14:37:10 +00:00
$upserted = [];
try {
$modified = $dbForProject -> createOrUpdateDocuments (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-05-20 14:37:10 +00:00
[ $newDocument ],
onNext : function ( Document $document ) use ( & $upserted ) {
$upserted [] = $document ;
},
);
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( DuplicateException ) {
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
} catch ( RelationshipException $e ) {
throw new Exception ( Exception :: RELATIONSHIP_VALUE_INVALID , $e -> getMessage ());
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
}
$document = $upserted [ 0 ];
// Add $collectionId and $databaseId for all documents
$processDocument = function ( Document $collection , Document $document ) use ( & $processDocument , $dbForProject , $database ) {
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-27 06:03:00 +00:00
);
2023-03-28 03:10:12 +00:00
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
2023-03-27 07:08:02 +00:00
continue ;
}
2023-03-28 03:10:12 +00:00
if ( ! \is_array ( $related )) {
$related = [ $related ];
}
2023-03-27 06:03:00 +00:00
2023-03-28 03:10:12 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2023-03-28 03:10:12 +00:00
);
2023-03-27 07:08:02 +00:00
2023-03-29 01:37:56 +00:00
foreach ( $related as $relation ) {
2023-04-03 10:39:31 +00:00
if ( $relation instanceof Document ) {
$processDocument ( $relatedCollection , $relation );
}
2023-03-27 02:42:36 +00:00
}
}
};
2023-03-27 06:03:00 +00:00
2023-03-28 09:02:49 +00:00
$processDocument ( $collection , $document );
2024-07-11 15:03:28 +00:00
$relationships = \array_map (
fn ( $document ) => $document -> getAttribute ( 'key' ),
\array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
)
);
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
-> setParam ( 'documentId' , $document -> getId ())
2022-06-22 10:51:49 +00:00
-> setContext ( 'collection' , $collection )
-> setContext ( 'database' , $database )
2024-07-11 15:03:28 +00:00
-> setPayload ( $response -> getPayload (), sensitive : $relationships );
2025-04-30 05:40:47 +00:00
$response -> dynamic ( $document , Response :: MODEL_DOCUMENT );
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2025-06-10 04:22:38 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/increment' )
2025-06-10 03:56:30 +00:00
-> desc ( 'Increment document attribute' )
-> groups ([ 'api' , 'database' ])
2025-06-10 04:35:03 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].documents.[documentId].increment' )
2025-06-10 03:56:30 +00:00
-> label ( 'scope' , 'documents.write' )
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
-> label ( 'audits.event' , 'documents.increment' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
group : 'documents' ,
name : 'incrementDocumentAttribute' ,
description : '/docs/references/databases/increment-document-attribute.md' ,
2025-06-10 19:44:40 +00:00
auth : [ AuthType :: ADMIN , AuthType :: KEY , AuthType :: SESSION , AuthType :: JWT ],
2025-06-10 03:56:30 +00:00
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT ,
)
],
contentType : ContentType :: JSON
))
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
-> param ( 'documentId' , '' , new UID (), 'Document ID.' )
2025-06-10 04:32:38 +00:00
-> param ( 'attribute' , '' , new Key (), 'Attribute key.' )
2025-06-10 03:56:30 +00:00
-> param ( 'value' , 1 , new Numeric (), 'Value to increment the attribute by. The value must be a number.' , true )
-> param ( 'max' , null , new Numeric (), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.' , true )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'queueForEvents' )
-> inject ( 'queueForStatsUsage' )
-> action ( function ( string $databaseId , string $collectionId , string $documentId , string $attribute , int | float $value , int | float | null $max , Response $response , Database $dbForProject , Event $queueForEvents , StatsUsage $queueForStatsUsage ) {
2025-06-10 04:22:46 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2025-06-10 03:56:30 +00:00
if ( $database -> isEmpty ()) {
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-06-10 04:22:46 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2025-06-10 03:56:30 +00:00
if ( $collection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
try {
$document = $dbForProject -> increaseDocumentAttribute (
collection : 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
id : $documentId ,
attribute : $attribute ,
value : $value ,
max : $max
);
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( NotFoundException ) {
throw new Exception ( Exception :: ATTRIBUTE_NOT_FOUND );
} catch ( LimitException ) {
2025-06-10 15:50:57 +00:00
throw new Exception ( Exception :: ATTRIBUTE_LIMIT_EXCEEDED , 'Attribute "' . $attribute . '" has reached the maximum value of ' . $max );
2025-06-10 03:56:30 +00:00
} catch ( TypeException ) {
2025-06-10 15:50:57 +00:00
throw new Exception ( Exception :: ATTRIBUTE_TYPE_INVALID , 'Attribute "' . $attribute . '" is not a number' );
2025-06-10 03:56:30 +00:00
}
$queueForStatsUsage
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , 1 )
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), 1 );
$queueForEvents
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collectionId )
-> setContext ( 'collection' , $collection )
-> setContext ( 'database' , $database );
$response -> dynamic ( $document , Response :: MODEL_DOCUMENT );
});
2025-06-10 04:22:38 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/decrement' )
2025-06-10 03:56:30 +00:00
-> desc ( 'Decrement document attribute' )
-> groups ([ 'api' , 'database' ])
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].documents.[documentId].decrement' )
-> label ( 'scope' , 'documents.write' )
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
-> label ( 'audits.event' , 'documents.decrement' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
group : 'documents' ,
name : 'decrementDocumentAttribute' ,
2025-06-10 04:36:55 +00:00
description : '/docs/references/databases/decrement-document-attribute.md' ,
2025-06-10 19:44:40 +00:00
auth : [ AuthType :: ADMIN , AuthType :: KEY , AuthType :: SESSION , AuthType :: JWT ],
2025-06-10 03:56:30 +00:00
responses : [
new SDKResponse (
2025-06-10 04:37:33 +00:00
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT ,
2025-06-10 03:56:30 +00:00
)
],
contentType : ContentType :: JSON
))
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
-> param ( 'documentId' , '' , new UID (), 'Document ID.' )
2025-06-10 04:32:38 +00:00
-> param ( 'attribute' , '' , new Key (), 'Attribute key.' )
2025-06-10 03:56:30 +00:00
-> param ( 'value' , 1 , new Numeric (), 'Value to decrement the attribute by. The value must be a number.' , true )
-> param ( 'min' , null , new Numeric (), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.' , true )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'queueForEvents' )
-> inject ( 'queueForStatsUsage' )
-> action ( function ( string $databaseId , string $collectionId , string $documentId , string $attribute , int | float $value , int | float | null $min , Response $response , Database $dbForProject , Event $queueForEvents , StatsUsage $queueForStatsUsage ) {
2025-06-10 04:22:46 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2025-06-10 03:56:30 +00:00
if ( $database -> isEmpty ()) {
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-06-10 04:22:46 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2025-06-10 03:56:30 +00:00
if ( $collection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
try {
$document = $dbForProject -> decreaseDocumentAttribute (
collection : 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
id : $documentId ,
attribute : $attribute ,
value : $value ,
min : $min
);
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( NotFoundException ) {
throw new Exception ( Exception :: ATTRIBUTE_NOT_FOUND );
} catch ( LimitException ) {
throw new Exception ( Exception :: ATTRIBUTE_LIMIT_EXCEEDED , 'Attribute "' . $attribute . '" has reached the minimum value of ' . $min );
} catch ( TypeException ) {
throw new Exception ( Exception :: ATTRIBUTE_TYPE_INVALID , 'Attribute "' . $attribute . '" is not a number' );
}
$queueForStatsUsage
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , 1 )
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), 1 );
$queueForEvents
-> setParam ( 'databaseId' , $databaseId )
-> setParam ( 'collectionId' , $collectionId )
-> setContext ( 'collection' , $collection )
-> setContext ( 'database' , $database );
$response -> dynamic ( $document , Response :: MODEL_DOCUMENT );
});
2024-11-11 06:27:26 +00:00
App :: patch ( '/v1/databases/:databaseId/collections/:collectionId/documents' )
-> desc ( 'Update documents' )
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'documents.write' )
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
-> label ( 'audits.event' , 'documents.update' )
2024-11-12 02:41:08 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
2024-11-11 06:27:26 +00:00
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
2024-12-16 05:59:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-04-29 22:45:18 +00:00
group : 'documents' ,
2024-12-16 05:59:01 +00:00
name : 'updateDocuments' ,
description : '/docs/references/databases/update-documents.md' ,
2025-06-10 19:50:18 +00:00
auth : [ AuthType :: ADMIN , AuthType :: KEY ],
2024-12-30 11:32:40 +00:00
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
2025-05-07 03:44:58 +00:00
model : Response :: MODEL_DOCUMENT_LIST ,
2024-12-30 11:32:40 +00:00
)
],
2025-01-15 03:54:49 +00:00
contentType : ContentType :: JSON
2024-12-16 05:59:01 +00:00
))
2024-11-11 06:27:26 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
-> param ( 'data' , [], new JSON (), 'Document data as JSON object. Include only attribute and value pairs to be updated.' , true )
-> param ( 'queries' , [], new ArrayList ( new Text ( APP_LIMIT_ARRAY_ELEMENT_SIZE ), APP_LIMIT_ARRAY_PARAMS_SIZE ), '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.' , true )
-> inject ( 'requestTimestamp' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2025-04-30 02:22:48 +00:00
-> inject ( 'queueForStatsUsage' )
2025-05-07 03:28:53 +00:00
-> inject ( 'plan' )
-> action ( function ( string $databaseId , string $collectionId , string | array $data , array $queries , ? \DateTime $requestTimestamp , Response $response , Database $dbForProject , StatsUsage $queueForStatsUsage , array $plan ) {
2025-03-05 03:42:09 +00:00
$data = \is_string ( $data )
? \json_decode ( $data , true )
: $data ;
2024-11-11 06:27:26 +00:00
2025-03-05 03:42:09 +00:00
if ( empty ( $data )) {
2024-11-11 06:27:26 +00:00
throw new Exception ( Exception :: DOCUMENT_MISSING_PAYLOAD );
}
2025-05-07 14:59:04 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
2024-11-11 06:27:26 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2025-05-07 14:59:04 +00:00
if ( $collection -> isEmpty ()) {
2024-11-11 06:27:26 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
2025-03-27 05:13:10 +00:00
$hasRelationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
);
if ( $hasRelationships ) {
2025-04-30 00:08:38 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Bulk update is not supported for collections with relationship attributes' );
2025-03-27 05:13:10 +00:00
}
2025-03-05 02:09:06 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2024-11-11 06:27:26 +00:00
2025-05-08 06:09:37 +00:00
if ( $data [ '$permissions' ]) {
$validator = new Permissions ();
if ( ! $validator -> isValid ( $data [ '$permissions' ])) {
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , $validator -> getDescription ());
}
2024-11-13 04:41:18 +00:00
}
2025-03-05 02:09:06 +00:00
2025-07-29 13:02:21 +00:00
// Remove sequence if set
unset ( $data [ '$sequence' ]);
2025-05-07 03:44:58 +00:00
$documents = [];
2025-05-08 06:10:26 +00:00
try {
$modified = $dbForProject -> updateDocuments (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-05-08 06:10:26 +00:00
new Document ( $data ),
$queries ,
onNext : function ( Document $document ) use ( $plan , & $documents ) {
if ( \count ( $documents ) < ( $plan [ 'databasesBatchSize' ] ? ? APP_LIMIT_DATABASE_BATCH )) {
$documents [] = $document ;
}
},
);
2025-05-15 11:33:00 +00:00
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( RelationshipException $e ) {
throw new Exception ( Exception :: RELATIONSHIP_VALUE_INVALID , $e -> getMessage ());
2025-05-08 06:10:26 +00:00
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
}
2025-05-07 03:44:58 +00:00
2025-05-07 14:59:04 +00:00
foreach ( $documents as $document ) {
2024-12-03 06:14:07 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
2025-05-07 03:44:58 +00:00
}
2024-11-11 06:27:26 +00:00
2025-04-30 02:22:48 +00:00
$queueForStatsUsage
2025-05-07 14:59:04 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $modified ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $modified ));
2025-04-30 02:22:48 +00:00
2025-05-07 14:59:04 +00:00
$response -> dynamic ( new Document ([
'total' => $modified ,
'documents' => $documents
]), Response :: MODEL_DOCUMENT_LIST );
2024-11-11 06:27:26 +00:00
});
2025-05-07 14:49:02 +00:00
App :: put ( '/v1/databases/:databaseId/collections/:collectionId/documents' )
2025-06-03 06:59:30 +00:00
-> desc ( 'Upsert documents' )
2025-05-07 14:49:02 +00:00
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'documents.write' )
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
-> label ( 'audits.event' , 'documents.upsert' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT * 2 )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
group : 'documents' ,
name : 'upsertDocuments' ,
description : '/docs/references/databases/upsert-documents.md' ,
2025-06-10 19:50:18 +00:00
auth : [ AuthType :: ADMIN , AuthType :: KEY ],
2025-05-07 14:49:02 +00:00
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DOCUMENT_LIST ,
)
],
contentType : ContentType :: JSON
))
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
2025-05-26 10:25:12 +00:00
-> param ( 'documents' , [], fn ( array $plan ) => new ArrayList ( new JSON (), $plan [ 'databasesBatchSize' ] ? ? APP_LIMIT_DATABASE_BATCH ), 'Array of document data as JSON objects. May contain partial documents.' , false , [ 'plan' ])
2025-05-07 14:49:02 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'queueForStatsUsage' )
-> inject ( 'plan' )
-> action ( function ( string $databaseId , string $collectionId , array $documents , Response $response , Database $dbForProject , StatsUsage $queueForStatsUsage , array $plan ) {
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2025-05-07 14:49:02 +00:00
if ( $collection -> isEmpty ()) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
$hasRelationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
);
if ( $hasRelationships ) {
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Bulk upsert is not supported for collections with relationship attributes' );
}
foreach ( $documents as $key => $document ) {
$documents [ $key ] = new Document ( $document );
}
$upserted = [];
2025-05-15 11:33:00 +00:00
try {
$modified = $dbForProject -> createOrUpdateDocuments (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-05-15 11:33:00 +00:00
$documents ,
onNext : function ( Document $document ) use ( $plan , & $upserted ) {
if ( \count ( $upserted ) < ( $plan [ 'databasesBatchSize' ] ? ? APP_LIMIT_DATABASE_BATCH )) {
$upserted [] = $document ;
}
},
);
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( DuplicateException ) {
throw new Exception ( Exception :: DOCUMENT_ALREADY_EXISTS );
} catch ( RelationshipException $e ) {
throw new Exception ( Exception :: RELATIONSHIP_VALUE_INVALID , $e -> getMessage ());
} catch ( StructureException $e ) {
throw new Exception ( Exception :: DOCUMENT_INVALID_STRUCTURE , $e -> getMessage ());
}
2025-05-07 14:49:02 +00:00
foreach ( $upserted as $document ) {
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
}
$queueForStatsUsage
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $modified ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $modified ));
2025-05-07 14:49:02 +00:00
$response -> dynamic ( new Document ([
'total' => $modified ,
'documents' => $upserted
]), Response :: MODEL_DOCUMENT_LIST );
2020-12-26 15:05:04 +00:00
});
2019-05-09 06:54:39 +00:00
2022-06-22 10:51:49 +00:00
App :: delete ( '/v1/databases/:databaseId/collections/:collectionId/documents/:documentId' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/collections/:collectionId/documents/:documentId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete document' )
2020-06-25 18:32:12 +00:00
-> groups ([ 'api' , 'database' ])
2019-06-08 13:13:19 +00:00
-> label ( 'scope' , 'documents.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2022-06-22 10:51:49 +00:00
-> label ( 'event' , 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'document.delete' )
2022-08-08 14:32:54 +00:00
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}' )
2022-08-31 03:58:32 +00:00
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
2022-08-30 23:34:17 +00:00
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-03-31 05:48:17 +00:00
group : 'documents' ,
2025-01-17 04:31:39 +00:00
name : 'deleteDocument' ,
description : '/docs/references/databases/delete-document.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2022-06-22 10:51:49 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2022-09-19 10:05:42 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'documentId' , '' , new UID (), 'Document ID.' )
2023-01-20 00:36:17 +00:00
-> inject ( 'requestTimestamp' )
2020-12-26 15:05:04 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-01-30 04:53:53 +00:00
-> inject ( 'queueForStatsUsage' )
2025-02-17 12:49:51 +00:00
-> action ( function ( string $databaseId , string $collectionId , string $documentId , ? \DateTime $requestTimestamp , Response $response , Database $dbForProject , Event $queueForEvents , StatsUsage $queueForStatsUsage ) {
2023-08-08 19:46:01 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2019-05-09 06:54:39 +00:00
2025-05-15 11:33:00 +00:00
$database = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'databases' , $databaseId ));
2023-08-16 21:58:25 +00:00
if ( $database -> isEmpty () || ( ! $database -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
2022-06-22 10:51:49 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-26 05:42:11 +00:00
$collection = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId ));
2023-08-16 21:58:25 +00:00
if ( $collection -> isEmpty () || ( ! $collection -> getAttribute ( 'enabled' , false ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2021-08-12 01:05:19 +00:00
}
2022-08-25 01:04:47 +00:00
// Read permission should not be required for delete
2025-05-26 05:42:11 +00:00
$document = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (), $documentId ));
2021-06-10 18:19:42 +00:00
if ( $document -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DOCUMENT_NOT_FOUND );
2020-06-29 21:43:34 +00:00
}
2019-05-09 06:54:39 +00:00
2024-11-04 07:57:08 +00:00
try {
2025-04-30 05:40:47 +00:00
$dbForProject -> deleteDocument (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-04-30 05:40:47 +00:00
$documentId
);
2025-05-15 11:33:00 +00:00
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( RestrictedException ) {
throw new Exception ( Exception :: DOCUMENT_DELETE_RESTRICTED );
2024-11-04 07:57:08 +00:00
}
2022-08-08 10:58:28 +00:00
2025-05-06 10:05:59 +00:00
$operations = 0 ;
2025-04-30 02:22:48 +00:00
2023-03-27 06:03:00 +00:00
// Add $collectionId and $databaseId for all documents
2025-04-30 02:22:48 +00:00
$processDocument = function ( Document $collection , Document $document ) use ( & $processDocument , $dbForProject , $database , & $operations ) {
$operations ++ ;
2023-03-27 06:03:00 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
2021-06-10 18:19:42 +00:00
2023-03-27 06:03:00 +00:00
$relationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
2024-03-06 17:34:21 +00:00
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
2023-03-27 06:03:00 +00:00
);
2023-03-28 03:10:12 +00:00
foreach ( $relationships as $relationship ) {
$related = $document -> getAttribute ( $relationship -> getAttribute ( 'key' ));
if ( empty ( $related )) {
2023-03-27 06:03:00 +00:00
continue ;
}
2023-03-28 03:10:12 +00:00
if ( ! \is_array ( $related )) {
$related = [ $related ];
}
2023-03-27 06:03:00 +00:00
2023-03-28 03:10:12 +00:00
$relatedCollectionId = $relationship -> getAttribute ( 'relatedCollection' );
2025-01-02 07:56:14 +00:00
$relatedCollection = Authorization :: skip (
2025-05-26 05:42:11 +00:00
fn () => $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $relatedCollectionId )
2025-01-02 07:56:14 +00:00
);
2023-03-27 02:42:36 +00:00
2023-03-29 03:30:47 +00:00
foreach ( $related as $relation ) {
2023-04-03 10:39:31 +00:00
if ( $relation instanceof Document ) {
$processDocument ( $relatedCollection , $relation );
}
2023-03-27 02:42:36 +00:00
}
}
};
2023-03-27 06:03:00 +00:00
2023-03-28 09:02:49 +00:00
$processDocument ( $collection , $document );
2021-12-27 10:45:24 +00:00
2025-02-12 11:19:51 +00:00
$queueForStatsUsage
2025-05-06 10:05:59 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $operations ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $operations ));
2025-01-06 16:57:35 +00:00
2024-07-11 15:03:28 +00:00
$relationships = \array_map (
fn ( $document ) => $document -> getAttribute ( 'key' ),
\array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
)
);
2022-01-03 10:29:15 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-06-22 10:51:49 +00:00
-> setParam ( 'databaseId' , $databaseId )
2022-04-13 12:39:31 +00:00
-> setParam ( 'collectionId' , $collection -> getId ())
-> setParam ( 'documentId' , $document -> getId ())
2022-06-22 10:51:49 +00:00
-> setContext ( 'collection' , $collection )
-> setContext ( 'database' , $database )
2024-07-11 15:03:28 +00:00
-> setPayload ( $response -> output ( $document , Response :: MODEL_DOCUMENT ), sensitive : $relationships );
2021-04-14 09:02:17 +00:00
2020-06-29 21:43:34 +00:00
$response -> noContent ();
2021-08-24 14:03:32 +00:00
});
2022-06-22 10:51:49 +00:00
2024-11-07 03:30:17 +00:00
App :: delete ( '/v1/databases/:databaseId/collections/:collectionId/documents' )
-> desc ( 'Delete documents' )
-> groups ([ 'api' , 'database' ])
-> label ( 'scope' , 'documents.write' )
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
-> label ( 'audits.event' , 'documents.delete' )
-> label ( 'audits.resource' , 'database/{request.databaseId}/collection/{request.collectionId}' )
-> label ( 'abuse-key' , 'ip:{ip},method:{method},url:{url},userId:{userId}' )
-> label ( 'abuse-limit' , APP_LIMIT_WRITE_RATE_DEFAULT )
-> label ( 'abuse-time' , APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT )
2024-12-16 05:59:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-04-29 22:45:18 +00:00
group : 'documents' ,
2024-12-16 05:59:01 +00:00
name : 'deleteDocuments' ,
description : '/docs/references/databases/delete-documents.md' ,
2025-06-10 19:50:18 +00:00
auth : [ AuthType :: ADMIN , AuthType :: KEY ],
2024-12-30 11:32:40 +00:00
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
2025-05-07 03:44:58 +00:00
model : Response :: MODEL_DOCUMENT_LIST ,
2024-12-30 11:32:40 +00:00
)
],
2025-01-15 03:54:49 +00:00
contentType : ContentType :: JSON
2024-12-16 05:59:01 +00:00
))
2024-11-07 03:30:17 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
-> param ( 'collectionId' , '' , new UID (), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).' )
-> param ( 'queries' , [], new ArrayList ( new Text ( APP_LIMIT_ARRAY_ELEMENT_SIZE ), APP_LIMIT_ARRAY_PARAMS_SIZE ), '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.' , true )
-> inject ( 'requestTimestamp' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2025-03-05 02:09:06 +00:00
-> inject ( 'queueForStatsUsage' )
2025-05-07 03:28:53 +00:00
-> inject ( 'plan' )
-> action ( function ( string $databaseId , string $collectionId , array $queries , ? \DateTime $requestTimestamp , Response $response , Database $dbForProject , StatsUsage $queueForStatsUsage , array $plan ) {
2025-05-07 14:59:04 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
2024-11-07 03:30:17 +00:00
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2025-05-26 05:42:11 +00:00
$collection = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
2025-05-07 14:59:04 +00:00
if ( $collection -> isEmpty ()) {
2024-11-07 03:30:17 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
}
2025-03-27 05:13:10 +00:00
$hasRelationships = \array_filter (
$collection -> getAttribute ( 'attributes' , []),
fn ( $attribute ) => $attribute -> getAttribute ( 'type' ) === Database :: VAR_RELATIONSHIP
);
if ( $hasRelationships ) {
2025-04-30 00:08:38 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Bulk delete is not supported for collections with relationship attributes' );
2025-03-27 05:13:10 +00:00
}
2025-03-05 02:09:06 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2024-11-07 03:30:17 +00:00
2025-05-07 03:44:58 +00:00
$documents = [];
2025-05-08 06:10:26 +00:00
try {
$modified = $dbForProject -> deleteDocuments (
2025-05-26 05:42:11 +00:00
'database_' . $database -> getSequence () . '_collection_' . $collection -> getSequence (),
2025-05-08 06:10:26 +00:00
$queries ,
onNext : function ( Document $document ) use ( $plan , & $documents ) {
if ( \count ( $documents ) < ( $plan [ 'databasesBatchSize' ] ? ? APP_LIMIT_DATABASE_BATCH )) {
$documents [] = $document ;
}
},
);
2025-05-15 11:33:00 +00:00
} catch ( ConflictException ) {
throw new Exception ( Exception :: DOCUMENT_UPDATE_CONFLICT );
} catch ( RestrictedException ) {
throw new Exception ( Exception :: DOCUMENT_DELETE_RESTRICTED );
2025-05-08 06:10:26 +00:00
}
2025-05-07 03:44:58 +00:00
2025-05-07 14:59:04 +00:00
foreach ( $documents as $document ) {
2024-11-22 01:43:45 +00:00
$document -> setAttribute ( '$databaseId' , $database -> getId ());
$document -> setAttribute ( '$collectionId' , $collection -> getId ());
2025-05-07 03:44:58 +00:00
}
2025-05-05 14:21:45 +00:00
2025-04-30 02:22:48 +00:00
$queueForStatsUsage
2025-05-07 14:59:04 +00:00
-> addMetric ( METRIC_DATABASES_OPERATIONS_WRITES , \max ( 1 , $modified ))
2025-05-26 05:42:11 +00:00
-> addMetric ( str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES ), \max ( 1 , $modified ));
2025-04-30 02:22:48 +00:00
2025-05-07 14:59:04 +00:00
$response -> dynamic ( new Document ([
'total' => $modified ,
'documents' => $documents ,
]), Response :: MODEL_DOCUMENT_LIST );
2024-11-07 03:30:17 +00:00
});
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/usage' )
2024-02-26 02:44:20 +00:00
-> desc ( 'Get databases usage stats' )
2023-11-06 21:28:45 +00:00
-> groups ([ 'api' , 'database' , 'usage' ])
2022-06-23 08:50:11 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'getUsage' ,
2025-01-17 07:44:25 +00:00
description : '/docs/references/databases/get-usage.md' ,
2025-01-17 04:31:39 +00:00
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_USAGE_DATABASES ,
)
],
contentType : ContentType :: JSON
))
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ], true ), '`Date range.' , true )
2022-06-23 08:50:11 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> action ( function ( string $range , Response $response , Database $dbForProject ) {
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
METRIC_DATABASES ,
METRIC_COLLECTIONS ,
METRIC_DOCUMENTS ,
2025-01-23 13:24:02 +00:00
METRIC_DATABASES_STORAGE ,
METRIC_DATABASES_OPERATIONS_READS ,
METRIC_DATABASES_OPERATIONS_WRITES ,
2023-10-25 07:39:59 +00:00
];
2022-06-23 08:50:11 +00:00
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 15:20:12 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2022-06-22 10:51:49 +00:00
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
2023-10-15 17:41:09 +00:00
}
2023-10-25 07:39:59 +00:00
}
});
2023-10-15 17:41:09 +00:00
2023-10-25 07:39:59 +00:00
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2023-10-15 17:41:09 +00:00
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2022-06-23 08:50:11 +00:00
}
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2023-11-08 09:09:32 +00:00
'databasesTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'collectionsTotal' => $usage [ $metrics [ 1 ]][ 'total' ],
'documentsTotal' => $usage [ $metrics [ 2 ]][ 'total' ],
2024-07-18 04:58:02 +00:00
'storageTotal' => $usage [ $metrics [ 3 ]][ 'total' ],
2025-01-23 13:24:02 +00:00
'databasesReadsTotal' => $usage [ $metrics [ 4 ]][ 'total' ],
'databasesWritesTotal' => $usage [ $metrics [ 5 ]][ 'total' ],
2023-11-08 09:09:32 +00:00
'databases' => $usage [ $metrics [ 0 ]][ 'data' ],
'collections' => $usage [ $metrics [ 1 ]][ 'data' ],
'documents' => $usage [ $metrics [ 2 ]][ 'data' ],
2024-07-18 04:58:02 +00:00
'storage' => $usage [ $metrics [ 3 ]][ 'data' ],
2025-01-23 13:24:02 +00:00
'databasesReads' => $usage [ $metrics [ 4 ]][ 'data' ],
'databasesWrites' => $usage [ $metrics [ 5 ]][ 'data' ],
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_DATABASES );
2022-06-23 08:50:11 +00:00
});
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/usage' )
2024-02-26 02:44:20 +00:00
-> desc ( 'Get database usage stats' )
2023-11-06 21:28:45 +00:00
-> groups ([ 'api' , 'database' , 'usage' ])
2022-06-23 08:50:11 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'getDatabaseUsage' ,
2025-01-17 07:44:25 +00:00
description : '/docs/references/databases/get-database-usage.md' ,
2025-01-17 04:31:39 +00:00
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_USAGE_DATABASE ,
)
],
contentType : ContentType :: JSON ,
))
2022-06-23 08:50:11 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ], true ), '`Date range.' , true )
2022-06-23 08:50:11 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> action ( function ( string $databaseId , string $range , Response $response , Database $dbForProject ) {
2023-10-25 07:39:59 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
if ( $database -> isEmpty ()) {
throw new Exception ( Exception :: DATABASE_NOT_FOUND );
}
2022-06-23 08:50:11 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
2025-05-26 05:42:11 +00:00
str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_COLLECTIONS ),
str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_DOCUMENTS ),
str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_STORAGE ),
str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_READS ),
str_replace ( '{databaseInternalId}' , $database -> getSequence (), METRIC_DATABASE_ID_OPERATIONS_WRITES )
2023-10-25 07:39:59 +00:00
];
2022-06-22 10:51:49 +00:00
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 15:20:12 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
2022-06-22 10:51:49 +00:00
}
2023-10-25 07:39:59 +00:00
}
});
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2022-06-23 08:50:11 +00:00
}
2022-06-22 10:51:49 +00:00
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2023-11-08 09:09:32 +00:00
'collectionsTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'documentsTotal' => $usage [ $metrics [ 1 ]][ 'total' ],
2024-07-18 04:58:02 +00:00
'storageTotal' => $usage [ $metrics [ 2 ]][ 'total' ],
2025-01-23 13:24:02 +00:00
'databaseReadsTotal' => $usage [ $metrics [ 3 ]][ 'total' ],
'databaseWritesTotal' => $usage [ $metrics [ 4 ]][ 'total' ],
2023-11-08 09:09:32 +00:00
'collections' => $usage [ $metrics [ 0 ]][ 'data' ],
'documents' => $usage [ $metrics [ 1 ]][ 'data' ],
2024-07-18 04:58:02 +00:00
'storage' => $usage [ $metrics [ 2 ]][ 'data' ],
2025-01-23 13:24:02 +00:00
'databaseReads' => $usage [ $metrics [ 3 ]][ 'data' ],
'databaseWrites' => $usage [ $metrics [ 4 ]][ 'data' ],
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_DATABASE );
2022-06-23 08:50:11 +00:00
});
2022-06-22 10:51:49 +00:00
App :: get ( '/v1/databases/:databaseId/collections/:collectionId/usage' )
2025-05-15 12:14:09 +00:00
-> alias ( '/v1/database/:collectionId/usage' )
2024-02-26 02:44:20 +00:00
-> desc ( 'Get collection usage stats' )
2023-11-06 21:28:45 +00:00
-> groups ([ 'api' , 'database' , 'usage' ])
2022-06-23 08:50:11 +00:00
-> label ( 'scope' , 'collections.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_DATABASES )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'databases' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'getCollectionUsage' ,
2025-01-17 07:44:25 +00:00
description : '/docs/references/databases/get-collection-usage.md' ,
2025-01-17 04:31:39 +00:00
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_USAGE_COLLECTION ,
)
],
contentType : ContentType :: JSON ,
))
2022-06-23 08:50:11 +00:00
-> param ( 'databaseId' , '' , new UID (), 'Database ID.' )
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ], true ), 'Date range.' , true )
2022-06-23 08:50:11 +00:00
-> param ( 'collectionId' , '' , new UID (), 'Collection ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> action ( function ( string $databaseId , string $range , string $collectionId , Response $response , Database $dbForProject ) {
2024-03-06 17:34:21 +00:00
$database = $dbForProject -> getDocument ( 'databases' , $databaseId );
2025-05-26 05:42:11 +00:00
$collectionDocument = $dbForProject -> getDocument ( 'database_' . $database -> getSequence (), $collectionId );
$collection = $dbForProject -> getCollection ( 'database_' . $database -> getSequence () . '_collection_' . $collectionDocument -> getSequence ());
2022-06-23 08:50:11 +00:00
if ( $collection -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: COLLECTION_NOT_FOUND );
2022-06-23 08:50:11 +00:00
}
2022-06-22 10:51:49 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
2025-05-26 05:42:11 +00:00
str_replace ([ '{databaseInternalId}' , '{collectionInternalId}' ], [ $database -> getSequence (), $collectionDocument -> getSequence ()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS ),
2023-10-25 07:39:59 +00:00
];
2022-06-23 08:50:11 +00:00
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 15:20:12 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2022-06-22 10:51:49 +00:00
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
2023-10-15 17:41:09 +00:00
}
2023-10-25 07:39:59 +00:00
}
});
2022-06-23 08:50:11 +00:00
2023-10-25 07:39:59 +00:00
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2022-06-22 10:51:49 +00:00
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2022-06-23 08:50:11 +00:00
}
2022-06-22 10:51:49 +00:00
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2025-05-07 14:49:02 +00:00
'documentsTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'documents' => $usage [ $metrics [ 0 ]][ 'data' ],
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_COLLECTION );
2022-06-23 08:50:11 +00:00
});