First commit

This commit is contained in:
Eldad Fux
2020-10-30 21:53:27 +02:00
parent 64ee540086
commit e81fb88736
38 changed files with 17554 additions and 19862 deletions
+60 -192
View File
@@ -2,15 +2,11 @@
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;
use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
use Utopia\Validator\JSON;
// use Utopia\Locale\Locale;
// use Utopia\Audit\Audit;
// use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
@@ -20,11 +16,12 @@ use Appwrite\Database\Validator\Collection;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Exception\Authorization as AuthorizationException;
use Appwrite\Database\Exception\Structure as StructureException;
use Appwrite\Utopia\Response;
App::post('/v1/database/collections')
->desc('Create Collection')
->groups(['api', 'database'])
->label('webhook', 'database.collections.create')
->label('event', 'database.collections.create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
@@ -34,10 +31,9 @@ App::post('/v1/database/collections')
->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('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'])
->action(function ($name, $read, $write, $rules, $response, $projectDB, $webhooks, $audits) {
->action(function ($name, $read, $write, $rules, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$parsedRules = [];
@@ -77,26 +73,15 @@ App::post('/v1/database/collections')
throw new Exception('Failed saving collection to DB', 500);
}
$data = $data->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.collections.create')
->setParam('resource', 'database/collection/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/collection/'.$data->getId())
->setParam('data', $data->getArrayCopy())
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
;
}, ['response', 'projectDB', 'webhooks', 'audits']);
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($data, Response::MODEL_COLLECTION);
}, ['response', 'projectDB', 'audits']);
App::get('/v1/database/collections')
->desc('List Collections')
@@ -126,7 +111,10 @@ App::get('/v1/database/collections')
],
]);
$response->json(['sum' => $projectDB->getSum(), 'collections' => $results]);
$response->dynamic(new Document([
'sum' => $projectDB->getSum(),
'collections' => $results
]), Response::MODEL_COLLECTION_LIST);
}, ['response', 'projectDB']);
App::get('/v1/database/collections/:collectionId')
@@ -148,79 +136,14 @@ App::get('/v1/database/collections/:collectionId')
throw new Exception('Collection not found', 404);
}
$response->json($collection->getArrayCopy());
$response->dynamic($collection, Response::MODEL_COLLECTION);
}, ['response', 'projectDB']);
// App::get('/v1/database/collections/:collectionId/logs')
// ->desc('Get Collection Logs')
// ->groups(['api', 'database'])
// ->label('scope', 'collections.read')
// ->label('sdk.platform', [APP_PLATFORM_SERVER])
// ->label('sdk.namespace', 'database')
// ->label('sdk.method', 'getCollectionLogs')
// ->label('sdk.description', '/docs/references/database/get-collection-logs.md')
// ->param('collectionId', '', new UID(), 'Collection unique ID.')
// ->action(
// function ($collectionId) use ($response, $register, $projectDB, $project) {
// $collection = $projectDB->getDocument($collectionId, false);
// if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
// throw new Exception('Collection not found', 404);
// }
// $adapter = new AuditAdapter($register->get('db'));
// $adapter->setNamespace('app_'.$project->getId());
// $audit = new Audit($adapter);
// $countries = Locale::getText('countries');
// $logs = $audit->getLogsByResource('database/collection/'.$collection->getId());
// $reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
// $output = [];
// foreach ($logs as $i => &$log) {
// $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
// $dd = new DeviceDetector($log['userAgent']);
// $dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
// $dd->parse();
// $output[$i] = [
// 'event' => $log['event'],
// 'ip' => $log['ip'],
// 'time' => strtotime($log['time']),
// 'OS' => $dd->getOs(),
// 'client' => $dd->getClient(),
// 'device' => $dd->getDevice(),
// 'brand' => $dd->getBrand(),
// 'model' => $dd->getModel(),
// 'geo' => [],
// ];
// try {
// $record = $reader->country($log['ip']);
// $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode);
// $output[$i]['geo']['country'] = $record->country->name;
// $output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
// } catch (\Exception $e) {
// $output[$i]['geo']['isoCode'] = '--';
// $output[$i]['geo']['country'] = Locale::getText('locale.country.unknown');
// }
// }
// $response->json($output);
// }
// );
App::put('/v1/database/collections/:collectionId')
->desc('Update Collection')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('webhook', 'database.collections.update')
->label('event', 'database.collections.update')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'updateCollection')
@@ -230,10 +153,9 @@ App::put('/v1/database/collections/:collectionId')
->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('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'])
->action(function ($collectionId, $name, $read, $write, $rules, $response, $projectDB, $webhooks, $audits) {
->action(function ($collectionId, $name, $read, $write, $rules, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$collection = $projectDB->getDocument($collectionId, false);
@@ -277,26 +199,20 @@ App::put('/v1/database/collections/:collectionId')
throw new Exception('Failed saving collection to DB', 500);
}
$data = $collection->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.collections.update')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/collections/'.$collection->getId())
->setParam('data', $collection->getArrayCopy())
;
$response->json($collection->getArrayCopy());
}, ['response', 'projectDB', 'webhooks', 'audits']);
$response->dynamic($collection, Response::MODEL_COLLECTION);
}, ['response', 'projectDB', 'audits']);
App::delete('/v1/database/collections/:collectionId')
->desc('Delete Collection')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('webhook', 'database.collections.delete')
->label('event', 'database.collections.delete')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteCollection')
@@ -318,16 +234,14 @@ App::delete('/v1/database/collections/:collectionId')
throw new Exception('Failed to remove collection from DB', 500);
}
$data = $collection->getArrayCopy();
$webhooks
->setParam('payload', $data)
->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION))
;
$audits
->setParam('event', 'database.collections.delete')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/collections/'.$collection->getId())
->setParam('data', $collection->getArrayCopy())
;
$response->noContent();
@@ -336,7 +250,7 @@ App::delete('/v1/database/collections/:collectionId')
App::post('/v1/database/collections/:collectionId/documents')
->desc('Create Document')
->groups(['api', 'database'])
->label('webhook', 'database.documents.create')
->label('event', 'database.documents.create')
->label('scope', 'documents.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@@ -349,10 +263,9 @@ App::post('/v1/database/collections/:collectionId/documents')
->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)
->action(function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType, $response, $projectDB, $webhooks, $audits) {
->action(function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -387,11 +300,11 @@ App::post('/v1/database/collections/:collectionId/documents')
}
/*
* 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)
*/
* 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)
*/
$new = new Document($data);
@@ -436,26 +349,18 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('Failed saving document to DB'.$exception->getMessage(), 500);
}
$data = $data->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.documents.create')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
->setParam('data', $data->getArrayCopy())
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
;
}, ['response', 'projectDB', 'webhooks', 'audits']);
$response->dynamic($data, Response::MODEL_ANY);
}, ['response', 'projectDB', 'audits']);
App::get('/v1/database/collections/:collectionId/documents')
->desc('List Documents')
@@ -495,27 +400,24 @@ App::get('/v1/database/collections/:collectionId/documents')
]),
]);
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)
;
}
// 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)
// ;
// }
$collection
->setAttribute('sum', $projectDB->getSum())
->setAttribute('documents', $list)
;
/*
* View
*/
$response->json($collection->getArrayCopy(/*['$id', '$collection', 'name', 'documents']*/[], ['rules']));
$response->dynamic($collection, Response::MODEL_DOCUMENT_LIST);
}, ['response', 'projectDB']);
App::get('/v1/database/collections/:collectionId/documents/:documentId')
@@ -540,36 +442,13 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('No document found', 404);
}
$output = $document->getArrayCopy();
$paths = \explode('/', $request->getParam('q', ''));
$paths = \array_slice($paths, 7, \count($paths));
if (\count($paths) > 0) {
if (\count($paths) % 2 == 1) {
$output = $document->getAttribute(\implode('.', $paths));
} else {
$id = (int) \array_pop($paths);
$output = $document->search('$id', $id, $document->getAttribute(\implode('.', $paths)));
}
$output = ($output instanceof Document) ? $output->getArrayCopy() : $output;
if (!\is_array($output)) {
throw new Exception('No document found', 404);
}
}
/*
* View
*/
$response->json($output);
$response->dynamic($document, Response::MODEL_ANY);
}, ['request', 'response', 'projectDB']);
App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Update Document')
->groups(['api', 'database'])
->label('webhook', 'database.documents.update')
->label('event', 'database.documents.update')
->label('scope', 'documents.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@@ -580,10 +459,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->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.')
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $projectDB, $webhooks, $audits) {
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$collection = $projectDB->getDocument($collectionId, false);
@@ -592,7 +470,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (!\is_array($data)) {
throw new Exception('Data param should be a valid JSON', 400);
throw new Exception('Data param should be a valid JSON object', 400);
}
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
@@ -621,6 +499,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
if (empty($data)) {
throw new Exception('Missing payload', 400);
}
try {
$data = $projectDB->updateDocument($data);
} catch (AuthorizationException $exception) {
@@ -631,29 +510,20 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('Failed saving document to DB', 500);
}
$data = $data->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.documents.update')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/document/'.$data->getId())
->setParam('data', $data->getArrayCopy())
;
/*
* View
*/
$response->json($data);
}, ['response', 'projectDB', 'webhooks', 'audits']);
$response->dynamic($data, Response::MODEL_ANY);
}, ['response', 'projectDB', 'audits']);
App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Delete Document')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('webhook', 'database.documents.delete')
->label('event', 'database.documents.delete')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteDocument')
@@ -687,16 +557,14 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('Failed to remove document from DB', 500);
}
$data = $document->getArrayCopy();
$webhooks
->setParam('payload', $data)
->setParam('payload', $response->output($document, Response::MODEL_ANY))
;
$audits
->setParam('event', 'database.documents.delete')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data) // Audit document in case of malicious or disastrous action
->setParam('resource', 'database/document/'.$document->getId())
->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action
;
$response->noContent();