2019-05-09 18:54:39 +12:00
< ? php
use Utopia\App ;
use Utopia\Exception ;
use Utopia\Validator\Range ;
use Utopia\Validator\WhiteList ;
use Utopia\Validator\Text ;
use Utopia\Validator\ArrayList ;
2020-06-19 08:13:03 +12:00
use Utopia\Validator\JSON ;
2020-03-25 06:56:32 +13:00
use Appwrite\Database\Database ;
use Appwrite\Database\Document ;
use Appwrite\Database\Validator\UID ;
use Appwrite\Database\Validator\Key ;
use Appwrite\Database\Validator\Structure ;
use Appwrite\Database\Validator\Collection ;
use Appwrite\Database\Validator\Authorization ;
use Appwrite\Database\Exception\Authorization as AuthorizationException ;
use Appwrite\Database\Exception\Structure as StructureException ;
2020-10-31 08:53:27 +13:00
use Appwrite\Utopia\Response ;
2020-06-21 23:56:42 +12:00
2020-06-29 05:31:21 +12:00
App :: post ( '/v1/database/collections' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Create Collection' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'database.collections.create' )
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'collections.write' )
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-02-08 19:06:40 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.method' , 'createCollection' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/create-collection.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_COLLECTION )
2020-09-11 02:40:14 +12:00
-> param ( 'name' , '' , new Text ( 128 ), 'Collection name. Max length: 128 chars.' )
-> param ( 'read' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
-> param ( 'write' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
2020-06-30 09:43:34 +12:00
-> param ( 'rules' , [], function ( $projectDB ) { return new ArrayList ( new Collection ( $projectDB , [ Database :: SYSTEM_COLLECTION_RULES ], [ '$collection' => Database :: SYSTEM_COLLECTION_RULES , '$permissions' => [ 'read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.' , false , [ 'projectDB' ])
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'audits' )
2020-10-31 08:53:27 +13:00
-> action ( function ( $name , $read , $write , $rules , $response , $projectDB , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-10-01 17:57:41 +13:00
2020-06-30 09:43:34 +12:00
$parsedRules = [];
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
foreach ( $rules as & $rule ) {
$parsedRules [] = \array_merge ([
'$collection' => Database :: SYSTEM_COLLECTION_RULES ,
'$permissions' => [
'read' => $read ,
'write' => $write ,
],
], $rule );
}
2020-03-09 04:38:51 +13:00
2020-06-30 09:43:34 +12:00
try {
$data = $projectDB -> createDocument ([
'$collection' => Database :: SYSTEM_COLLECTION_COLLECTIONS ,
'name' => $name ,
'dateCreated' => \time (),
'dateUpdated' => \time (),
'structure' => true ,
'$permissions' => [
'read' => $read ,
'write' => $write ,
],
'rules' => $parsedRules ,
]);
} catch ( AuthorizationException $exception ) {
2020-08-30 16:49:24 +12:00
throw new Exception ( 'Unauthorized permissions' , 401 );
2020-06-30 09:43:34 +12:00
} catch ( StructureException $exception ) {
throw new Exception ( 'Bad structure. ' . $exception -> getMessage (), 400 );
} catch ( \Exception $exception ) {
throw new Exception ( 'Failed saving document to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( false === $data ) {
throw new Exception ( 'Failed saving collection to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'database.collections.create' )
2020-10-31 08:53:27 +13:00
-> setParam ( 'resource' , 'database/collection/' . $data -> getId ())
-> setParam ( 'data' , $data -> getArrayCopy ())
2020-06-30 09:43:34 +12:00
;
2020-10-31 21:42:41 +13:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $data , Response :: MODEL_COLLECTION )
;
2020-12-27 04:05:04 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/database/collections' )
2020-02-01 11:34:07 +13:00
-> desc ( 'List Collections' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'collections.read' )
-> label ( 'sdk.namespace' , 'database' )
2020-02-08 19:06:40 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_SERVER ])
2020-02-01 11:34:07 +13:00
-> label ( 'sdk.method' , 'listCollections' )
-> label ( 'sdk.description' , '/docs/references/database/list-collections.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_COLLECTION_LIST )
2020-09-11 02:40:14 +12:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
-> param ( 'limit' , 25 , new Range ( 0 , 100 ), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.' , true )
-> param ( 'offset' , 0 , new Range ( 0 , 40000 ), 'Results offset. The default value is 0. Use this param to manage pagination.' , true )
-> param ( 'orderType' , 'ASC' , new WhiteList ([ 'ASC' , 'DESC' ], true ), 'Order result by ASC or DESC order.' , true )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-06-30 09:43:34 +12:00
-> action ( function ( $search , $limit , $offset , $orderType , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB -> getCollection ([
'limit' => $limit ,
'offset' => $offset ,
2020-12-30 08:30:49 +13:00
'orderType' => $orderType ,
2020-06-30 09:43:34 +12:00
'search' => $search ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_COLLECTIONS ,
],
]);
2020-10-31 08:53:27 +13:00
$response -> dynamic ( new Document ([
'sum' => $projectDB -> getSum (),
'collections' => $results
]), Response :: MODEL_COLLECTION_LIST );
2020-12-27 04:05:04 +13:00
});
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/database/collections/:collectionId' )
2020-02-01 11:34:07 +13:00
-> desc ( 'Get Collection' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'collections.read' )
-> label ( 'sdk.namespace' , 'database' )
2020-02-08 19:06:40 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_SERVER ])
2020-02-01 11:34:07 +13:00
-> label ( 'sdk.method' , 'getCollection' )
-> label ( 'sdk.description' , '/docs/references/database/get-collection.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_COLLECTION )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , '' , new UID (), 'Collection unique ID.' )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-06-30 09:43:34 +12:00
-> action ( function ( $collectionId , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
$collection = $projectDB -> getDocument ( $collectionId , false );
if ( empty ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
2020-02-01 11:34:07 +13:00
}
2020-06-30 09:43:34 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $collection , Response :: MODEL_COLLECTION );
2020-12-27 04:05:04 +13:00
});
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: put ( '/v1/database/collections/:collectionId' )
2019-06-09 23:44:58 +12:00
-> desc ( 'Update Collection' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'collections.write' )
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'database.collections.update' )
2019-06-09 23:44:58 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-02-08 19:06:40 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_SERVER ])
2019-06-09 20:51:10 +12:00
-> label ( 'sdk.method' , 'updateCollection' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/update-collection.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_COLLECTION )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , '' , new UID (), 'Collection unique ID.' )
-> param ( 'name' , null , new Text ( 128 ), 'Collection name. Max length: 128 chars.' )
-> param ( 'read' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions(/docs/permissions) and get a full list of available permissions.' )
-> param ( 'write' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
2020-07-01 03:46:42 +12:00
-> param ( 'rules' , [], function ( $projectDB ) { return new ArrayList ( new Collection ( $projectDB , [ Database :: SYSTEM_COLLECTION_RULES ], [ '$collection' => Database :: SYSTEM_COLLECTION_RULES , '$permissions' => [ 'read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.' , true , [ 'projectDB' ])
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'audits' )
2020-10-31 08:53:27 +13:00
-> action ( function ( $collectionId , $name , $read , $write , $rules , $response , $projectDB , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$collection = $projectDB -> getDocument ( $collectionId , false );
2019-10-01 17:57:41 +13:00
2020-06-30 09:43:34 +12:00
if ( empty ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
}
2019-10-01 17:57:41 +13:00
2020-06-30 09:43:34 +12:00
$parsedRules = [];
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
foreach ( $rules as & $rule ) {
$parsedRules [] = \array_merge ([
'$collection' => Database :: SYSTEM_COLLECTION_RULES ,
'$permissions' => [
'read' => $read ,
'write' => $write ,
],
], $rule );
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
try {
$collection = $projectDB -> updateDocument ( \array_merge ( $collection -> getArrayCopy (), [
'name' => $name ,
'structure' => true ,
'dateUpdated' => \time (),
'$permissions' => [
'read' => $read ,
'write' => $write ,
],
'rules' => $parsedRules ,
]));
} catch ( AuthorizationException $exception ) {
2020-08-30 16:49:24 +12:00
throw new Exception ( 'Unauthorized permissions' , 401 );
2020-06-30 09:43:34 +12:00
} catch ( StructureException $exception ) {
throw new Exception ( 'Bad structure. ' . $exception -> getMessage (), 400 );
} catch ( \Exception $exception ) {
throw new Exception ( 'Failed saving document to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( false === $collection ) {
throw new Exception ( 'Failed saving collection to DB' , 500 );
}
2020-01-05 04:45:28 +13:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'database.collections.update' )
2020-10-31 08:53:27 +13:00
-> setParam ( 'resource' , 'database/collections/' . $collection -> getId ())
-> setParam ( 'data' , $collection -> getArrayCopy ())
2020-06-30 09:43:34 +12:00
;
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $collection , Response :: MODEL_COLLECTION );
2020-12-27 04:05:04 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: delete ( '/v1/database/collections/:collectionId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Delete Collection' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'collections.write' )
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'database.collections.delete' )
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-02-08 19:06:40 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.method' , 'deleteCollection' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/delete-collection.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , '' , new UID (), 'Collection unique ID.' )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'events' )
-> inject ( 'audits' )
-> inject ( 'deletes' )
2020-12-07 11:14:57 +13:00
-> action ( function ( $collectionId , $response , $projectDB , $events , $audits , $deletes ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-12-07 11:14:57 +13:00
/** @var Appwrite\Event\Event $events */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$collection = $projectDB -> getDocument ( $collectionId , false );
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( empty ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
}
2020-01-05 04:45:28 +13:00
2020-06-30 09:43:34 +12:00
if ( ! $projectDB -> deleteDocument ( $collectionId )) {
throw new Exception ( 'Failed to remove collection from DB' , 500 );
}
2020-12-05 09:47:02 +13:00
$deletes
2020-12-19 03:05:15 +13:00
-> setParam ( 'type' , DELETE_TYPE_DOCUMENT )
2020-12-05 09:47:02 +13:00
-> setParam ( 'document' , $collection )
;
2020-12-07 11:14:57 +13:00
$events
2020-10-31 08:53:27 +13:00
-> setParam ( 'payload' , $response -> output ( $collection , Response :: MODEL_COLLECTION ))
2020-06-30 09:43:34 +12:00
;
2019-05-09 18:54:39 +12:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'database.collections.delete' )
2020-10-31 08:53:27 +13:00
-> setParam ( 'resource' , 'database/collections/' . $collection -> getId ())
-> setParam ( 'data' , $collection -> getArrayCopy ())
2020-06-30 09:43:34 +12:00
;
2021-03-19 09:32:16 +13:00
$response -> dynamic ( new Document (), Response :: MODEL_NONE );
2020-12-27 04:05:04 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: post ( '/v1/database/collections/:collectionId/documents' )
2020-02-01 11:34:07 +13:00
-> desc ( 'Create Document' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'database.documents.create' )
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'documents.write' )
-> label ( 'sdk.namespace' , 'database' )
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
-> label ( 'sdk.method' , 'createDocument' )
-> label ( 'sdk.description' , '/docs/references/database/create-document.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_ANY )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , null , new UID (), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).' )
-> param ( 'data' , [], new JSON (), 'Document data as JSON object.' )
-> param ( 'read' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
-> param ( 'write' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
-> param ( 'parentDocument' , '' , new UID (), 'Parent document unique ID. Use when you want your new document to be a child of a parent document.' , true )
-> param ( 'parentProperty' , '' , new Key (), 'Parent document property name. Use when you want your new document to be a child of a parent document.' , true )
-> param ( 'parentPropertyType' , Document :: SET_TYPE_ASSIGN , new WhiteList ([ Document :: SET_TYPE_ASSIGN , Document :: SET_TYPE_APPEND , Document :: SET_TYPE_PREPEND ], true ), 'Parent document property connection type. You can set this value to **assign**, **append** or **prepend**, default value is assign. Use when you want your new document to be a child of a parent document.' , true )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'audits' )
2020-10-31 08:53:27 +13:00
-> action ( function ( $collectionId , $data , $read , $write , $parentDocument , $parentProperty , $parentPropertyType , $response , $projectDB , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2020-06-30 09:43:34 +12:00
$data = ( \is_string ( $data )) ? \json_decode ( $data , true ) : $data ; // Cast to JSON array
if ( empty ( $data )) {
throw new Exception ( 'Missing payload' , 400 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( isset ( $data [ '$id' ])) {
throw new Exception ( '$id is not allowed for creating new documents, try update instead' , 400 );
}
$collection = $projectDB -> getDocument ( $collectionId , false );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( \is_null ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$data [ '$collection' ] = $collectionId ; // Adding this param to make API easier for developers
$data [ '$permissions' ] = [
'read' => $read ,
'write' => $write ,
];
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
// Read parent document + validate not 404 + validate read / write permission like patch method
// Add payload to parent document property
if (( ! empty ( $parentDocument )) && ( ! empty ( $parentProperty ))) {
$parentDocument = $projectDB -> getDocument ( $parentDocument , false );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( empty ( $parentDocument -> getArrayCopy ())) { // Check empty
throw new Exception ( 'No parent document found' , 404 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
/*
2020-10-31 08:53:27 +13:00
* 1. Check child has valid structure ,
* 2. Check user have write permission for parent document
* 3. Assign parent data ( including child ) to $data
* 4. Validate the combined result has valid structure ( inside $projectDB -> createDocument method )
*/
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$new = new Document ( $data );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$structure = new Structure ( $projectDB );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( ! $structure -> isValid ( $new )) {
throw new Exception ( 'Invalid data structure: ' . $structure -> getDescription (), 400 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$authorization = new Authorization ( $parentDocument , 'write' );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
if ( ! $authorization -> isValid ( $new -> getPermissions ())) {
2020-08-30 16:49:24 +12:00
throw new Exception ( 'Unauthorized permissions' , 401 );
2020-06-30 09:43:34 +12:00
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$parentDocument
-> setAttribute ( $parentProperty , $data , $parentPropertyType );
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$data = $parentDocument -> getArrayCopy ();
2020-08-27 03:54:07 +12:00
$collection = $projectDB -> getDocument ( $parentDocument -> getCollection (), false );
2020-06-30 09:43:34 +12:00
}
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
/**
* Set default collection values
*/
foreach ( $collection -> getAttribute ( 'rules' ) as $key => $rule ) {
2020-10-15 10:34:57 +13:00
$key = $rule [ 'key' ] ? ? '' ;
$default = $rule [ 'default' ] ? ? null ;
2020-04-19 01:57:28 +12:00
2020-06-30 09:43:34 +12:00
if ( ! isset ( $data [ $key ])) {
$data [ $key ] = $default ;
2020-04-19 01:57:28 +12:00
}
2020-06-30 09:43:34 +12:00
}
2020-04-19 01:57:28 +12:00
2020-06-30 09:43:34 +12:00
try {
$data = $projectDB -> createDocument ( $data );
} catch ( AuthorizationException $exception ) {
2020-08-30 16:49:24 +12:00
throw new Exception ( 'Unauthorized permissions' , 401 );
2020-06-30 09:43:34 +12:00
} catch ( StructureException $exception ) {
throw new Exception ( 'Bad structure. ' . $exception -> getMessage (), 400 );
} catch ( \Exception $exception ) {
throw new Exception ( 'Failed saving document to DB' . $exception -> getMessage (), 500 );
}
2020-02-01 11:34:07 +13:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'database.documents.create' )
-> setParam ( 'resource' , 'database/document/' . $data [ '$id' ])
2020-10-31 08:53:27 +13:00
-> setParam ( 'data' , $data -> getArrayCopy ())
2020-06-30 09:43:34 +12:00
;
2020-02-01 11:34:07 +13:00
2020-06-30 09:43:34 +12:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
2020-10-31 21:42:41 +13:00
-> dynamic ( $data , Response :: MODEL_ANY )
2020-06-30 09:43:34 +12:00
;
2020-12-27 04:05:04 +13:00
});
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/database/collections/:collectionId/documents' )
2019-05-09 18:54:39 +12:00
-> desc ( 'List Documents' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'documents.read' )
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.method' , 'listDocuments' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/list-documents.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_DOCUMENT_LIST )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , null , new UID (), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).' )
-> param ( 'filters' , [], new ArrayList ( new Text ( 128 )), 'Array of filter strings. Each filter is constructed from a key name, comparison operator (=, !=, >, <, <=, >=) and a value. You can also use a dot (.) separator in attribute names to filter by child document attributes. Examples: \'name=John Doe\' or \'category.$id>=5bed2d152c362\'.' , true )
2020-11-06 05:02:25 +13:00
-> param ( 'limit' , 25 , new Range ( 0 , 100 ), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.' , true )
-> param ( 'offset' , 0 , new Range ( 0 , 900000000 ), 'Offset value. The default value is 0. Use this param to manage pagination.' , true )
2020-12-13 12:22:55 +13:00
-> param ( 'orderField' , '' , new Text ( 128 ), 'Document field that results will be sorted by.' , true )
2020-09-11 02:40:14 +12:00
-> param ( 'orderType' , 'ASC' , new WhiteList ([ 'DESC' , 'ASC' ], true ), 'Order direction. Possible values are DESC for descending order, or ASC for ascending order.' , true )
-> param ( 'orderCast' , 'string' , new WhiteList ([ 'int' , 'string' , 'date' , 'time' , 'datetime' ], true ), 'Order field type casting. Possible values are int, string, date, time or datetime. The database will attempt to cast the order field to the value you pass here. The default value is a string.' , true )
-> param ( 'search' , '' , new Text ( 256 ), 'Search query. Enter any free text search. The database will try to find a match against all document attributes and children. Max length: 256 chars.' , true )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
2020-07-09 02:28:01 +12:00
-> action ( function ( $collectionId , $filters , $limit , $offset , $orderField , $orderType , $orderCast , $search , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$collection = $projectDB -> getDocument ( $collectionId , false );
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( \is_null ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
}
2020-06-22 00:12:13 +12:00
2020-06-30 09:43:34 +12:00
$list = $projectDB -> getCollection ([
'limit' => $limit ,
'offset' => $offset ,
'orderField' => $orderField ,
'orderType' => $orderType ,
'orderCast' => $orderCast ,
'search' => $search ,
'filters' => \array_merge ( $filters , [
'$collection=' . $collectionId ,
]),
]);
2020-10-31 08:53:27 +13:00
// if (App::isDevelopment()) {
// $collection
// ->setAttribute('debug', $projectDB->getDebug())
// ->setAttribute('limit', $limit)
// ->setAttribute('offset', $offset)
// ->setAttribute('orderField', $orderField)
// ->setAttribute('orderType', $orderType)
// ->setAttribute('orderCast', $orderCast)
// ->setAttribute('filters', $filters)
// ;
// }
2020-06-30 09:43:34 +12:00
$collection
-> setAttribute ( 'sum' , $projectDB -> getSum ())
-> setAttribute ( 'documents' , $list )
;
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $collection , Response :: MODEL_DOCUMENT_LIST );
2020-12-27 04:05:04 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/database/collections/:collectionId/documents/:documentId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Get Document' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'documents.read' )
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.method' , 'getDocument' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/get-document.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_ANY )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , null , new UID (), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).' )
-> param ( 'documentId' , null , new UID (), 'Document unique ID.' )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> action ( function ( $collectionId , $documentId , $response , $projectDB ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$document = $projectDB -> getDocument ( $documentId , false );
$collection = $projectDB -> getDocument ( $collectionId , false );
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( empty ( $document -> getArrayCopy ()) || $document -> getCollection () != $collection -> getId ()) { // Check empty
throw new Exception ( 'No document found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $document , Response :: MODEL_ANY );
2020-12-27 04:05:04 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: patch ( '/v1/database/collections/:collectionId/documents/:documentId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Update Document' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'database.documents.update' )
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'documents.write' )
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.method' , 'updateDocument' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/update-document.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_ANY )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , null , new UID (), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).' )
-> param ( 'documentId' , null , new UID (), 'Document unique ID.' )
-> param ( 'data' , [], new JSON (), 'Document data as JSON object.' )
-> param ( 'read' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
-> param ( 'write' , [], new ArrayList ( new Text ( 64 )), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.' )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'audits' )
2020-10-31 08:53:27 +13:00
-> action ( function ( $collectionId , $documentId , $data , $read , $write , $response , $projectDB , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$collection = $projectDB -> getDocument ( $collectionId , false );
$document = $projectDB -> getDocument ( $documentId , false );
2019-09-17 07:03:24 +12:00
2020-06-30 09:43:34 +12:00
$data = ( \is_string ( $data )) ? \json_decode ( $data , true ) : $data ; // Cast to JSON array
2019-09-17 07:03:24 +12:00
2020-06-30 09:43:34 +12:00
if ( ! \is_array ( $data )) {
2020-10-31 08:53:27 +13:00
throw new Exception ( 'Data param should be a valid JSON object' , 400 );
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( \is_null ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( empty ( $document -> getArrayCopy ()) || $document -> getCollection () != $collectionId ) { // Check empty
throw new Exception ( 'No document found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
//TODO check merge read write permissions
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( ! empty ( $read )) { // Overwrite permissions only when passed
$data [ '$permissions' ][ 'read' ] = $read ;
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( ! empty ( $write )) { // Overwrite permissions only when passed
2020-07-12 06:38:51 +12:00
$data [ '$permissions' ][ 'write' ] = $write ;
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$data = \array_merge ( $document -> getArrayCopy (), $data );
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$data [ '$collection' ] = $collection -> getId (); // Make sure user don't switch collectionID
$data [ '$id' ] = $document -> getId (); // Make sure user don't switch document unique ID
if ( empty ( $data )) {
throw new Exception ( 'Missing payload' , 400 );
}
2020-10-31 08:53:27 +13:00
2020-06-30 09:43:34 +12:00
try {
$data = $projectDB -> updateDocument ( $data );
} catch ( AuthorizationException $exception ) {
2020-08-30 16:49:24 +12:00
throw new Exception ( 'Unauthorized permissions' , 401 );
2020-06-30 09:43:34 +12:00
} catch ( StructureException $exception ) {
throw new Exception ( 'Bad structure. ' . $exception -> getMessage (), 400 );
} catch ( \Exception $exception ) {
throw new Exception ( 'Failed saving document to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'database.documents.update' )
2020-10-31 08:53:27 +13:00
-> setParam ( 'resource' , 'database/document/' . $data -> getId ())
-> setParam ( 'data' , $data -> getArrayCopy ())
2020-06-30 09:43:34 +12:00
;
2019-05-09 18:54:39 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $data , Response :: MODEL_ANY );
2020-12-27 04:05:04 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: delete ( '/v1/database/collections/:collectionId/documents/:documentId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Delete Document' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'database' ])
2019-06-09 01:13:19 +12:00
-> label ( 'scope' , 'documents.write' )
2020-10-31 08:53:27 +13:00
-> label ( 'event' , 'database.documents.delete' )
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'database' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.method' , 'deleteDocument' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/database/delete-document.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2020-09-11 02:40:14 +12:00
-> param ( 'collectionId' , null , new UID (), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).' )
-> param ( 'documentId' , null , new UID (), 'Document unique ID.' )
2020-12-27 04:05:04 +13:00
-> inject ( 'response' )
-> inject ( 'projectDB' )
-> inject ( 'events' )
-> inject ( 'audits' )
2020-12-07 11:14:57 +13:00
-> action ( function ( $collectionId , $documentId , $response , $projectDB , $events , $audits ) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2020-06-30 09:43:34 +12:00
/** @var Appwrite\Database\Database $projectDB */
2020-12-07 11:14:57 +13:00
/** @var Appwrite\Event\Event $events */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$collection = $projectDB -> getDocument ( $collectionId , false );
$document = $projectDB -> getDocument ( $documentId , false );
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( empty ( $document -> getArrayCopy ()) || $document -> getCollection () != $collectionId ) { // Check empty
throw new Exception ( 'No document found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ( \is_null ( $collection -> getId ()) || Database :: SYSTEM_COLLECTION_COLLECTIONS != $collection -> getCollection ()) {
throw new Exception ( 'Collection not found' , 404 );
}
try {
$projectDB -> deleteDocument ( $documentId );
} catch ( AuthorizationException $exception ) {
2020-08-30 16:49:24 +12:00
throw new Exception ( 'Unauthorized permissions' , 401 );
2020-06-30 09:43:34 +12:00
} catch ( StructureException $exception ) {
throw new Exception ( 'Bad structure. ' . $exception -> getMessage (), 400 );
} catch ( \Exception $exception ) {
throw new Exception ( 'Failed to remove document from DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-12-07 11:14:57 +13:00
$events
2020-10-31 08:53:27 +13:00
-> setParam ( 'payload' , $response -> output ( $document , Response :: MODEL_ANY ))
2020-06-30 09:43:34 +12:00
;
2020-10-31 08:53:27 +13:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 09:43:34 +12:00
-> setParam ( 'event' , 'database.documents.delete' )
2020-10-31 08:53:27 +13:00
-> setParam ( 'resource' , 'database/document/' . $document -> getId ())
-> setParam ( 'data' , $document -> getArrayCopy ()) // Audit document in case of malicious or disastrous action
2020-06-30 09:43:34 +12:00
;
2019-05-09 18:54:39 +12:00
2021-03-19 09:32:16 +13:00
$response -> dynamic ( new Document (), Response :: MODEL_NONE );
2020-12-27 04:05:04 +13:00
});