From cfe49b68927fb6b2be41e90dff2362d1071f4a05 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 13:54:45 +0530 Subject: [PATCH 01/35] feat(usage): refactored functions usage endpoint --- app/controllers/api/database.php | 261 ++++++++++++++++++++---------- app/controllers/api/functions.php | 95 +++++++++++ app/controllers/api/projects.php | 2 +- 3 files changed, 273 insertions(+), 85 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index db8a97a9ec..86f4029cdb 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1,5 +1,6 @@ isValid($default)) { throw new Exception('Length of default attribute exceeds attribute size', 400); } - } + } if (!\is_null($format)) { $name = \json_decode($format, true)['name']; @@ -110,16 +111,14 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database $database ->setParam('type', DATABASE_TYPE_CREATE_ATTRIBUTE) - ->setParam('document', $attribute) - ; + ->setParam('document', $attribute); $usage->setParam('database.collections.update', 1); $audits ->setParam('event', 'database.attributes.create') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $attribute) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $attribute); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); @@ -167,9 +166,8 @@ App::post('/v1/database/collections') $audits ->setParam('event', 'database.collections.create') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $collection->getArrayCopy()) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $collection->getArrayCopy()); $usage->setParam('database.collections.create', 1); @@ -250,6 +248,112 @@ App::get('/v1/database/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); +App::get('/v1/database/usage') + ->desc('Get Database Usage') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'database') + ->label('sdk.method', 'getUsage') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForConsole') + ->inject('dbForInternal') + ->inject('register') + ->action(function ($range, $response, $dbForConsole, $dbForInternal, $register) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForConsole */ + /** @var Utopia\Database\Database $dbForInternal */ + /** @var Utopia\Registry\Registry $register */ + + $stats = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + + $metrics = [ + 'database.collections.create', + 'database.collections.read', + 'database.collections.update', + 'database.collections.delete', + 'database.documents.create', + 'database.documents.read', + 'database.documents.update', + 'database.documents.delete' + ]; + + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + $stats[$metric] = array_reverse($stats[$metric]); + } + } + + $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.documents.count'])], 0, ['time'], [Database::ORDER_DESC]); + $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; + + $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['n + '])], 0, ['time'], [Database::ORDER_DESC]); + $collectionsTotal = $collectionsCount ? $collectionsCount->getAttribute('value', 0) : 0; + + Authorization::reset(); + + $response->json([ + 'range' => $range, + 'stats' => $stats, + 'requests' => [ + 'data' => $stats['requests'] ?? [], + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $stats['requests'] ?? [])), + ], + 'documents' => [ + 'data' => [], + 'total' => $documentsTotal, + ], + 'collections' => [ + 'data' => [], + 'total' => $collectionsTotal, + ] + ]); + }); + + +// :collectionId/usage +// 'reads', +// 'writes', +// 'updates', +// 'delete' + App::get('/v1/database/collections/:collectionId/logs') ->desc('List Collection Logs') ->groups(['api', 'database']) @@ -283,7 +387,7 @@ App::get('/v1/database/collections/:collectionId/logs') $audit = new Audit($dbForInternal); - $logs = $audit->getLogsByResource('database/collection/'.$collection->getId()); + $logs = $audit->getLogsByResource('database/collection/' . $collection->getId()); $output = []; @@ -335,8 +439,8 @@ App::get('/v1/database/collections/:collectionId/logs') $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')); + $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'); @@ -390,16 +494,15 @@ App::put('/v1/database/collections/:collectionId') } catch (AuthorizationException $exception) { throw new Exception('Unauthorized permissions', 401); } catch (StructureException $exception) { - throw new Exception('Bad structure. '.$exception->getMessage(), 400); - } + throw new Exception('Bad structure. ' . $exception->getMessage(), 400); + } $usage->setParam('database.collections.update', 1); $audits ->setParam('event', 'database.collections.update') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $collection->getArrayCopy()) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $collection->getArrayCopy()); $response->dynamic($collection, Response::MODEL_COLLECTION); }); @@ -440,14 +543,12 @@ App::delete('/v1/database/collections/:collectionId') $usage->setParam('database.collections.delete', 1); $events - ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION)) - ; + ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION)); $audits ->setParam('event', 'database.collections.delete') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $collection->getArrayCopy()) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $collection->getArrayCopy()); $response->noContent(); }); @@ -530,7 +631,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name'=>'email']), + 'format' => \json_encode(['name' => 'email']), ]), $response, $dbForExternal, $database, $audits, $usage); }); @@ -571,7 +672,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name'=>'ip']), + 'format' => \json_encode(['name' => 'ip']), ]), $response, $dbForExternal, $database, $audits, $usage); }); @@ -613,7 +714,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name'=>'url']), + 'format' => \json_encode(['name' => 'url']), ]), $response, $dbForExternal, $database, $audits, $usage); }); @@ -648,7 +749,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ - + return $attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, @@ -658,7 +759,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') 'default' => $default, 'array' => $array, 'format' => \json_encode([ - 'name'=>'int-range', + 'name' => 'int-range', 'min' => $min, 'max' => $max, ]), @@ -705,7 +806,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float') 'default' => $default, 'array' => $array, 'format' => \json_encode([ - 'name'=>'float-range', + 'name' => 'float-range', 'min' => $min, 'max' => $max, ]), @@ -835,7 +936,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') ])]); $usage->setParam('database.collections.read', 1); - + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); }); @@ -890,20 +991,17 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') $database ->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setParam('document', $attribute) - ; + ->setParam('document', $attribute); $usage->setParam('database.collections.update', 1); $events - ->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE)) - ; + ->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE)); $audits ->setParam('event', 'database.attributes.delete') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $attribute->getArrayCopy()) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $attribute->getArrayCopy()); $response->noContent(); }); @@ -984,20 +1082,17 @@ App::post('/v1/database/collections/:collectionId/indexes') $database ->setParam('type', DATABASE_TYPE_CREATE_INDEX) - ->setParam('document', $index) - ; + ->setParam('document', $index); $usage->setParam('database.collections.update', 1); $audits ->setParam('event', 'database.indexes.create') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $index->getArrayCopy()) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $index->getArrayCopy()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($index, Response::MODEL_INDEX); - }); App::get('/v1/database/collections/:collectionId/indexes') @@ -1083,7 +1178,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId') ])]); $usage->setParam('database.collections.read', 1); - + $response->dynamic($index, Response::MODEL_INDEX); }); @@ -1124,7 +1219,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') $indexes = $collection->getAttribute('indexes'); // find attribute in collection - $index= null; + $index = null; foreach ($indexes as $i) { if ($i->getId() === $indexId) { $index = $i->setAttribute('$collection', $collectionId); // set the collectionId @@ -1138,20 +1233,17 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') $database ->setParam('type', DATABASE_TYPE_DELETE_INDEX) - ->setParam('document', $index) - ; + ->setParam('document', $index); $usage->setParam('database.collections.update', 1); $events - ->setParam('payload', $response->output($index, Response::MODEL_INDEX)) - ; + ->setParam('payload', $response->output($index, Response::MODEL_INDEX)); $audits ->setParam('event', 'database.indexes.delete') - ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $index->getArrayCopy()) - ; + ->setParam('resource', 'database/collection/' . $collection->getId()) + ->setParam('data', $index->getArrayCopy()); $response->noContent(); }); @@ -1184,7 +1276,7 @@ App::post('/v1/database/collections/:collectionId/documents') /** @var Utopia\Database\Document $user */ /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ - + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data)) { @@ -1194,7 +1286,7 @@ App::post('/v1/database/collections/:collectionId/documents') if (isset($data['$id'])) { throw new Exception('$id is not allowed for creating new documents, try update instead', 400); } - + $collection = $dbForExternal->getCollection($collectionId); if ($collection->isEmpty()) { @@ -1203,26 +1295,23 @@ App::post('/v1/database/collections/:collectionId/documents') $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers $data['$id'] = $documentId == 'unique()' ? $dbForExternal->getId() : $documentId; - $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user - $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user + $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user + $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; // By default set write permissions for user try { $document = $dbForExternal->createDocument($collectionId, new Document($data)); - } - catch (StructureException $exception) { + } catch (StructureException $exception) { throw new Exception($exception->getMessage(), 400); } $usage ->setParam('database.documents.create', 1) - ->setParam('collectionId', $collectionId) - ; + ->setParam('collectionId', $collectionId); $audits ->setParam('event', 'database.documents.create') - ->setParam('resource', 'database/document/'.$document->getId()) - ->setParam('data', $document->getArrayCopy()) - ; + ->setParam('resource', 'database/document/' . $document->getId()) + ->setParam('data', $document->getArrayCopy()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($document, Response::MODEL_DOCUMENT); @@ -1286,9 +1375,8 @@ App::get('/v1/database/collections/:collectionId/documents') $usage ->setParam('database.documents.read', 1) - ->setParam('collectionId', $collectionId) - ; - + ->setParam('collectionId', $collectionId); + $response->dynamic(new Document([ 'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT), 'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null), @@ -1329,8 +1417,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') $usage ->setParam('database.documents.read', 1) - ->setParam('collectionId', $collectionId) - ; + ->setParam('collectionId', $collectionId); $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -1379,7 +1466,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') if (empty($data)) { throw new Exception('Missing payload', 400); } - + if (!\is_array($data)) { throw new Exception('Data param should be a valid JSON object', 400); } @@ -1393,24 +1480,20 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') try { $document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)); - } - catch (AuthorizationException $exception) { + } catch (AuthorizationException $exception) { throw new Exception('Unauthorized permissions', 401); + } catch (StructureException $exception) { + throw new Exception('Bad structure. ' . $exception->getMessage(), 400); } - catch (StructureException $exception) { - throw new Exception('Bad structure. '.$exception->getMessage(), 400); - } - + $usage ->setParam('database.documents.update', 1) - ->setParam('collectionId', $collectionId) - ; + ->setParam('collectionId', $collectionId); $audits ->setParam('event', 'database.documents.update') - ->setParam('resource', 'database/document/'.$document->getId()) - ->setParam('data', $document->getArrayCopy()) - ; + ->setParam('resource', 'database/document/' . $document->getId()) + ->setParam('data', $document->getArrayCopy()); $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -1456,18 +1539,28 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') $usage ->setParam('database.documents.delete', 1) - ->setParam('collectionId', $collectionId) - ; + ->setParam('collectionId', $collectionId); $events - ->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT)) - ; + ->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT)); $audits ->setParam('event', 'database.documents.delete') - ->setParam('resource', 'database/document/'.$document->getId()) + ->setParam('resource', 'database/document/' . $document->getId()) ->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action ; $response->noContent(); }); + + +// Refactor usage endpoint in Functions API + + +// Create usage endpoint in the Database API + + +// Create usage endpoint in the users API + + +// Create usage endpoint in the Storage API \ No newline at end of file diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index fbd27c47f3..0f7a4f9a99 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -259,6 +259,101 @@ App::get('/v1/functions/:functionId/usage') } }); +App::get('/v1/functions/:functionId/usage2') + ->desc('Get Function Usage') + ->groups(['api', 'functions']) + ->label('scope', 'functions.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'functions') + ->label('sdk.method', 'getUsage') + ->param('functionId', '', new UID(), 'Function unique ID.') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true) + ->inject('response') + ->inject('project') + ->inject('dbForInternal') + ->inject('register') + ->action(function ($functionId, $range, $response, $project, $dbForInternal, $register) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Document $project */ + /** @var Utopia\Database\Database $dbForInternal */ + /** @var Utopia\Registry\Registry $register */ + + $function = $dbForInternal->getDocument('functions', $functionId); + + if ($function->isEmpty()) { + throw new Exception('Function not found', 404); + } + + if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + + $metrics = ["functions.$functionId.executions", "functions.$functionId.failures", "functions.$functionId.compute"]; + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + $stats[$metric] = array_reverse($stats[$metric]); + } + + $executions = $stats["functions.$functionId.executions"] ?? []; + $failures = $stats["functions.$functionId.failures"] ?? []; + $compute = $stats["functions.$functionId.compute"] ?? []; + + $response->json([ + 'range' => $range, + 'executions' => [ + 'data' => $executions, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $executions)), + ], + 'failures' => [ + 'data' => $failures, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $failures)), + ], + 'compute' => [ + 'data' => $compute, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $compute)), + ], + ]); + } else { + $response->json([]); + } + }); + App::put('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Update Function') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 7d8f4ed54c..ba9d5627b5 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -217,7 +217,7 @@ App::get('/v1/projects/:projectId') }); App::get('/v1/projects/:projectId/usage') - ->desc('Get Project') + ->desc('Get Project Usage') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) From d654e4cd090f68b4f0320998f945f383f7454fa7 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 14:28:42 +0530 Subject: [PATCH 02/35] feat(usage): added usage endpoint to storage api --- app/controllers/api/storage.php | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index ed3c61dcdb..daecfb64e6 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -10,6 +10,7 @@ use Utopia\Validator\HexColor; use Utopia\Cache\Cache; use Utopia\Cache\Adapter\Filesystem; use Appwrite\ClamAV\Network; +use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Validator\CustomId; use Utopia\Database\Document; use Utopia\Database\Validator\UID; @@ -22,6 +23,7 @@ use Utopia\Image\Image; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Utopia\Response; use Utopia\Config\Config; +use Utopia\Database\Database; use Utopia\Database\Query; App::post('/v1/storage/files') @@ -640,4 +642,104 @@ App::delete('/v1/storage/files/:fileId') ; $response->noContent(); + }); + +App::get('/v1/storage/:bucketId/usage') + ->desc('Get Bucket Usage') + ->groups(['api', 'storage']) + ->label('scope', 'storage.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'storage') + ->label('sdk.method', 'getUsage') + ->param('bucketId', '', new UID(), 'Bucket unique ID.') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForInternal') + ->inject('register') + ->action(function ($bucketId, $range, $response, $dbForInternal) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForInternal */ + + $stats = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + + $metrics = [ + "storage.buckets.$bucketId.files.create", + "storage.buckets.$bucketId.files.read", + "storage.buckets.$bucketId.files.update", + "storage.buckets.$bucketId.files.delete" + ]; + + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + $stats[$metric] = array_reverse($stats[$metric]); + } + } + + Authorization::reset(); + + $create = $stats["storage.buckets.$bucketId.files.create"] ?? []; + $read = $stats["storage.buckets.$bucketId.files.read"] ?? []; + $update = $stats["storage.buckets.$bucketId.files.update"] ?? []; + $delete = $stats["storage.buckets.$bucketId.files.delete"] ?? []; + + $response->json([ + 'range' => $range, + 'create' => [ + 'data' => $create, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $create)), + ], + 'read' => [ + 'data' => $read, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $read)), + ], + 'update' => [ + 'data' => $update, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $update)), + ], + 'delete' => [ + 'data' => $delete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $delete)), + ], + ]); }); \ No newline at end of file From 070b7d827013a39a42bf8a09edbc6b40f1ea01c4 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 14:30:39 +0530 Subject: [PATCH 03/35] feat(usage): added usage endpoint to storage api --- app/controllers/api/storage.php | 72 +++++++++++++++++---------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index daecfb64e6..d167ee88b7 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -706,40 +706,42 @@ App::get('/v1/storage/:bucketId/usage') $stats[$metric] = array_reverse($stats[$metric]); } + + Authorization::reset(); + + $create = $stats["storage.buckets.$bucketId.files.create"] ?? []; + $read = $stats["storage.buckets.$bucketId.files.read"] ?? []; + $update = $stats["storage.buckets.$bucketId.files.update"] ?? []; + $delete = $stats["storage.buckets.$bucketId.files.delete"] ?? []; + + $response->json([ + 'range' => $range, + 'create' => [ + 'data' => $create, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $create)), + ], + 'read' => [ + 'data' => $read, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $read)), + ], + 'update' => [ + 'data' => $update, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $update)), + ], + 'delete' => [ + 'data' => $delete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $delete)), + ], + ]); + } else { + $response->json([]); } - - Authorization::reset(); - - $create = $stats["storage.buckets.$bucketId.files.create"] ?? []; - $read = $stats["storage.buckets.$bucketId.files.read"] ?? []; - $update = $stats["storage.buckets.$bucketId.files.update"] ?? []; - $delete = $stats["storage.buckets.$bucketId.files.delete"] ?? []; - - $response->json([ - 'range' => $range, - 'create' => [ - 'data' => $create, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $create)), - ], - 'read' => [ - 'data' => $read, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $read)), - ], - 'update' => [ - 'data' => $update, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $update)), - ], - 'delete' => [ - 'data' => $delete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $delete)), - ], - ]); }); \ No newline at end of file From aefbdb05974274eb8217e2ff14b09847af2454c4 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 15:27:46 +0530 Subject: [PATCH 04/35] feat(usage): added usage endpoint to users api --- app/controllers/api/users.php | 127 ++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 09d9d1bcdf..45d4eb03ed 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -2,6 +2,7 @@ use Appwrite\Auth\Auth; use Appwrite\Auth\Validator\Password; +use Appwrite\Database\Validator\Authorization; use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Exception; @@ -17,6 +18,8 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Validator\UID; use DeviceDetector\DeviceDetector; use Appwrite\Database\Validator\CustomId; +use Utopia\Database\Database; +use Utopia\Database\Query; App::post('/v1/users') ->desc('Create User') @@ -605,3 +608,127 @@ App::delete('/v1/users/:userId') ; $response->noContent(); }); + +App::get('/v1/users/usage') + ->desc('Get Users Usage') + ->groups(['api', 'users']) + ->label('scope', 'users.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'users') + ->label('sdk.method', 'getUsage') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForInternal') + ->inject('register') + ->action(function ($range, $response, $dbForInternal) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForInternal */ + + $stats = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + + $metrics = [ + "users.create", + "users.read", + "users.update", + "users.delete", + "users.sessions.create", + "users.sessions.delete" + ]; + + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + $stats[$metric] = array_reverse($stats[$metric]); + } + + $usersCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['users.count'])], 0, ['time'], [Database::ORDER_DESC]); + $usersTotal = $usersCount ? $usersCount->getAttribute('value', 0) : 0; + + Authorization::reset(); + + $create = $stats["users.create"] ?? []; + $read = $stats["users.read"] ?? []; + $update = $stats["users.update"] ?? []; + $delete = $stats["users.delete"] ?? []; + $sessionsCreate = $stats["users.sessions.create"] ?? []; + $sessionsDelete = $stats["users.sessions.delete"] ?? []; + + $response->json([ + 'range' => $range, + 'users' => [ + 'data' => [], + 'total' => $usersTotal, + ], + 'create' => [ + 'data' => $create, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $create)), + ], + 'read' => [ + 'data' => $read, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $read)), + ], + 'update' => [ + 'data' => $update, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $update)), + ], + 'delete' => [ + 'data' => $delete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $delete)), + ], + 'sessionsCreate' => [ + 'data' => $sessionsCreate, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $sessionsCreate)), + ], + 'sessionsDelete' => [ + 'data' => $sessionsDelete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $sessionsDelete)), + ] + ]); + } else { + $response->json([]); + } + }); \ No newline at end of file From f3074bc024b64b043d658146d0be9132b724e64a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 15:34:57 +0530 Subject: [PATCH 05/35] feat(usage): some refactoring --- app/controllers/api/database.php | 58 ++++++++++++++++--------------- app/controllers/api/functions.php | 2 ++ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 86f4029cdb..f2bf0329ca 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -316,35 +316,37 @@ App::get('/v1/database/usage') $stats[$metric] = array_reverse($stats[$metric]); } + + $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.documents.count'])], 0, ['time'], [Database::ORDER_DESC]); + $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; + + $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['n + '])], 0, ['time'], [Database::ORDER_DESC]); + $collectionsTotal = $collectionsCount ? $collectionsCount->getAttribute('value', 0) : 0; + + Authorization::reset(); + + $response->json([ + 'range' => $range, + 'stats' => $stats, + 'requests' => [ + 'data' => $stats['requests'] ?? [], + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $stats['requests'] ?? [])), + ], + 'documents' => [ + 'data' => [], + 'total' => $documentsTotal, + ], + 'collections' => [ + 'data' => [], + 'total' => $collectionsTotal, + ] + ]); + } else { + $response->json([]); } - - $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.documents.count'])], 0, ['time'], [Database::ORDER_DESC]); - $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; - - $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['n - '])], 0, ['time'], [Database::ORDER_DESC]); - $collectionsTotal = $collectionsCount ? $collectionsCount->getAttribute('value', 0) : 0; - - Authorization::reset(); - - $response->json([ - 'range' => $range, - 'stats' => $stats, - 'requests' => [ - 'data' => $stats['requests'] ?? [], - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $stats['requests'] ?? [])), - ], - 'documents' => [ - 'data' => [], - 'total' => $documentsTotal, - ], - 'collections' => [ - 'data' => [], - 'total' => $collectionsTotal, - ] - ]); }); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 0f7a4f9a99..ee3088ce18 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -323,6 +323,8 @@ App::get('/v1/functions/:functionId/usage2') $stats[$metric] = array_reverse($stats[$metric]); } + + Authorization::reset(); $executions = $stats["functions.$functionId.executions"] ?? []; $failures = $stats["functions.$functionId.failures"] ?? []; From 8b393b11653e0f8cf8ff86050be2890f28cdc2eb Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 16:53:33 +0530 Subject: [PATCH 06/35] feat(usage): added usage endpoint for database --- app/controllers/api/database.php | 65 ++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index f2bf0329ca..b4238d284a 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -320,26 +320,75 @@ App::get('/v1/database/usage') $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.documents.count'])], 0, ['time'], [Database::ORDER_DESC]); $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; - $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['n - '])], 0, ['time'], [Database::ORDER_DESC]); + $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.collections.count'])], 0, ['time'], [Database::ORDER_DESC]); $collectionsTotal = $collectionsCount ? $collectionsCount->getAttribute('value', 0) : 0; Authorization::reset(); + $documentsCreate = $stats["database.documents.create"] ?? []; + $documentsRead = $stats["database.documents.read"] ?? []; + $documentsUpdate = $stats["database.documents.update"] ?? []; + $documentsDelete = $stats["database.documents.delete"] ?? []; + $collectionsCreate = $stats["database.collections.create"] ?? []; + $collectionsRead = $stats["database.collections.read"] ?? []; + $collectionsUpdate = $stats["database.collections.update"] ?? []; + $collectionsDelete = $stats["database.collections.delete"] ?? []; + $response->json([ 'range' => $range, - 'stats' => $stats, - 'requests' => [ - 'data' => $stats['requests'] ?? [], + 'documents.create' => [ + 'data' => $documentsCreate, 'total' => \array_sum(\array_map(function ($item) { return $item['value']; - }, $stats['requests'] ?? [])), + }, $documentsCreate)), ], - 'documents' => [ + 'documents.read' => [ + 'data' => $documentsRead, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $documentsRead)), + ], + 'documents.update' => [ + 'data' => $documentsUpdate, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $documentsUpdate)), + ], + 'documents.delete' => [ + 'data' => $documentsDelete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $documentsDelete)), + ], + 'collections.create' => [ + 'data' => $collectionsCreate, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $collectionsCreate)), + ], + 'collections.read' => [ + 'data' => $collectionsRead, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $collectionsRead)), + ], + 'collections.update' => [ + 'data' => $collectionsUpdate, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $collectionsUpdate)), + ], + 'collections.delete' => [ + 'data' => $collectionsDelete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $collectionsDelete)), + ], + 'documentCount' => [ 'data' => [], 'total' => $documentsTotal, ], - 'collections' => [ + 'collectionCount' => [ 'data' => [], 'total' => $collectionsTotal, ] From 2468e6e97d5a1c5a03f88487e227c9bfe0cc38e4 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:08:57 +0530 Subject: [PATCH 07/35] feat(usage): added usage endpoint for collections --- app/controllers/api/database.php | 124 +++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index b4238d284a..5b41fbcf6f 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -257,10 +257,9 @@ App::get('/v1/database/usage') ->label('sdk.method', 'getUsage') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') - ->inject('dbForConsole') ->inject('dbForInternal') ->inject('register') - ->action(function ($range, $response, $dbForConsole, $dbForInternal, $register) { + ->action(function ($range, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ /** @var Utopia\Database\Database $dbForInternal */ @@ -399,11 +398,122 @@ App::get('/v1/database/usage') }); -// :collectionId/usage -// 'reads', -// 'writes', -// 'updates', -// 'delete' +App::get('/v1/database/:collectionId/usage') + ->desc('Get Database Usage') + ->groups(['api', 'database']) + ->label('scope', 'collections.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'database') + ->label('sdk.method', 'getUsage') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->param('collectionId', '', new UID(), 'Collection unique ID.') + ->inject('response') + ->inject('dbForInternal') + ->inject('dbForExternal') + ->action(function ($range, $collectionId, $response, $dbForInternal, $dbForExternal) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForConsole */ + /** @var Utopia\Database\Database $dbForInternal */ + /** @var Utopia\Registry\Registry $register */ + + $collection = $dbForExternal->getCollection($collectionId); + + if ($collection->isEmpty()) { + throw new Exception('Collection not found', 404); + } + + $stats = []; + if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + + $metrics = [ + "database.collections.$collectionId.documents.create", + "database.collections.$collectionId.documents.read", + "database.collections.$collectionId.documents.update", + "database.collections.$collectionId.documents.delete", + ]; + + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + $stats[$metric] = array_reverse($stats[$metric]); + } + + $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ["database.collections.$collectionId.documents.count"])], 0, ['time'], [Database::ORDER_DESC]); + $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; + + Authorization::reset(); + + $create = $stats["database.collections.$collectionId.documents.create"] ?? []; + $read = $stats["database.collections.$collectionId.documents.read"] ?? []; + $update = $stats["database.collections.$collectionId.documents.update"] ?? []; + $delete = $stats["database.collections.$collectionId.documents.delete"] ?? []; + + $response->json([ + 'range' => $range, + 'create' => [ + 'data' => $create, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $create)), + ], + 'read' => [ + 'data' => $read, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $read)), + ], + 'update' => [ + 'data' => $update, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $update)), + ], + 'delete' => [ + 'data' => $delete, + 'total' => \array_sum(\array_map(function ($item) { + return $item['value']; + }, $delete)), + ], + 'documentCount' => [ + 'data' => [], + 'total' => $documentsTotal, + ], + ]); + } else { + $response->json([]); + } + }); App::get('/v1/database/collections/:collectionId/logs') ->desc('List Collection Logs') From ae030ebd4f383f86983e01be5981c6f116e28446 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:10:35 +0530 Subject: [PATCH 08/35] feat(usage): doc fix --- app/controllers/api/database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 5b41fbcf6f..e65d6929fd 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -399,7 +399,7 @@ App::get('/v1/database/usage') App::get('/v1/database/:collectionId/usage') - ->desc('Get Database Usage') + ->desc('Get Database Usage for a collection') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) From c36b5b8b30716bfdae32fb6c1364a329db38981a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:11:13 +0530 Subject: [PATCH 09/35] feat(usage): cleanup comments --- app/controllers/api/database.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index e65d6929fd..c167c2f6dd 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1712,16 +1712,4 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') ; $response->noContent(); - }); - - -// Refactor usage endpoint in Functions API - - -// Create usage endpoint in the Database API - - -// Create usage endpoint in the users API - - -// Create usage endpoint in the Storage API \ No newline at end of file + }); \ No newline at end of file From 24925d2eb259ea003f945c8cf7d6a7f5a0bf6bb8 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:40:52 +0530 Subject: [PATCH 10/35] feat(usage): cleanup comments --- app/controllers/api/database.php | 5 ++--- app/controllers/api/storage.php | 4 +++- app/controllers/api/users.php | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index c167c2f6dd..ddbbe7e39b 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -249,7 +249,7 @@ App::get('/v1/database/collections/:collectionId') }); App::get('/v1/database/usage') - ->desc('Get Database Usage') + ->desc('Get usage stats for the database') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -258,7 +258,6 @@ App::get('/v1/database/usage') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') - ->inject('register') ->action(function ($range, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ @@ -399,7 +398,7 @@ App::get('/v1/database/usage') App::get('/v1/database/:collectionId/usage') - ->desc('Get Database Usage for a collection') + ->desc('Get usage stats for a collection') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index d167ee88b7..0800f2ac65 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -645,7 +645,7 @@ App::delete('/v1/storage/files/:fileId') }); App::get('/v1/storage/:bucketId/usage') - ->desc('Get Bucket Usage') + ->desc('Get usage stats for a storage bucket') ->groups(['api', 'storage']) ->label('scope', 'storage.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -660,6 +660,8 @@ App::get('/v1/storage/:bucketId/usage') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ + // TODO: Check is the storage bucket exists else throw 404 + $stats = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 45d4eb03ed..6d82c4f472 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -610,7 +610,7 @@ App::delete('/v1/users/:userId') }); App::get('/v1/users/usage') - ->desc('Get Users Usage') + ->desc('Get usage stats for the users API') ->groups(['api', 'users']) ->label('scope', 'users.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) From 9ebc5b891b29654f699cb518bfafa017ee4a142e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:47:19 +0530 Subject: [PATCH 11/35] feat(usage): added usage endpoint for storage --- app/controllers/api/storage.php | 62 +++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 0800f2ac65..3b433988e0 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -644,6 +644,64 @@ App::delete('/v1/storage/files/:fileId') $response->noContent(); }); +App::get('/v1/storage/usage') + ->desc('Get usage stats for storage') + ->groups(['api', 'storage']) + ->label('scope', 'storage.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'storage') + ->label('sdk.method', 'getUsage') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForInternal') + ->action(function ($range, $response, $dbForInternal) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForInternal */ + + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + + $storageTotal = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['storage.total'])], 0, ['time'], [Database::ORDER_DESC]); + $storage = $storageTotal ? $storageTotal->getAttribute('value', 0) : 0; + + $filesCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['storage.files.count'])], 0, ['time'], [Database::ORDER_DESC]); + $filesTotal = $filesCount ? $filesCount->getAttribute('value', 0) : 0; + + Authorization::reset(); + + $response->json([ + 'range' => $range, + 'storage' => [ + 'total' => $storage, + ], + 'files' => [ + 'total' => $filesTotal, + ], + ]); + } else { + $response->json([]); + } + }); + App::get('/v1/storage/:bucketId/usage') ->desc('Get usage stats for a storage bucket') ->groups(['api', 'storage']) @@ -655,13 +713,11 @@ App::get('/v1/storage/:bucketId/usage') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') - ->inject('register') ->action(function ($bucketId, $range, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - // TODO: Check is the storage bucket exists else throw 404 - + // TODO: Check if the storage bucket exists else throw 404 $stats = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ From e7eb0e7cdfdad9cbd1c9f0f5c02c2b8b82746141 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:47:56 +0530 Subject: [PATCH 12/35] feat(usage): added usage endpoint for storage --- app/controllers/api/projects.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index ba9d5627b5..45b7d7b010 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -217,7 +217,7 @@ App::get('/v1/projects/:projectId') }); App::get('/v1/projects/:projectId/usage') - ->desc('Get Project Usage') + ->desc('Get usage stats for a project') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) From a2a73d87e25a3efd14000d81984cb221613ea81d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 20 Aug 2021 17:52:04 +0530 Subject: [PATCH 13/35] feat(usage): added usage endpoint for database --- app/controllers/api/database.php | 149 ++++++++++++++++++------------- 1 file changed, 87 insertions(+), 62 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index ddbbe7e39b..59083546a1 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -61,7 +61,7 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database if (!$validator->isValid($default)) { throw new Exception('Length of default attribute exceeds attribute size', 400); } - } + } if (!\is_null($format)) { $name = \json_decode($format, true)['name']; @@ -111,14 +111,16 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database $database ->setParam('type', DATABASE_TYPE_CREATE_ATTRIBUTE) - ->setParam('document', $attribute); + ->setParam('document', $attribute) + ; $usage->setParam('database.collections.update', 1); $audits ->setParam('event', 'database.attributes.create') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $attribute); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $attribute) + ; $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); @@ -166,8 +168,9 @@ App::post('/v1/database/collections') $audits ->setParam('event', 'database.collections.create') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $collection->getArrayCopy()); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $collection->getArrayCopy()) + ; $usage->setParam('database.collections.create', 1); @@ -248,7 +251,7 @@ App::get('/v1/database/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); -App::get('/v1/database/usage') + App::get('/v1/database/usage') ->desc('Get usage stats for the database') ->groups(['api', 'database']) ->label('scope', 'collections.read') @@ -396,7 +399,6 @@ App::get('/v1/database/usage') } }); - App::get('/v1/database/:collectionId/usage') ->desc('Get usage stats for a collection') ->groups(['api', 'database']) @@ -547,7 +549,7 @@ App::get('/v1/database/collections/:collectionId/logs') $audit = new Audit($dbForInternal); - $logs = $audit->getLogsByResource('database/collection/' . $collection->getId()); + $logs = $audit->getLogsByResource('database/collection/'.$collection->getId()); $output = []; @@ -599,8 +601,8 @@ App::get('/v1/database/collections/:collectionId/logs') $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')); + $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'); @@ -654,15 +656,16 @@ App::put('/v1/database/collections/:collectionId') } catch (AuthorizationException $exception) { throw new Exception('Unauthorized permissions', 401); } catch (StructureException $exception) { - throw new Exception('Bad structure. ' . $exception->getMessage(), 400); - } + throw new Exception('Bad structure. '.$exception->getMessage(), 400); + } $usage->setParam('database.collections.update', 1); $audits ->setParam('event', 'database.collections.update') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $collection->getArrayCopy()); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $collection->getArrayCopy()) + ; $response->dynamic($collection, Response::MODEL_COLLECTION); }); @@ -703,12 +706,14 @@ App::delete('/v1/database/collections/:collectionId') $usage->setParam('database.collections.delete', 1); $events - ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION)); + ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION)) + ; $audits ->setParam('event', 'database.collections.delete') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $collection->getArrayCopy()); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $collection->getArrayCopy()) + ; $response->noContent(); }); @@ -791,7 +796,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name' => 'email']), + 'format' => \json_encode(['name'=>'email']), ]), $response, $dbForExternal, $database, $audits, $usage); }); @@ -832,7 +837,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name' => 'ip']), + 'format' => \json_encode(['name'=>'ip']), ]), $response, $dbForExternal, $database, $audits, $usage); }); @@ -874,7 +879,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name' => 'url']), + 'format' => \json_encode(['name'=>'url']), ]), $response, $dbForExternal, $database, $audits, $usage); }); @@ -909,7 +914,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ - + return $attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, @@ -919,7 +924,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') 'default' => $default, 'array' => $array, 'format' => \json_encode([ - 'name' => 'int-range', + 'name'=>'int-range', 'min' => $min, 'max' => $max, ]), @@ -966,7 +971,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float') 'default' => $default, 'array' => $array, 'format' => \json_encode([ - 'name' => 'float-range', + 'name'=>'float-range', 'min' => $min, 'max' => $max, ]), @@ -1096,7 +1101,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') ])]); $usage->setParam('database.collections.read', 1); - + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); }); @@ -1151,17 +1156,20 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') $database ->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE) - ->setParam('document', $attribute); + ->setParam('document', $attribute) + ; $usage->setParam('database.collections.update', 1); $events - ->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE)); + ->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE)) + ; $audits ->setParam('event', 'database.attributes.delete') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $attribute->getArrayCopy()); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $attribute->getArrayCopy()) + ; $response->noContent(); }); @@ -1242,17 +1250,20 @@ App::post('/v1/database/collections/:collectionId/indexes') $database ->setParam('type', DATABASE_TYPE_CREATE_INDEX) - ->setParam('document', $index); + ->setParam('document', $index) + ; $usage->setParam('database.collections.update', 1); $audits ->setParam('event', 'database.indexes.create') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $index->getArrayCopy()); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $index->getArrayCopy()) + ; $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($index, Response::MODEL_INDEX); + }); App::get('/v1/database/collections/:collectionId/indexes') @@ -1338,7 +1349,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId') ])]); $usage->setParam('database.collections.read', 1); - + $response->dynamic($index, Response::MODEL_INDEX); }); @@ -1379,7 +1390,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') $indexes = $collection->getAttribute('indexes'); // find attribute in collection - $index = null; + $index= null; foreach ($indexes as $i) { if ($i->getId() === $indexId) { $index = $i->setAttribute('$collection', $collectionId); // set the collectionId @@ -1393,17 +1404,20 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') $database ->setParam('type', DATABASE_TYPE_DELETE_INDEX) - ->setParam('document', $index); + ->setParam('document', $index) + ; $usage->setParam('database.collections.update', 1); $events - ->setParam('payload', $response->output($index, Response::MODEL_INDEX)); + ->setParam('payload', $response->output($index, Response::MODEL_INDEX)) + ; $audits ->setParam('event', 'database.indexes.delete') - ->setParam('resource', 'database/collection/' . $collection->getId()) - ->setParam('data', $index->getArrayCopy()); + ->setParam('resource', 'database/collection/'.$collection->getId()) + ->setParam('data', $index->getArrayCopy()) + ; $response->noContent(); }); @@ -1436,7 +1450,7 @@ App::post('/v1/database/collections/:collectionId/documents') /** @var Utopia\Database\Document $user */ /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Stats\Stats $usage */ - + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data)) { @@ -1446,7 +1460,7 @@ App::post('/v1/database/collections/:collectionId/documents') if (isset($data['$id'])) { throw new Exception('$id is not allowed for creating new documents, try update instead', 400); } - + $collection = $dbForExternal->getCollection($collectionId); if ($collection->isEmpty()) { @@ -1455,23 +1469,26 @@ App::post('/v1/database/collections/:collectionId/documents') $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers $data['$id'] = $documentId == 'unique()' ? $dbForExternal->getId() : $documentId; - $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user - $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; // By default set write permissions for user + $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user + $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user try { $document = $dbForExternal->createDocument($collectionId, new Document($data)); - } catch (StructureException $exception) { + } + catch (StructureException $exception) { throw new Exception($exception->getMessage(), 400); } $usage ->setParam('database.documents.create', 1) - ->setParam('collectionId', $collectionId); + ->setParam('collectionId', $collectionId) + ; $audits ->setParam('event', 'database.documents.create') - ->setParam('resource', 'database/document/' . $document->getId()) - ->setParam('data', $document->getArrayCopy()); + ->setParam('resource', 'database/document/'.$document->getId()) + ->setParam('data', $document->getArrayCopy()) + ; $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($document, Response::MODEL_DOCUMENT); @@ -1535,8 +1552,9 @@ App::get('/v1/database/collections/:collectionId/documents') $usage ->setParam('database.documents.read', 1) - ->setParam('collectionId', $collectionId); - + ->setParam('collectionId', $collectionId) + ; + $response->dynamic(new Document([ 'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT), 'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null), @@ -1577,7 +1595,8 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') $usage ->setParam('database.documents.read', 1) - ->setParam('collectionId', $collectionId); + ->setParam('collectionId', $collectionId) + ; $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -1626,7 +1645,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') if (empty($data)) { throw new Exception('Missing payload', 400); } - + if (!\is_array($data)) { throw new Exception('Data param should be a valid JSON object', 400); } @@ -1640,20 +1659,24 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') try { $document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)); - } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401); - } catch (StructureException $exception) { - throw new Exception('Bad structure. ' . $exception->getMessage(), 400); } - + catch (AuthorizationException $exception) { + throw new Exception('Unauthorized permissions', 401); + } + catch (StructureException $exception) { + throw new Exception('Bad structure. '.$exception->getMessage(), 400); + } + $usage ->setParam('database.documents.update', 1) - ->setParam('collectionId', $collectionId); + ->setParam('collectionId', $collectionId) + ; $audits ->setParam('event', 'database.documents.update') - ->setParam('resource', 'database/document/' . $document->getId()) - ->setParam('data', $document->getArrayCopy()); + ->setParam('resource', 'database/document/'.$document->getId()) + ->setParam('data', $document->getArrayCopy()) + ; $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -1699,16 +1722,18 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') $usage ->setParam('database.documents.delete', 1) - ->setParam('collectionId', $collectionId); + ->setParam('collectionId', $collectionId) + ; $events - ->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT)); + ->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT)) + ; $audits ->setParam('event', 'database.documents.delete') - ->setParam('resource', 'database/document/' . $document->getId()) + ->setParam('resource', 'database/document/'.$document->getId()) ->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action ; $response->noContent(); - }); \ No newline at end of file + }); From 7775daa478a4d1f30cff6f3252d6d949f14ad568 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 26 Aug 2021 16:30:03 +0530 Subject: [PATCH 14/35] feat(usage): added new response model --- app/controllers/api/database.php | 2 - composer.json | 2 +- composer.lock | 14 ++--- docker-compose.yml | 4 +- src/Appwrite/Utopia/Response.php | 1 + src/Appwrite/Utopia/Response/Model/Metric.php | 53 +++++++++++++++++++ 6 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/Metric.php diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 2a85899a25..edbb9aa5a6 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1,8 +1,6 @@ addRule('message', [ + 'type' => self::TYPE_STRING, + 'description' => 'Error message.', + 'default' => '', + 'example' => 'Not found', + ]) + ->addRule('code', [ + 'type' => self::TYPE_STRING, + 'description' => 'Error code.', + 'default' => '', + 'example' => '404', + ]) + ->addRule('version', [ + 'type' => self::TYPE_STRING, + 'description' => 'Server version number.', + 'default' => '', + 'example' => '1.0', + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'Metric'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_METRIC; + } +} \ No newline at end of file From 3d512e74bf61119ba4bc9aa2f2c2cb1c2fc7bc05 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 26 Aug 2021 23:45:36 +0530 Subject: [PATCH 15/35] feat(usage): added response models for database api --- app/controllers/api/database.php | 157 +++++------------- app/controllers/api/storage.php | 27 ++- src/Appwrite/Utopia/Response.php | 7 + .../Utopia/Response/Model/CollectionUsage.php | 77 +++++++++ .../Utopia/Response/Model/DatabaseUsage.php | 112 +++++++++++++ src/Appwrite/Utopia/Response/Model/Metric.php | 26 ++- 6 files changed, 269 insertions(+), 137 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/CollectionUsage.php create mode 100644 src/Appwrite/Utopia/Response/Model/DatabaseUsage.php diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index edbb9aa5a6..a18c2e885c 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -245,13 +245,16 @@ App::get('/v1/database/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); - App::get('/v1/database/usage') +App::get('/v1/database/usage') ->desc('Get usage stats for the database') ->groups(['api', 'database']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'database') ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_DATABASE_USAGE) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') @@ -261,11 +264,11 @@ App::get('/v1/database/collections/:collectionId') /** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Registry\Registry $register */ - $stats = []; + $usage = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '30m', + 'period' => '15m', 'limit' => 48, ], '7d' => [ @@ -285,6 +288,8 @@ App::get('/v1/database/collections/:collectionId') Authorization::disable(); $metrics = [ + 'database.documents.count', + 'database.collections.count', 'database.collections.create', 'database.collections.read', 'database.collections.update', @@ -295,6 +300,7 @@ App::get('/v1/database/collections/:collectionId') 'database.documents.delete' ]; + $stats = []; foreach ($metrics as $metric) { $requestDocs = $dbForInternal->find('stats', [ new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), @@ -312,85 +318,24 @@ App::get('/v1/database/collections/:collectionId') $stats[$metric] = array_reverse($stats[$metric]); } - $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.documents.count'])], 0, ['time'], [Database::ORDER_DESC]); - $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; - - $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.collections.count'])], 0, ['time'], [Database::ORDER_DESC]); - $collectionsTotal = $collectionsCount ? $collectionsCount->getAttribute('value', 0) : 0; - Authorization::reset(); - $documentsCreate = $stats["database.documents.create"] ?? []; - $documentsRead = $stats["database.documents.read"] ?? []; - $documentsUpdate = $stats["database.documents.update"] ?? []; - $documentsDelete = $stats["database.documents.delete"] ?? []; - $collectionsCreate = $stats["database.collections.create"] ?? []; - $collectionsRead = $stats["database.collections.read"] ?? []; - $collectionsUpdate = $stats["database.collections.update"] ?? []; - $collectionsDelete = $stats["database.collections.delete"] ?? []; - - $response->json([ + $usage = new Document([ 'range' => $range, - 'documents.create' => [ - 'data' => $documentsCreate, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $documentsCreate)), - ], - 'documents.read' => [ - 'data' => $documentsRead, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $documentsRead)), - ], - 'documents.update' => [ - 'data' => $documentsUpdate, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $documentsUpdate)), - ], - 'documents.delete' => [ - 'data' => $documentsDelete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $documentsDelete)), - ], - 'collections.create' => [ - 'data' => $collectionsCreate, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $collectionsCreate)), - ], - 'collections.read' => [ - 'data' => $collectionsRead, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $collectionsRead)), - ], - 'collections.update' => [ - 'data' => $collectionsUpdate, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $collectionsUpdate)), - ], - 'collections.delete' => [ - 'data' => $collectionsDelete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $collectionsDelete)), - ], - 'documentCount' => [ - 'data' => [], - 'total' => $documentsTotal, - ], - 'collectionCount' => [ - 'data' => [], - 'total' => $collectionsTotal, - ] + 'documents.count' => $stats["database.documents.count"], + 'collections.count' => $stats["database.collections.count"], + 'documents.create' => $stats["database.documents.create"], + 'documents.read' => $stats["database.documents.read"], + 'documents.update' => $stats["database.documents.update"], + 'documents.delete' => $stats["database.documents.delete"], + 'collections.create' => $stats["database.collections.create"], + 'collections.read' => $stats["database.collections.read"], + 'collections.update' => $stats["database.collections.update"], + 'collections.delete' => $stats["database.collections.delete"], ]); - } else { - $response->json([]); } + + $response->dynamic($usage, Response::MODEL_DATABASE_USAGE); }); App::get('/v1/database/:collectionId/usage') @@ -400,6 +345,9 @@ App::get('/v1/database/:collectionId/usage') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'database') ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_COLLECTION_USAGE) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->param('collectionId', '', new UID(), 'Collection unique ID.') ->inject('response') @@ -417,11 +365,11 @@ App::get('/v1/database/:collectionId/usage') throw new Exception('Collection not found', 404); } - $stats = []; + $usage = []; if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '30m', + 'period' => '15m', 'limit' => 48, ], '7d' => [ @@ -441,12 +389,14 @@ App::get('/v1/database/:collectionId/usage') Authorization::disable(); $metrics = [ + "database.collections.$collectionId.documents.count", "database.collections.$collectionId.documents.create", "database.collections.$collectionId.documents.read", "database.collections.$collectionId.documents.update", "database.collections.$collectionId.documents.delete", ]; + $stats = []; foreach ($metrics as $metric) { $requestDocs = $dbForInternal->find('stats', [ new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), @@ -460,54 +410,22 @@ App::get('/v1/database/:collectionId/usage') 'date' => $requestDoc->getAttribute('time'), ]; } - $stats[$metric] = array_reverse($stats[$metric]); } - - $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ["database.collections.$collectionId.documents.count"])], 0, ['time'], [Database::ORDER_DESC]); - $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; Authorization::reset(); - - $create = $stats["database.collections.$collectionId.documents.create"] ?? []; - $read = $stats["database.collections.$collectionId.documents.read"] ?? []; - $update = $stats["database.collections.$collectionId.documents.update"] ?? []; - $delete = $stats["database.collections.$collectionId.documents.delete"] ?? []; - $response->json([ + $usage = new Document([ 'range' => $range, - 'create' => [ - 'data' => $create, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $create)), - ], - 'read' => [ - 'data' => $read, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $read)), - ], - 'update' => [ - 'data' => $update, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $update)), - ], - 'delete' => [ - 'data' => $delete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $delete)), - ], - 'documentCount' => [ - 'data' => [], - 'total' => $documentsTotal, - ], + 'documents.count' => $stats["database.collections.$collectionId.documents.count"], + 'documents.create' => $stats["database.collections.$collectionId.documents.create"], + 'documents.read' => $stats["database.collections.$collectionId.documents.read"], + 'documents.update' => $stats["database.collections.$collectionId.documents.update"], + 'documents.delete' => $stats["database.collections.$collectionId.documents.delete"] ]); - } else { - $response->json([]); } + + $response->dynamic($usage, Response::MODEL_COLLECTION_USAGE); }); App::get('/v1/database/collections/:collectionId/logs') @@ -1458,6 +1376,7 @@ App::post('/v1/database/collections/:collectionId/documents') $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user + var_dump($collectionId); try { $document = $dbForExternal->createDocument($collectionId, new Document($data)); } diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 3b433988e0..5589dbe95f 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -647,7 +647,7 @@ App::delete('/v1/storage/files/:fileId') App::get('/v1/storage/usage') ->desc('Get usage stats for storage') ->groups(['api', 'storage']) - ->label('scope', 'storage.read') + ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getUsage') @@ -705,7 +705,7 @@ App::get('/v1/storage/usage') App::get('/v1/storage/:bucketId/usage') ->desc('Get usage stats for a storage bucket') ->groups(['api', 'storage']) - ->label('scope', 'storage.read') + ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getUsage') @@ -772,6 +772,29 @@ App::get('/v1/storage/:bucketId/usage') $update = $stats["storage.buckets.$bucketId.files.update"] ?? []; $delete = $stats["storage.buckets.$bucketId.files.delete"] ?? []; + + // 'name' => [ + // [ + // [ + // 'value' => '', + // 'date' => 'unix timestamp' + // ] + // ] + // ] + + // $res = [ + // 'range' => '', + // 'name' => [ + // [ + // 'value' => , + // 'date' => + // ] + // ], + // 'nameTotal' => [ + + // ] + // ]; + $response->json([ 'range' => $range, 'create' => [ diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index f7217a5155..44799fdaa2 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -13,9 +13,11 @@ use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\Attribute; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Collection; +use Appwrite\Utopia\Response\Model\CollectionUsage; use Appwrite\Utopia\Response\Model\Continent; use Appwrite\Utopia\Response\Model\Country; use Appwrite\Utopia\Response\Model\Currency; +use Appwrite\Utopia\Response\Model\DatabaseUsage; use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Domain; use Appwrite\Utopia\Response\Model\Error; @@ -57,6 +59,7 @@ class Response extends SwooleResponse const MODEL_LOG_LIST = 'logList'; const MODEL_ERROR = 'error'; const MODEL_METRIC = 'metric'; + const MODEL_METRIC_LIST = 'metricList'; const MODEL_ERROR_DEV = 'errorDev'; const MODEL_BASE_LIST = 'baseList'; @@ -69,6 +72,8 @@ class Response extends SwooleResponse const MODEL_INDEX_LIST = 'indexList'; const MODEL_DOCUMENT = 'document'; const MODEL_DOCUMENT_LIST = 'documentList'; + const MODEL_DATABASE_USAGE = 'databaseUsage'; + const MODEL_COLLECTION_USAGE = 'collectionUsage'; // Users const MODEL_USER = 'user'; @@ -183,6 +188,8 @@ class Response extends SwooleResponse ->setModel(new Attribute()) ->setModel(new Index()) ->setModel(new ModelDocument()) + ->setModel(new DatabaseUsage()) + ->setModel(new CollectionUsage()) ->setModel(new Log()) ->setModel(new User()) ->setModel(new Preferences()) diff --git a/src/Appwrite/Utopia/Response/Model/CollectionUsage.php b/src/Appwrite/Utopia/Response/Model/CollectionUsage.php new file mode 100644 index 0000000000..2281b3d032 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/CollectionUsage.php @@ -0,0 +1,77 @@ +addRule('range', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'The value of this metric at a timestamp.', + 'default' => 0, + 'example' => 1, + ]) + ->addRule('documents.count', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for total number of documents.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents created.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.read', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents read.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.update', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents updated.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.delete', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents deleted.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'CollectionUsage'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_COLLECTION_USAGE; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php b/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php new file mode 100644 index 0000000000..58d9575d70 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php @@ -0,0 +1,112 @@ +addRule('range', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'The value of this metric at a timestamp.', + 'default' => 0, + 'example' => 1, + ]) + ->addRule('documents.count', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for total number of documents.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('collections.count', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for total number of collections.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents created.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.read', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents read.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.update', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents updated.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents.delete', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for documents deleted.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('collections.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for collections created.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('collections.read', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for collections read.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('collections.update', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for collections updated.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('collections.delete', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for collections delete.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'DatabaseUsage'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_DATABASE_USAGE; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/Metric.php b/src/Appwrite/Utopia/Response/Model/Metric.php index 73625eb1f3..26038af081 100644 --- a/src/Appwrite/Utopia/Response/Model/Metric.php +++ b/src/Appwrite/Utopia/Response/Model/Metric.php @@ -10,23 +10,17 @@ class Metric extends Model public function __construct() { $this - ->addRule('message', [ - 'type' => self::TYPE_STRING, - 'description' => 'Error message.', - 'default' => '', - 'example' => 'Not found', + ->addRule('value', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'The value of this metric at the timestamp.', + 'default' => -1, + 'example' => 1, ]) - ->addRule('code', [ - 'type' => self::TYPE_STRING, - 'description' => 'Error code.', - 'default' => '', - 'example' => '404', - ]) - ->addRule('version', [ - 'type' => self::TYPE_STRING, - 'description' => 'Server version number.', - 'default' => '', - 'example' => '1.0', + ->addRule('timestamp', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'The UNIX timestamp at which this metric was aggregated.', + 'default' => 0, + 'example' => 1592981250 ]) ; } From 6d6b0ee02418fe6ea7d70eb8b562ef46d256f023 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 27 Aug 2021 00:14:32 +0530 Subject: [PATCH 16/35] feat(usage): added response models for storage API --- app/controllers/api/storage.php | 212 ++++++++---------- src/Appwrite/Utopia/Response.php | 6 + .../Utopia/Response/Model/BucketsUsage.php | 70 ++++++ .../Utopia/Response/Model/CollectionUsage.php | 8 +- .../Utopia/Response/Model/DatabaseUsage.php | 8 +- .../Utopia/Response/Model/StorageUsage.php | 56 +++++ 6 files changed, 230 insertions(+), 130 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/BucketsUsage.php create mode 100644 src/Appwrite/Utopia/Response/Model/StorageUsage.php diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 5589dbe95f..69a7c18708 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -651,6 +651,9 @@ App::get('/v1/storage/usage') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_STORAGE_USAGE) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') @@ -658,71 +661,11 @@ App::get('/v1/storage/usage') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ + $usage = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '30m', - 'limit' => 48, - ], - '7d' => [ - 'period' => '1d', - 'limit' => 7, - ], - '30d' => [ - 'period' => '1d', - 'limit' => 30, - ], - '90d' => [ - 'period' => '1d', - 'limit' => 90, - ], - ]; - - Authorization::disable(); - - $storageTotal = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['storage.total'])], 0, ['time'], [Database::ORDER_DESC]); - $storage = $storageTotal ? $storageTotal->getAttribute('value', 0) : 0; - - $filesCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['storage.files.count'])], 0, ['time'], [Database::ORDER_DESC]); - $filesTotal = $filesCount ? $filesCount->getAttribute('value', 0) : 0; - - Authorization::reset(); - - $response->json([ - 'range' => $range, - 'storage' => [ - 'total' => $storage, - ], - 'files' => [ - 'total' => $filesTotal, - ], - ]); - } else { - $response->json([]); - } - }); - -App::get('/v1/storage/:bucketId/usage') - ->desc('Get usage stats for a storage bucket') - ->groups(['api', 'storage']) - ->label('scope', 'files.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getUsage') - ->param('bucketId', '', new UID(), 'Bucket unique ID.') - ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) - ->inject('response') - ->inject('dbForInternal') - ->action(function ($bucketId, $range, $response, $dbForInternal) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForInternal */ - - // TODO: Check if the storage bucket exists else throw 404 - $stats = []; - if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $period = [ - '24h' => [ - 'period' => '30m', + 'period' => '15m', 'limit' => 48, ], '7d' => [ @@ -742,12 +685,11 @@ App::get('/v1/storage/:bucketId/usage') Authorization::disable(); $metrics = [ - "storage.buckets.$bucketId.files.create", - "storage.buckets.$bucketId.files.read", - "storage.buckets.$bucketId.files.update", - "storage.buckets.$bucketId.files.delete" + "storage.total", + "storage.files.count" ]; + $stats = []; foreach ($metrics as $metric) { $requestDocs = $dbForInternal->find('stats', [ new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), @@ -761,68 +703,94 @@ App::get('/v1/storage/:bucketId/usage') 'date' => $requestDoc->getAttribute('time'), ]; } - $stats[$metric] = array_reverse($stats[$metric]); } + $usage = new Document([ + 'range' => $range, + 'storage' => $stats['storage.total'], + 'files' => $stats['storage.files.count'] + ]); + } + + $response->dynamic($usage, Response::MODEL_STORAGE_USAGE); + }); + +App::get('/v1/storage/:bucketId/usage') + ->desc('Get usage stats for a storage bucket') + ->groups(['api', 'storage']) + ->label('scope', 'files.read') + ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) + ->label('sdk.namespace', 'storage') + ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_BUCKETS_USAGE) + ->param('bucketId', '', new UID(), 'Bucket unique ID.') + ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->inject('response') + ->inject('dbForInternal') + ->action(function ($bucketId, $range, $response, $dbForInternal) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForInternal */ + + // TODO: Check if the storage bucket exists else throw 404 + + $usage = []; + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + $period = [ + '24h' => [ + 'period' => '30m', + 'limit' => 48, + ], + '7d' => [ + 'period' => '1d', + 'limit' => 7, + ], + '30d' => [ + 'period' => '1d', + 'limit' => 30, + ], + '90d' => [ + 'period' => '1d', + 'limit' => 90, + ], + ]; + + Authorization::disable(); + $metrics = [ + "storage.buckets.$bucketId.files.create", + "storage.buckets.$bucketId.files.read", + "storage.buckets.$bucketId.files.update", + "storage.buckets.$bucketId.files.delete" + ]; + + $stats = []; + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } Authorization::reset(); - $create = $stats["storage.buckets.$bucketId.files.create"] ?? []; - $read = $stats["storage.buckets.$bucketId.files.read"] ?? []; - $update = $stats["storage.buckets.$bucketId.files.update"] ?? []; - $delete = $stats["storage.buckets.$bucketId.files.delete"] ?? []; - - - // 'name' => [ - // [ - // [ - // 'value' => '', - // 'date' => 'unix timestamp' - // ] - // ] - // ] - - // $res = [ - // 'range' => '', - // 'name' => [ - // [ - // 'value' => , - // 'date' => - // ] - // ], - // 'nameTotal' => [ - - // ] - // ]; - - $response->json([ + $usage = new Document([ 'range' => $range, - 'create' => [ - 'data' => $create, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $create)), - ], - 'read' => [ - 'data' => $read, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $read)), - ], - 'update' => [ - 'data' => $update, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $update)), - ], - 'delete' => [ - 'data' => $delete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $delete)), - ], + 'files.create' => $stats["storage.buckets.$bucketId.files.create"], + 'files.read' => $stats["storage.buckets.$bucketId.files.read"], + 'files.update' => $stats["storage.buckets.$bucketId.files.update"], + 'files.delete' => $stats["storage.buckets.$bucketId.files.delete"] ]); - } else { - $response->json([]); } + + $response->dynamic($usage, Response::MODEL_BUCKETS_USAGE); }); \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 44799fdaa2..811f07cb55 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response\Model\None; use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\Attribute; use Appwrite\Utopia\Response\Model\BaseList; +use Appwrite\Utopia\Response\Model\BucketsUsage; use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\CollectionUsage; use Appwrite\Utopia\Response\Model\Continent; @@ -45,6 +46,7 @@ use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last +use Appwrite\Utopia\Response\Model\StorageUsage; use stdClass; /** @@ -88,6 +90,8 @@ class Response extends SwooleResponse const MODEL_FILE = 'file'; const MODEL_FILE_LIST = 'fileList'; const MODEL_BUCKET = 'bucket'; // - Missing + const MODEL_BUCKETS_USAGE = 'bucketsUsage'; + const MODEL_STORAGE_USAGE = 'storageUsage'; // Locale const MODEL_LOCALE = 'locale'; @@ -198,6 +202,8 @@ class Response extends SwooleResponse ->setModel(new JWT()) ->setModel(new Locale()) ->setModel(new File()) + ->setModel(new StorageUsage()) + ->setModel(new BucketsUsage()) ->setModel(new Team()) ->setModel(new Membership()) ->setModel(new Func()) diff --git a/src/Appwrite/Utopia/Response/Model/BucketsUsage.php b/src/Appwrite/Utopia/Response/Model/BucketsUsage.php new file mode 100644 index 0000000000..0fb923e147 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/BucketsUsage.php @@ -0,0 +1,70 @@ +addRule('range', [ + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', + ]) + ->addRule('files.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for files created.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('files.read', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for files read.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('files.update', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for files updated.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('files.delete', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for files deleted.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'BucketsUsage'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_BUCKETS_USAGE; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/CollectionUsage.php b/src/Appwrite/Utopia/Response/Model/CollectionUsage.php index 2281b3d032..a0b0a51c9b 100644 --- a/src/Appwrite/Utopia/Response/Model/CollectionUsage.php +++ b/src/Appwrite/Utopia/Response/Model/CollectionUsage.php @@ -12,10 +12,10 @@ class CollectionUsage extends Model { $this ->addRule('range', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'The value of this metric at a timestamp.', - 'default' => 0, - 'example' => 1, + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', ]) ->addRule('documents.count', [ 'type' => Response::MODEL_METRIC_LIST, diff --git a/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php b/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php index 58d9575d70..1c24625004 100644 --- a/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php +++ b/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php @@ -12,10 +12,10 @@ class DatabaseUsage extends Model { $this ->addRule('range', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'The value of this metric at a timestamp.', - 'default' => 0, - 'example' => 1, + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', ]) ->addRule('documents.count', [ 'type' => Response::MODEL_METRIC_LIST, diff --git a/src/Appwrite/Utopia/Response/Model/StorageUsage.php b/src/Appwrite/Utopia/Response/Model/StorageUsage.php new file mode 100644 index 0000000000..e26e0d4b3d --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/StorageUsage.php @@ -0,0 +1,56 @@ +addRule('range', [ + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', + ]) + ->addRule('storage', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for the occupied storage size.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('files', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for total number of files.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'StorageUsage'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_STORAGE_USAGE; + } +} \ No newline at end of file From b893190ce52c7859bcf2d22fb17fefe4e5f8b275 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 27 Aug 2021 00:23:55 +0530 Subject: [PATCH 17/35] feat(usage): added response models for users API --- app/controllers/api/users.php | 77 ++++------------ src/Appwrite/Utopia/Response.php | 3 + .../Utopia/Response/Model/UsersUsage.php | 91 +++++++++++++++++++ 3 files changed, 114 insertions(+), 57 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/UsersUsage.php diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 6d82c4f472..5a0cb03209 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -18,6 +18,7 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Validator\UID; use DeviceDetector\DeviceDetector; use Appwrite\Database\Validator\CustomId; +use Appwrite\Utopia\Response\Model; use Utopia\Database\Database; use Utopia\Database\Query; @@ -616,6 +617,9 @@ App::get('/v1/users/usage') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USERS_USAGE) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') @@ -624,11 +628,11 @@ App::get('/v1/users/usage') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $stats = []; + $usage = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '30m', + 'period' => '15m', 'limit' => 48, ], '7d' => [ @@ -646,8 +650,8 @@ App::get('/v1/users/usage') ]; Authorization::disable(); - $metrics = [ + "users.count", "users.create", "users.read", "users.update", @@ -656,6 +660,7 @@ App::get('/v1/users/usage') "users.sessions.delete" ]; + $stats = []; foreach ($metrics as $metric) { $requestDocs = $dbForInternal->find('stats', [ new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), @@ -672,63 +677,21 @@ App::get('/v1/users/usage') $stats[$metric] = array_reverse($stats[$metric]); } - - $usersCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['users.count'])], 0, ['time'], [Database::ORDER_DESC]); - $usersTotal = $usersCount ? $usersCount->getAttribute('value', 0) : 0; - + Authorization::reset(); - $create = $stats["users.create"] ?? []; - $read = $stats["users.read"] ?? []; - $update = $stats["users.update"] ?? []; - $delete = $stats["users.delete"] ?? []; - $sessionsCreate = $stats["users.sessions.create"] ?? []; - $sessionsDelete = $stats["users.sessions.delete"] ?? []; - - $response->json([ + $usage = new Document([ 'range' => $range, - 'users' => [ - 'data' => [], - 'total' => $usersTotal, - ], - 'create' => [ - 'data' => $create, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $create)), - ], - 'read' => [ - 'data' => $read, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $read)), - ], - 'update' => [ - 'data' => $update, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $update)), - ], - 'delete' => [ - 'data' => $delete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $delete)), - ], - 'sessionsCreate' => [ - 'data' => $sessionsCreate, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $sessionsCreate)), - ], - 'sessionsDelete' => [ - 'data' => $sessionsDelete, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $sessionsDelete)), - ] + 'users.count' => $stats["users.count"], + 'users.create' => $stats["users.create"], + 'users.read' => $stats["users.read"], + 'users.update' => $stats["users.update"], + 'users.delete' => $stats["users.delete"], + 'sessions.create' => $stats["users.sessions.create"], + 'sessions.delete' => $stats["users.sessions.delete"] ]); - } else { - $response->json([]); + } + + $response->dynamic($usage, Response::MODEL_USERS_USAGE); }); \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 811f07cb55..728e52b9e0 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -47,6 +47,7 @@ use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last use Appwrite\Utopia\Response\Model\StorageUsage; +use Appwrite\Utopia\Response\Model\UsersUsage; use stdClass; /** @@ -80,6 +81,7 @@ class Response extends SwooleResponse // Users const MODEL_USER = 'user'; const MODEL_USER_LIST = 'userList'; + const MODEL_USERS_USAGE = 'usersUsage'; const MODEL_SESSION = 'session'; const MODEL_SESSION_LIST = 'sessionList'; const MODEL_TOKEN = 'token'; @@ -196,6 +198,7 @@ class Response extends SwooleResponse ->setModel(new CollectionUsage()) ->setModel(new Log()) ->setModel(new User()) + ->setModel(new UsersUsage()) ->setModel(new Preferences()) ->setModel(new Session()) ->setModel(new Token()) diff --git a/src/Appwrite/Utopia/Response/Model/UsersUsage.php b/src/Appwrite/Utopia/Response/Model/UsersUsage.php new file mode 100644 index 0000000000..bb3db30b26 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/UsersUsage.php @@ -0,0 +1,91 @@ +addRule('range', [ + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', + ]) + ->addRule('users.count', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for total number of users.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('users.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for users created.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('users.read', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for users read.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('users.update', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for users updated.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('users.delete', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for users deleted.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('sessions.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for sessions created.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('sessions.delete', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for sessions deleted.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'UsersUsage'; + } + + /** + * Get Type + * + * @return string + */ + public function getType():string + { + return Response::MODEL_USERS_USAGE; + } +} \ No newline at end of file From 1e30cdba4b8270f4ec9869ba576e32d574981dae Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 27 Aug 2021 00:42:36 +0530 Subject: [PATCH 18/35] feat(usage): added response models for functions API --- app/controllers/api/functions.php | 165 ++---------------- src/Appwrite/Utopia/Response.php | 3 + .../Utopia/Response/Model/FunctionsUsage.php | 63 +++++++ 3 files changed, 84 insertions(+), 147 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/FunctionsUsage.php diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index ee3088ce18..9359d267f5 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -146,6 +146,9 @@ App::get('/v1/functions/:functionId/usage') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_FUNCTIONS_USAGE) ->param('functionId', '', new UID(), 'Function unique ID.') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true) ->inject('response') @@ -164,130 +167,11 @@ App::get('/v1/functions/:functionId/usage') throw new Exception('Function not found', 404); } + $usage = []; if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')), - 'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')), - 'group' => '30m', - ], - '7d' => [ - 'start' => DateTime::createFromFormat('U', \strtotime('-7 days')), - 'end' => DateTime::createFromFormat('U', \strtotime('now')), - 'group' => '1d', - ], - '30d' => [ - 'start' => DateTime::createFromFormat('U', \strtotime('-30 days')), - 'end' => DateTime::createFromFormat('U', \strtotime('now')), - 'group' => '1d', - ], - '90d' => [ - 'start' => DateTime::createFromFormat('U', \strtotime('-90 days')), - 'end' => DateTime::createFromFormat('U', \strtotime('now')), - 'group' => '1d', - ], - ]; - - $client = $register->get('influxdb'); - - $executions = []; - $failures = []; - $compute = []; - - if ($client) { - $start = $period[$range]['start']->format(DateTime::RFC3339); - $end = $period[$range]['end']->format(DateTime::RFC3339); - $database = $client->selectDB('telegraf'); - - // Executions - $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)'); - $points = $result->getPoints(); - - foreach ($points as $point) { - $executions[] = [ - 'value' => (!empty($point['value'])) ? $point['value'] : 0, - 'date' => \strtotime($point['time']), - ]; - } - - // Failures - $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' AND "functionStatus"=\'failed\' GROUP BY time('.$period[$range]['group'].') FILL(null)'); - $points = $result->getPoints(); - - foreach ($points as $point) { - $failures[] = [ - 'value' => (!empty($point['value'])) ? $point['value'] : 0, - 'date' => \strtotime($point['time']), - ]; - } - - // Compute - $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_time" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)'); - $points = $result->getPoints(); - - foreach ($points as $point) { - $compute[] = [ - 'value' => round((!empty($point['value'])) ? $point['value'] / 1000 : 0, 2), // minutes - 'date' => \strtotime($point['time']), - ]; - } - } - - $response->json([ - 'range' => $range, - 'executions' => [ - 'data' => $executions, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $executions)), - ], - 'failures' => [ - 'data' => $failures, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $failures)), - ], - 'compute' => [ - 'data' => $compute, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $compute)), - ], - ]); - } else { - $response->json([]); - } - }); - -App::get('/v1/functions/:functionId/usage2') - ->desc('Get Function Usage') - ->groups(['api', 'functions']) - ->label('scope', 'functions.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'functions') - ->label('sdk.method', 'getUsage') - ->param('functionId', '', new UID(), 'Function unique ID.') - ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true) - ->inject('response') - ->inject('project') - ->inject('dbForInternal') - ->inject('register') - ->action(function ($functionId, $range, $response, $project, $dbForInternal, $register) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForInternal */ - /** @var Utopia\Registry\Registry $register */ - - $function = $dbForInternal->getDocument('functions', $functionId); - - if ($function->isEmpty()) { - throw new Exception('Function not found', 404); - } - - if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $period = [ - '24h' => [ - 'period' => '30m', + 'period' => '15m', 'limit' => 48, ], '7d' => [ @@ -306,7 +190,13 @@ App::get('/v1/functions/:functionId/usage2') Authorization::disable(); - $metrics = ["functions.$functionId.executions", "functions.$functionId.failures", "functions.$functionId.compute"]; + $metrics = [ + "functions.$functionId.executions", + "functions.$functionId.failures", + "functions.$functionId.compute" + ]; + + $stats = []; foreach ($metrics as $metric) { $requestDocs = $dbForInternal->find('stats', [ new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), @@ -325,35 +215,16 @@ App::get('/v1/functions/:functionId/usage2') } Authorization::reset(); - - $executions = $stats["functions.$functionId.executions"] ?? []; - $failures = $stats["functions.$functionId.failures"] ?? []; - $compute = $stats["functions.$functionId.compute"] ?? []; - $response->json([ + $usage = new Document([ 'range' => $range, - 'executions' => [ - 'data' => $executions, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $executions)), - ], - 'failures' => [ - 'data' => $failures, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $failures)), - ], - 'compute' => [ - 'data' => $compute, - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $compute)), - ], + 'functions.executions' => $stats["functions.$functionId.executions"], + 'functions.failures' => $stats["functions.$functionId.failures"], + 'functions.compute' => $stats["functions.$functionId.compute"] ]); - } else { - $response->json([]); } + + $response->dynamic($usage, Response::MODEL_FUNCTIONS_USAGE); }); App::put('/v1/functions/:functionId') diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 728e52b9e0..a1d0d404ed 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -26,6 +26,7 @@ use Appwrite\Utopia\Response\Model\ErrorDev; use Appwrite\Utopia\Response\Model\Execution; use Appwrite\Utopia\Response\Model\File; use Appwrite\Utopia\Response\Model\Func; +use Appwrite\Utopia\Response\Model\FunctionsUsage; use Appwrite\Utopia\Response\Model\Index; use Appwrite\Utopia\Response\Model\JWT; use Appwrite\Utopia\Response\Model\Key; @@ -117,6 +118,7 @@ class Response extends SwooleResponse // Functions const MODEL_FUNCTION = 'function'; const MODEL_FUNCTION_LIST = 'functionList'; + const MODEL_FUNCTIONS_USAGE = 'functionsUsage'; const MODEL_TAG = 'tag'; const MODEL_TAG_LIST = 'tagList'; const MODEL_EXECUTION = 'execution'; @@ -210,6 +212,7 @@ class Response extends SwooleResponse ->setModel(new Team()) ->setModel(new Membership()) ->setModel(new Func()) + ->setModel(new FunctionsUsage()) ->setModel(new Tag()) ->setModel(new Execution()) ->setModel(new Project()) diff --git a/src/Appwrite/Utopia/Response/Model/FunctionsUsage.php b/src/Appwrite/Utopia/Response/Model/FunctionsUsage.php new file mode 100644 index 0000000000..60f017705e --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/FunctionsUsage.php @@ -0,0 +1,63 @@ +addRule('range', [ + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', + ]) + ->addRule('functions.executions', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for function executions.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('functions.failures', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for function execution failures.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('functions.compute', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for function execution duration.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'FunctionsUsage'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_FUNCTIONS_USAGE; + } +} \ No newline at end of file From 038aeb01fb39d41b3886c81452174f83f4c0f788 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 27 Aug 2021 01:01:37 +0530 Subject: [PATCH 19/35] feat(usage): added response models for projects API --- app/controllers/api/projects.php | 85 +++++++---------- src/Appwrite/Utopia/Response.php | 3 + .../Utopia/Response/Model/ProjectUsage.php | 91 +++++++++++++++++++ 3 files changed, 125 insertions(+), 54 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/ProjectUsage.php diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index d4a8f86846..e8551c29ce 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -223,6 +223,9 @@ App::get('/v1/projects/:projectId/usage') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'getUsage') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_PROJECT_USAGE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') @@ -241,11 +244,11 @@ App::get('/v1/projects/:projectId/usage') throw new Exception('Project not found', 404); } - $stats = []; + $usage = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '30m', + 'period' => '15m', 'limit' => 48, ], '7d' => [ @@ -266,7 +269,17 @@ App::get('/v1/projects/:projectId/usage') Authorization::disable(); - $metrics = ['requests', 'network', 'executions']; + $metrics = [ + 'requests', + 'network', + 'executions', + 'users.count', + 'database.documents.count', + 'database.collections.count', + 'storage.total' + ]; + + $stats = []; foreach ($metrics as $metric) { $requestDocs = $dbForInternal->find('stats', [ new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), @@ -282,59 +295,23 @@ App::get('/v1/projects/:projectId/usage') } $stats[$metric] = array_reverse($stats[$metric]); - } + } + + Authorization::reset(); + + $usage = new Document([ + 'range' => $range, + 'requests' => $stats['requests'], + 'network' => $stats['network'], + 'functions' => $stats['executions'], + 'documents' => $stats['database.documents.count'], + 'collections' => $stats['database.collections.count'], + 'users' => $stats['users.count'], + 'storage' => $stats['storage.total'] + ]); } - $usersCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['users.count'])], 0, ['time'], [Database::ORDER_DESC]); - $usersTotal = $usersCount ? $usersCount->getAttribute('value', 0) : 0; - - $documentsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.documents.count'])], 0, ['time'], [Database::ORDER_DESC]); - $documentsTotal = $documentsCount ? $documentsCount->getAttribute('value', 0) : 0; - - $collectionsCount = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['database.collections.count'])], 0, ['time'], [Database::ORDER_DESC]); - $collectionsTotal = $collectionsCount ? $collectionsCount->getAttribute('value', 0) : 0; - - $storageTotal = $dbForInternal->findOne('stats', [new Query('metric', Query::TYPE_EQUAL, ['storage.total'])], 0, ['time'], [Database::ORDER_DESC]); - $storage = $storageTotal ? $storageTotal->getAttribute('value', 0) : 0; - - Authorization::reset(); - - $response->json([ - 'range' => $range, - 'requests' => [ - 'data' => $stats['requests'] ?? [], - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $stats['requests'] ?? [])), - ], - 'network' => [ - 'data' => \array_map(function ($value) {return ['value' => \round($value['value'] / 1000000, 2), 'date' => $value['date']];}, $stats['network'] ?? []), // convert bytes to mb - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $stats['network'] ?? [])), - ], - 'functions' => [ - 'data' => $stats['executions'] ?? [], - 'total' => \array_sum(\array_map(function ($item) { - return $item['value']; - }, $stats['executions'] ?? [])), - ], - 'documents' => [ - 'data' => [], - 'total' => $documentsTotal, - ], - 'collections' => [ - 'data' => [], - 'total' => $collectionsTotal, - ], - 'users' => [ - 'data' => [], - 'total' => $usersTotal, - ], - 'storage' => [ - 'total' => $storage, - ], - ]); + $response->dynamic($usage, Response::MODEL_PROJECT_USAGE); }); App::patch('/v1/projects/:projectId') diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index a1d0d404ed..c87c924807 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -47,6 +47,7 @@ use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last +use Appwrite\Utopia\Response\Model\ProjectUsage; use Appwrite\Utopia\Response\Model\StorageUsage; use Appwrite\Utopia\Response\Model\UsersUsage; use stdClass; @@ -127,6 +128,7 @@ class Response extends SwooleResponse // Project const MODEL_PROJECT = 'project'; const MODEL_PROJECT_LIST = 'projectList'; + const MODEL_PROJECT_USAGE = 'projectUsage'; const MODEL_WEBHOOK = 'webhook'; const MODEL_WEBHOOK_LIST = 'webhookList'; const MODEL_KEY = 'key'; @@ -216,6 +218,7 @@ class Response extends SwooleResponse ->setModel(new Tag()) ->setModel(new Execution()) ->setModel(new Project()) + ->setModel(new ProjectUsage()) ->setModel(new Webhook()) ->setModel(new Key()) ->setModel(new Domain()) diff --git a/src/Appwrite/Utopia/Response/Model/ProjectUsage.php b/src/Appwrite/Utopia/Response/Model/ProjectUsage.php new file mode 100644 index 0000000000..deee3b78b1 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/ProjectUsage.php @@ -0,0 +1,91 @@ +addRule('range', [ + 'type' => self::TYPE_STRING, + 'description' => 'The time range of the usage stats.', + 'default' => '', + 'example' => '30d', + ]) + ->addRule('requests', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for number of requests.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('network', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for consumed bandwidth.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('functions', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for function executions.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('documents', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for number of documents.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('collections', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for number of collections.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('users', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for number of users.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ->addRule('functions', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for the occupied storage size.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'ProjectUsage'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_PROJECT_USAGE; + } +} \ No newline at end of file From ad99f8bd2404a2f505125c0998b8bdab32ba8834 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 27 Aug 2021 22:15:08 +0530 Subject: [PATCH 20/35] feat(usage): rename all usage response models --- app/controllers/api/database.php | 1 - docker-compose.yml | 2 +- src/Appwrite/Utopia/Response.php | 41 ++++++++++--------- .../{BucketsUsage.php => UsageBuckets.php} | 8 ++-- ...ollectionUsage.php => UsageCollection.php} | 8 ++-- .../{DatabaseUsage.php => UsageDatabase.php} | 8 ++-- ...{FunctionsUsage.php => UsageFunctions.php} | 8 ++-- .../{ProjectUsage.php => UsageProject.php} | 8 ++-- .../{StorageUsage.php => UsageStorage.php} | 6 +-- .../Model/{UsersUsage.php => UsageUsers.php} | 6 +-- 10 files changed, 48 insertions(+), 48 deletions(-) rename src/Appwrite/Utopia/Response/Model/{BucketsUsage.php => UsageBuckets.php} (93%) rename src/Appwrite/Utopia/Response/Model/{CollectionUsage.php => UsageCollection.php} (93%) rename src/Appwrite/Utopia/Response/Model/{DatabaseUsage.php => UsageDatabase.php} (96%) rename src/Appwrite/Utopia/Response/Model/{FunctionsUsage.php => UsageFunctions.php} (91%) rename src/Appwrite/Utopia/Response/Model/{ProjectUsage.php => UsageProject.php} (95%) rename src/Appwrite/Utopia/Response/Model/{StorageUsage.php => UsageStorage.php} (92%) rename src/Appwrite/Utopia/Response/Model/{UsersUsage.php => UsageUsers.php} (96%) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index a18c2e885c..86e8304d8b 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1376,7 +1376,6 @@ App::post('/v1/database/collections/:collectionId/documents') $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user - var_dump($collectionId); try { $document = $dbForExternal->createDocument($collectionId, new Document($data)); } diff --git a/docker-compose.yml b/docker-compose.yml index 910c07f094..12bc51b802 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,7 +63,7 @@ services: - ./psalm.xml:/usr/src/code/psalm.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database + # - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index c87c924807..0c4c6bf0aa 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -12,13 +12,10 @@ use Appwrite\Utopia\Response\Model\None; use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\Attribute; use Appwrite\Utopia\Response\Model\BaseList; -use Appwrite\Utopia\Response\Model\BucketsUsage; use Appwrite\Utopia\Response\Model\Collection; -use Appwrite\Utopia\Response\Model\CollectionUsage; use Appwrite\Utopia\Response\Model\Continent; use Appwrite\Utopia\Response\Model\Country; use Appwrite\Utopia\Response\Model\Currency; -use Appwrite\Utopia\Response\Model\DatabaseUsage; use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Domain; use Appwrite\Utopia\Response\Model\Error; @@ -26,7 +23,6 @@ use Appwrite\Utopia\Response\Model\ErrorDev; use Appwrite\Utopia\Response\Model\Execution; use Appwrite\Utopia\Response\Model\File; use Appwrite\Utopia\Response\Model\Func; -use Appwrite\Utopia\Response\Model\FunctionsUsage; use Appwrite\Utopia\Response\Model\Index; use Appwrite\Utopia\Response\Model\JWT; use Appwrite\Utopia\Response\Model\Key; @@ -47,8 +43,13 @@ use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last -use Appwrite\Utopia\Response\Model\ProjectUsage; -use Appwrite\Utopia\Response\Model\StorageUsage; +use Appwrite\Utopia\Response\Model\UsageBuckets; +use Appwrite\Utopia\Response\Model\UsageCollection; +use Appwrite\Utopia\Response\Model\UsageDatabase; +use Appwrite\Utopia\Response\Model\UsageFunctions; +use Appwrite\Utopia\Response\Model\UsageProject; +use Appwrite\Utopia\Response\Model\UsageStorage; +use Appwrite\Utopia\Response\Model\UsageUsers; use Appwrite\Utopia\Response\Model\UsersUsage; use stdClass; @@ -67,6 +68,13 @@ class Response extends SwooleResponse const MODEL_METRIC_LIST = 'metricList'; const MODEL_ERROR_DEV = 'errorDev'; const MODEL_BASE_LIST = 'baseList'; + const MODEL_USAGE_DATABASE = 'usageDatabase'; + const MODEL_USAGE_COLLECTION = 'usageCollection'; + const MODEL_USAGE_USERS = 'usageUsers'; + const MODEL_USAGE_BUCKETS = 'usageBuckets'; + const MODEL_USAGE_STORAGE = 'usageStorage'; + const MODEL_USAGE_FUNCTIONS = 'usageFunctions'; + const MODEL_USAGE_PROJECT = 'usageProject'; // Database const MODEL_COLLECTION = 'collection'; @@ -77,13 +85,10 @@ class Response extends SwooleResponse const MODEL_INDEX_LIST = 'indexList'; const MODEL_DOCUMENT = 'document'; const MODEL_DOCUMENT_LIST = 'documentList'; - const MODEL_DATABASE_USAGE = 'databaseUsage'; - const MODEL_COLLECTION_USAGE = 'collectionUsage'; // Users const MODEL_USER = 'user'; const MODEL_USER_LIST = 'userList'; - const MODEL_USERS_USAGE = 'usersUsage'; const MODEL_SESSION = 'session'; const MODEL_SESSION_LIST = 'sessionList'; const MODEL_TOKEN = 'token'; @@ -94,8 +99,6 @@ class Response extends SwooleResponse const MODEL_FILE = 'file'; const MODEL_FILE_LIST = 'fileList'; const MODEL_BUCKET = 'bucket'; // - Missing - const MODEL_BUCKETS_USAGE = 'bucketsUsage'; - const MODEL_STORAGE_USAGE = 'storageUsage'; // Locale const MODEL_LOCALE = 'locale'; @@ -119,7 +122,6 @@ class Response extends SwooleResponse // Functions const MODEL_FUNCTION = 'function'; const MODEL_FUNCTION_LIST = 'functionList'; - const MODEL_FUNCTIONS_USAGE = 'functionsUsage'; const MODEL_TAG = 'tag'; const MODEL_TAG_LIST = 'tagList'; const MODEL_EXECUTION = 'execution'; @@ -128,7 +130,6 @@ class Response extends SwooleResponse // Project const MODEL_PROJECT = 'project'; const MODEL_PROJECT_LIST = 'projectList'; - const MODEL_PROJECT_USAGE = 'projectUsage'; const MODEL_WEBHOOK = 'webhook'; const MODEL_WEBHOOK_LIST = 'webhookList'; const MODEL_KEY = 'key'; @@ -198,27 +199,20 @@ class Response extends SwooleResponse ->setModel(new Attribute()) ->setModel(new Index()) ->setModel(new ModelDocument()) - ->setModel(new DatabaseUsage()) - ->setModel(new CollectionUsage()) ->setModel(new Log()) ->setModel(new User()) - ->setModel(new UsersUsage()) ->setModel(new Preferences()) ->setModel(new Session()) ->setModel(new Token()) ->setModel(new JWT()) ->setModel(new Locale()) ->setModel(new File()) - ->setModel(new StorageUsage()) - ->setModel(new BucketsUsage()) ->setModel(new Team()) ->setModel(new Membership()) ->setModel(new Func()) - ->setModel(new FunctionsUsage()) ->setModel(new Tag()) ->setModel(new Execution()) ->setModel(new Project()) - ->setModel(new ProjectUsage()) ->setModel(new Webhook()) ->setModel(new Key()) ->setModel(new Domain()) @@ -228,6 +222,13 @@ class Response extends SwooleResponse ->setModel(new Language()) ->setModel(new Currency()) ->setModel(new Phone()) + ->setModel(new UsageDatabase()) + ->setModel(new UsageCollection()) + ->setModel(new UsageUsers()) + ->setModel(new UsageStorage()) + ->setModel(new UsageBuckets()) + ->setModel(new UsageFunctions()) + ->setModel(new UsageProject()) // Verification // Recovery // Tests (keep last) diff --git a/src/Appwrite/Utopia/Response/Model/BucketsUsage.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php similarity index 93% rename from src/Appwrite/Utopia/Response/Model/BucketsUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageBuckets.php index 0fb923e147..11fb2e2f8c 100644 --- a/src/Appwrite/Utopia/Response/Model/BucketsUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class BucketsUsage extends Model +class UsageBuckets extends Model { public function __construct() { @@ -55,16 +55,16 @@ class BucketsUsage extends Model */ public function getName():string { - return 'BucketsUsage'; + return 'UsageBuckets'; } /** - * Get Collection + * Get Type * * @return string */ public function getType():string { - return Response::MODEL_BUCKETS_USAGE; + return Response::MODEL_USAGE_BUCKETS; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/CollectionUsage.php b/src/Appwrite/Utopia/Response/Model/UsageCollection.php similarity index 93% rename from src/Appwrite/Utopia/Response/Model/CollectionUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageCollection.php index a0b0a51c9b..f2815831a2 100644 --- a/src/Appwrite/Utopia/Response/Model/CollectionUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageCollection.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class CollectionUsage extends Model +class UsageCollection extends Model { public function __construct() { @@ -62,16 +62,16 @@ class CollectionUsage extends Model */ public function getName():string { - return 'CollectionUsage'; + return 'UsageCollection'; } /** - * Get Collection + * Get Type * * @return string */ public function getType():string { - return Response::MODEL_COLLECTION_USAGE; + return Response::MODEL_USAGE_COLLECTION; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php similarity index 96% rename from src/Appwrite/Utopia/Response/Model/DatabaseUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageDatabase.php index 1c24625004..2ebe0b64c0 100644 --- a/src/Appwrite/Utopia/Response/Model/DatabaseUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class DatabaseUsage extends Model +class UsageDatabase extends Model { public function __construct() { @@ -97,16 +97,16 @@ class DatabaseUsage extends Model */ public function getName():string { - return 'DatabaseUsage'; + return 'UsageDatabase'; } /** - * Get Collection + * Get Type * * @return string */ public function getType():string { - return Response::MODEL_DATABASE_USAGE; + return Response::MODEL_USAGE_DATABASE; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/FunctionsUsage.php b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php similarity index 91% rename from src/Appwrite/Utopia/Response/Model/FunctionsUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageFunctions.php index 60f017705e..625bdab71c 100644 --- a/src/Appwrite/Utopia/Response/Model/FunctionsUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class FunctionsUsage extends Model +class UsageFunctions extends Model { public function __construct() { @@ -48,16 +48,16 @@ class FunctionsUsage extends Model */ public function getName():string { - return 'FunctionsUsage'; + return 'UsageFunctions'; } /** - * Get Collection + * Get Type * * @return string */ public function getType():string { - return Response::MODEL_FUNCTIONS_USAGE; + return Response::MODEL_USAGE_FUNCTIONS; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/ProjectUsage.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php similarity index 95% rename from src/Appwrite/Utopia/Response/Model/ProjectUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageProject.php index deee3b78b1..95bb3cd42d 100644 --- a/src/Appwrite/Utopia/Response/Model/ProjectUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class ProjectUsage extends Model +class UsageProject extends Model { public function __construct() { @@ -76,16 +76,16 @@ class ProjectUsage extends Model */ public function getName():string { - return 'ProjectUsage'; + return 'UsageProject'; } /** - * Get Collection + * Get Type * * @return string */ public function getType():string { - return Response::MODEL_PROJECT_USAGE; + return Response::MODEL_USAGE_PROJECT; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/StorageUsage.php b/src/Appwrite/Utopia/Response/Model/UsageStorage.php similarity index 92% rename from src/Appwrite/Utopia/Response/Model/StorageUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageStorage.php index e26e0d4b3d..66eaed602c 100644 --- a/src/Appwrite/Utopia/Response/Model/StorageUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageStorage.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class StorageUsage extends Model +class UsageStorage extends Model { public function __construct() { @@ -45,12 +45,12 @@ class StorageUsage extends Model } /** - * Get Collection + * Get Type * * @return string */ public function getType():string { - return Response::MODEL_STORAGE_USAGE; + return Response::MODEL_USAGE_STORAGE; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/UsersUsage.php b/src/Appwrite/Utopia/Response/Model/UsageUsers.php similarity index 96% rename from src/Appwrite/Utopia/Response/Model/UsersUsage.php rename to src/Appwrite/Utopia/Response/Model/UsageUsers.php index bb3db30b26..db8056f5c6 100644 --- a/src/Appwrite/Utopia/Response/Model/UsersUsage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageUsers.php @@ -6,7 +6,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use stdClass; -class UsersUsage extends Model +class UsageUsers extends Model { public function __construct() { @@ -76,7 +76,7 @@ class UsersUsage extends Model */ public function getName():string { - return 'UsersUsage'; + return 'UsageUsers'; } /** @@ -86,6 +86,6 @@ class UsersUsage extends Model */ public function getType():string { - return Response::MODEL_USERS_USAGE; + return Response::MODEL_USAGE_USERS; } } \ No newline at end of file From 6835124af8f387f0df364991d39f0ef2e76db473 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 27 Aug 2021 23:04:43 +0530 Subject: [PATCH 21/35] feat(usage): rename all usage response models --- app/controllers/api/database.php | 8 ++++---- app/controllers/api/functions.php | 4 ++-- app/controllers/api/projects.php | 4 ++-- app/controllers/api/storage.php | 8 ++++---- app/controllers/api/users.php | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 86e8304d8b..1ff2e9b76e 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -254,7 +254,7 @@ App::get('/v1/database/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_DATABASE_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_DATABASE) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') @@ -335,7 +335,7 @@ App::get('/v1/database/usage') ]); } - $response->dynamic($usage, Response::MODEL_DATABASE_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_DATABASE); }); App::get('/v1/database/:collectionId/usage') @@ -347,7 +347,7 @@ App::get('/v1/database/:collectionId/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_COLLECTION_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_COLLECTION) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->param('collectionId', '', new UID(), 'Collection unique ID.') ->inject('response') @@ -425,7 +425,7 @@ App::get('/v1/database/:collectionId/usage') ]); } - $response->dynamic($usage, Response::MODEL_COLLECTION_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_COLLECTION); }); App::get('/v1/database/collections/:collectionId/logs') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 9359d267f5..dec4fa92bf 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -148,7 +148,7 @@ App::get('/v1/functions/:functionId/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_FUNCTIONS_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS) ->param('functionId', '', new UID(), 'Function unique ID.') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true) ->inject('response') @@ -224,7 +224,7 @@ App::get('/v1/functions/:functionId/usage') ]); } - $response->dynamic($usage, Response::MODEL_FUNCTIONS_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_FUNCTIONS); }); App::put('/v1/functions/:functionId') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index e8551c29ce..2d8311845a 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -225,7 +225,7 @@ App::get('/v1/projects/:projectId/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_PROJECT_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') @@ -311,7 +311,7 @@ App::get('/v1/projects/:projectId/usage') ]); } - $response->dynamic($usage, Response::MODEL_PROJECT_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_PROJECT); }); App::patch('/v1/projects/:projectId') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 69a7c18708..c3804b2f60 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -653,7 +653,7 @@ App::get('/v1/storage/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_STORAGE_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_STORAGE) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') @@ -713,7 +713,7 @@ App::get('/v1/storage/usage') ]); } - $response->dynamic($usage, Response::MODEL_STORAGE_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_STORAGE); }); App::get('/v1/storage/:bucketId/usage') @@ -725,7 +725,7 @@ App::get('/v1/storage/:bucketId/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_BUCKETS_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_BUCKETS) ->param('bucketId', '', new UID(), 'Bucket unique ID.') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') @@ -792,5 +792,5 @@ App::get('/v1/storage/:bucketId/usage') ]); } - $response->dynamic($usage, Response::MODEL_BUCKETS_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_BUCKETS); }); \ No newline at end of file diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 5a0cb03209..1895098bf2 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -619,7 +619,7 @@ App::get('/v1/users/usage') ->label('sdk.method', 'getUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_USERS_USAGE) + ->label('sdk.response.model', Response::MODEL_USAGE_USERS) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForInternal') @@ -693,5 +693,5 @@ App::get('/v1/users/usage') } - $response->dynamic($usage, Response::MODEL_USERS_USAGE); + $response->dynamic($usage, Response::MODEL_USAGE_USERS); }); \ No newline at end of file From 1b074a3c38b6aa49b85c8a26dcfaa19952ea686b Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 28 Aug 2021 21:38:50 +0530 Subject: [PATCH 22/35] feat(model): added better description for rule --- src/Appwrite/Utopia/Response/Model/UsageProject.php | 4 ++-- src/Appwrite/Utopia/Response/Model/UsageStorage.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 95bb3cd42d..c44af4cf6d 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -59,9 +59,9 @@ class UsageProject extends Model 'example' => new stdClass, 'array' => true ]) - ->addRule('functions', [ + ->addRule('storage', [ 'type' => Response::MODEL_METRIC_LIST, - 'description' => 'Aggregated stats for the occupied storage size.', + 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], 'example' => new stdClass, 'array' => true diff --git a/src/Appwrite/Utopia/Response/Model/UsageStorage.php b/src/Appwrite/Utopia/Response/Model/UsageStorage.php index 66eaed602c..4462a219a9 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageStorage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageStorage.php @@ -19,7 +19,7 @@ class UsageStorage extends Model ]) ->addRule('storage', [ 'type' => Response::MODEL_METRIC_LIST, - 'description' => 'Aggregated stats for the occupied storage size.', + 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], 'example' => new stdClass, 'array' => true From f7d657cc38ff6418f002fb5a7f11e9dec914a388 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 28 Aug 2021 21:55:48 +0530 Subject: [PATCH 23/35] feat(model): use the new authorization skip methods --- app/controllers/api/database.php | 73 +++++++++++++++---------------- app/controllers/api/functions.php | 36 +++++++-------- app/controllers/api/projects.php | 36 +++++++-------- app/controllers/api/storage.php | 68 ++++++++++++++-------------- app/controllers/api/users.php | 37 ++++++++-------- 5 files changed, 122 insertions(+), 128 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 3b3e477d90..e2af571bf7 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1,6 +1,5 @@ find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - $stats[$metric] = array_reverse($stats[$metric]); - } - - Authorization::reset(); + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, @@ -387,8 +385,6 @@ App::get('/v1/database/:collectionId/usage') 'limit' => 90, ], ]; - - Authorization::disable(); $metrics = [ "database.collections.$collectionId.documents.count", @@ -399,23 +395,24 @@ App::get('/v1/database/:collectionId/usage') ]; $stats = []; - foreach ($metrics as $metric) { - $requestDocs = $dbForInternal->find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - $stats[$metric] = array_reverse($stats[$metric]); - } - - Authorization::reset(); + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index dec4fa92bf..fadc323832 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -188,8 +188,6 @@ App::get('/v1/functions/:functionId/usage') ], ]; - Authorization::disable(); - $metrics = [ "functions.$functionId.executions", "functions.$functionId.failures", @@ -197,24 +195,24 @@ App::get('/v1/functions/:functionId/usage') ]; $stats = []; - foreach ($metrics as $metric) { - $requestDocs = $dbForInternal->find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - $stats[$metric] = array_reverse($stats[$metric]); - } - - Authorization::reset(); + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2d8311845a..0d1c3b713a 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -267,8 +267,6 @@ App::get('/v1/projects/:projectId/usage') $dbForInternal->setNamespace('project_' . $projectId . '_internal'); - Authorization::disable(); - $metrics = [ 'requests', 'network', @@ -280,24 +278,24 @@ App::get('/v1/projects/:projectId/usage') ]; $stats = []; - foreach ($metrics as $metric) { - $requestDocs = $dbForInternal->find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - $stats[$metric] = array_reverse($stats[$metric]); - } - - Authorization::reset(); + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index c3804b2f60..a11d50b5e3 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -10,7 +10,7 @@ use Utopia\Validator\HexColor; use Utopia\Cache\Cache; use Utopia\Cache\Adapter\Filesystem; use Appwrite\ClamAV\Network; -use Appwrite\Database\Validator\Authorization; +use Utopia\Database\Validator\Authorization; use Appwrite\Database\Validator\CustomId; use Utopia\Database\Document; use Utopia\Database\Validator\UID; @@ -682,29 +682,30 @@ App::get('/v1/storage/usage') ], ]; - Authorization::disable(); - $metrics = [ "storage.total", "storage.files.count" ]; $stats = []; - foreach ($metrics as $metric) { - $requestDocs = $dbForInternal->find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - $stats[$metric] = array_reverse($stats[$metric]); - } + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, @@ -757,7 +758,6 @@ App::get('/v1/storage/:bucketId/usage') ], ]; - Authorization::disable(); $metrics = [ "storage.buckets.$bucketId.files.create", "storage.buckets.$bucketId.files.read", @@ -766,22 +766,24 @@ App::get('/v1/storage/:bucketId/usage') ]; $stats = []; - foreach ($metrics as $metric) { - $requestDocs = $dbForInternal->find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - $stats[$metric] = array_reverse($stats[$metric]); - } - Authorization::reset(); + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 1895098bf2..7541487ba0 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -2,7 +2,6 @@ use Appwrite\Auth\Auth; use Appwrite\Auth\Validator\Password; -use Appwrite\Database\Validator\Authorization; use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Exception; @@ -21,6 +20,7 @@ use Appwrite\Database\Validator\CustomId; use Appwrite\Utopia\Response\Model; use Utopia\Database\Database; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; App::post('/v1/users') ->desc('Create User') @@ -649,7 +649,6 @@ App::get('/v1/users/usage') ], ]; - Authorization::disable(); $metrics = [ "users.count", "users.create", @@ -661,24 +660,24 @@ App::get('/v1/users/usage') ]; $stats = []; - foreach ($metrics as $metric) { - $requestDocs = $dbForInternal->find('stats', [ - new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), - new Query('metric', Query::TYPE_EQUAL, [$metric]), - ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - $stats[$metric] = array_reverse($stats[$metric]); - } - - Authorization::reset(); + Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) { + foreach ($metrics as $metric) { + $requestDocs = $dbForInternal->find('stats', [ + new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]), + new Query('metric', Query::TYPE_EQUAL, [$metric]), + ], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]); + + $stats[$metric] = []; + foreach ($requestDocs as $requestDoc) { + $stats[$metric][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + $stats[$metric] = array_reverse($stats[$metric]); + } + }); $usage = new Document([ 'range' => $range, From a4e2aeb5e79615f544a3f4bbd2533021624e7adf Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 11:00:37 +0530 Subject: [PATCH 24/35] feat(usage): added provider metrics to endpoints --- app/controllers/api/users.php | 5 ++++- src/Appwrite/Utopia/Response/Model/UsageUsers.php | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 7541487ba0..7fcdffcc8b 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -621,10 +621,11 @@ App::get('/v1/users/usage') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USAGE_USERS) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) + ->param('provider', '', new WhiteList(['email', 'anonymous', 'oauth2-google', 'oauth2-apple'], true), 'Provider Name.', true) ->inject('response') ->inject('dbForInternal') ->inject('register') - ->action(function ($range, $response, $dbForInternal) { + ->action(function ($range, $provider, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ @@ -656,6 +657,7 @@ App::get('/v1/users/usage') "users.update", "users.delete", "users.sessions.create", + "users.sessions.$provider.create", "users.sessions.delete" ]; @@ -687,6 +689,7 @@ App::get('/v1/users/usage') 'users.update' => $stats["users.update"], 'users.delete' => $stats["users.delete"], 'sessions.create' => $stats["users.sessions.create"], + 'sessions.provider.create' => $stats["users.sessions.$provider.create"], 'sessions.delete' => $stats["users.sessions.delete"] ]); diff --git a/src/Appwrite/Utopia/Response/Model/UsageUsers.php b/src/Appwrite/Utopia/Response/Model/UsageUsers.php index db8056f5c6..913c4f33fe 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageUsers.php +++ b/src/Appwrite/Utopia/Response/Model/UsageUsers.php @@ -59,6 +59,13 @@ class UsageUsers extends Model 'example' => new stdClass, 'array' => true ]) + ->addRule('sessions.provider.create', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).', + 'default' => null, + 'example' => new stdClass, + 'array' => true + ]) ->addRule('sessions.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for sessions deleted.', From 58a5320cf11f04ccebd35e92a71b846bdb9bed15 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 13:20:52 +0530 Subject: [PATCH 25/35] feat(tests): added tests for database usage --- tests/e2e/Scopes/SideConsole.php | 23 +++++ .../Services/Database/DatabaseConsoleTest.php | 89 +++++++++++++++++ .../Functions/FunctionsConsoleTest.php | 97 +++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 tests/e2e/Scopes/SideConsole.php create mode 100644 tests/e2e/Services/Database/DatabaseConsoleTest.php create mode 100644 tests/e2e/Services/Functions/FunctionsConsoleTest.php diff --git a/tests/e2e/Scopes/SideConsole.php b/tests/e2e/Scopes/SideConsole.php new file mode 100644 index 0000000000..ada99a6e6a --- /dev/null +++ b/tests/e2e/Scopes/SideConsole.php @@ -0,0 +1,23 @@ + 'http://localhost', + 'cookie' => 'a_session_console='. $this->getRoot()['session'], + 'x-appwrite-mode' => 'admin' + ]; + } + + /** + * @return string + */ + public function getSide() + { + return 'console'; + } +} diff --git a/tests/e2e/Services/Database/DatabaseConsoleTest.php b/tests/e2e/Services/Database/DatabaseConsoleTest.php new file mode 100644 index 0000000000..0921d2c50a --- /dev/null +++ b/tests/e2e/Services/Database/DatabaseConsoleTest.php @@ -0,0 +1,89 @@ +client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'collectionId' => 'unique()', + 'name' => 'Movies', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $this->assertEquals($movies['headers']['status-code'], 201); + $this->assertEquals($movies['body']['name'], 'Movies'); + + return ['moviesId' => $movies['body']['$id']]; + } + + public function testGetDatabaseUsage() + { + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/database/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['documents.count']); + $this->assertIsArray($response['body']['collections.count']); + $this->assertIsArray($response['body']['documents.create']); + $this->assertIsArray($response['body']['documents.read']); + $this->assertIsArray($response['body']['documents.update']); + $this->assertIsArray($response['body']['documents.delete']); + $this->assertIsArray($response['body']['collections.create']); + $this->assertIsArray($response['body']['collections.read']); + $this->assertIsArray($response['body']['collections.update']); + $this->assertIsArray($response['body']['collections.delete']); + } + + + /** + * @depends testCreateCollection + */ + public function testGetCollectionUsage(array $data) + { + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['documents.count']); + $this->assertIsArray($response['body']['documents.create']); + $this->assertIsArray($response['body']['documents.read']); + $this->assertIsArray($response['body']['documents.update']); + $this->assertIsArray($response['body']['documents.delete']); + } +} \ No newline at end of file diff --git a/tests/e2e/Services/Functions/FunctionsConsoleTest.php b/tests/e2e/Services/Functions/FunctionsConsoleTest.php new file mode 100644 index 0000000000..99dd4d315d --- /dev/null +++ b/tests/e2e/Services/Functions/FunctionsConsoleTest.php @@ -0,0 +1,97 @@ + 'http://localhost', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + 'x-appwrite-mode' => 'admin' + ]; + } + + public function testCreateCollection():array + { + /** + * Test for SUCCESS + */ + $movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'collectionId' => 'unique()', + 'name' => 'Movies', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $this->assertEquals($movies['headers']['status-code'], 201); + $this->assertEquals($movies['body']['name'], 'Movies'); + + return ['moviesId' => $movies['body']['$id']]; + } + + public function testGetDatabaseUsage() + { + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/database/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['documents.count']); + $this->assertIsArray($response['body']['collections.count']); + $this->assertIsArray($response['body']['documents.create']); + $this->assertIsArray($response['body']['documents.read']); + $this->assertIsArray($response['body']['documents.update']); + $this->assertIsArray($response['body']['documents.delete']); + $this->assertIsArray($response['body']['collections.create']); + $this->assertIsArray($response['body']['collections.read']); + $this->assertIsArray($response['body']['collections.update']); + $this->assertIsArray($response['body']['collections.delete']); + } + + + /** + * @depends testCreateCollection + */ + public function testGetCollectionUsage(array $data) + { + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['documents.count']); + $this->assertIsArray($response['body']['documents.create']); + $this->assertIsArray($response['body']['documents.read']); + $this->assertIsArray($response['body']['documents.update']); + $this->assertIsArray($response['body']['documents.delete']); + } + +} \ No newline at end of file From 94a17e9ce06b65cd408cb7ff57e80426a8506b96 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 13:29:08 +0530 Subject: [PATCH 26/35] feat(tests): added tests for functions usage --- .../Services/Database/DatabaseConsoleTest.php | 36 +++++- .../Functions/FunctionsConsoleTest.php | 109 ++++++++---------- 2 files changed, 86 insertions(+), 59 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseConsoleTest.php b/tests/e2e/Services/Database/DatabaseConsoleTest.php index 0921d2c50a..f8d50188ce 100644 --- a/tests/e2e/Services/Database/DatabaseConsoleTest.php +++ b/tests/e2e/Services/Database/DatabaseConsoleTest.php @@ -36,6 +36,19 @@ class DatabaseConsoleTest extends Scope public function testGetDatabaseUsage() { + /** + * Test for FAILURE + */ + + $response = $this->client->call(Client::METHOD_GET, '/database/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + /** * Test for SUCCESS */ @@ -68,9 +81,30 @@ class DatabaseConsoleTest extends Scope public function testGetCollectionUsage(array $data) { /** - * Test for SUCCESS + * Test for FAILURE */ + $response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_GET, '/database/randomCollectionId/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 404); + + /** + * Test for SUCCESS + */ $response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] diff --git a/tests/e2e/Services/Functions/FunctionsConsoleTest.php b/tests/e2e/Services/Functions/FunctionsConsoleTest.php index 99dd4d315d..669a42c2ee 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleTest.php @@ -5,80 +5,75 @@ namespace Tests\E2E\Services\Database; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Client; +use Tests\E2E\Scopes\SideConsole; -class DatabaseConsoleTest extends Scope +class FunctionsConsoleTest extends Scope { use ProjectCustom; + use SideConsole; - public function getHeaders():array + public function testCreateFunction():array { - return [ - 'origin' => 'http://localhost', - 'cookie' => 'a_session_console=' . $this->getRoot()['session'], - 'x-appwrite-mode' => 'admin' - ]; - } - - public function testCreateCollection():array - { - /** - * Test for SUCCESS - */ - $movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'collectionId' => 'unique()', - 'name' => 'Movies', - 'read' => ['role:all'], - 'write' => ['role:all'], - 'permission' => 'document', + 'functionId' => 'unique()', + 'name' => 'Test', + 'execute' => ['user:'.$this->getUser()['$id']], + 'runtime' => 'php-8.0', + 'vars' => [ + 'funcKey1' => 'funcValue1', + 'funcKey2' => 'funcValue2', + 'funcKey3' => 'funcValue3', + ], + 'events' => [ + 'account.create', + 'account.delete', + ], + 'schedule' => '0 0 1 1 *', + 'timeout' => 10, ]); - $this->assertEquals($movies['headers']['status-code'], 201); - $this->assertEquals($movies['body']['name'], 'Movies'); + $this->assertEquals(201, $function['headers']['status-code']); - return ['moviesId' => $movies['body']['$id']]; + return [ + 'functionId' => $function['body']['$id'] + ]; } - public function testGetDatabaseUsage() - { - /** - * Test for SUCCESS - */ - - $response = $this->client->call(Client::METHOD_GET, '/database/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h' - ]); - - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['documents.count']); - $this->assertIsArray($response['body']['collections.count']); - $this->assertIsArray($response['body']['documents.create']); - $this->assertIsArray($response['body']['documents.read']); - $this->assertIsArray($response['body']['documents.update']); - $this->assertIsArray($response['body']['documents.delete']); - $this->assertIsArray($response['body']['collections.create']); - $this->assertIsArray($response['body']['collections.read']); - $this->assertIsArray($response['body']['collections.update']); - $this->assertIsArray($response['body']['collections.delete']); - } - - /** - * @depends testCreateCollection + * @depends testCreateFunction */ public function testGetCollectionUsage(array $data) { + /** + * Test for FAILURE + */ + + $response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '232h' + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/functions/randomFunctionId/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ @@ -87,11 +82,9 @@ class DatabaseConsoleTest extends Scope $this->assertEquals($response['headers']['status-code'], 200); $this->assertEquals($response['body']['range'], '24h'); - $this->assertIsArray($response['body']['documents.count']); - $this->assertIsArray($response['body']['documents.create']); - $this->assertIsArray($response['body']['documents.read']); - $this->assertIsArray($response['body']['documents.update']); - $this->assertIsArray($response['body']['documents.delete']); + $this->assertIsArray($response['body']['functions.executions']); + $this->assertIsArray($response['body']['functions.failures']); + $this->assertIsArray($response['body']['functions.compute']); } } \ No newline at end of file From 9eea6d55f737f41d199245a6e37327d31aae8e2d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 13:41:29 +0530 Subject: [PATCH 27/35] feat(tests): added tests for projects usage --- ...Test.php => DatabaseConsoleClientTest.php} | 4 ++- ...est.php => FunctionsConsoleClientTest.php} | 3 ++- .../Projects/ProjectsConsoleClientTest.php | 26 +++++++------------ 3 files changed, 14 insertions(+), 19 deletions(-) rename tests/e2e/Services/Database/{DatabaseConsoleTest.php => DatabaseConsoleClientTest.php} (96%) rename tests/e2e/Services/Functions/{FunctionsConsoleTest.php => FunctionsConsoleClientTest.php} (96%) diff --git a/tests/e2e/Services/Database/DatabaseConsoleTest.php b/tests/e2e/Services/Database/DatabaseConsoleClientTest.php similarity index 96% rename from tests/e2e/Services/Database/DatabaseConsoleTest.php rename to tests/e2e/Services/Database/DatabaseConsoleClientTest.php index f8d50188ce..625f26107b 100644 --- a/tests/e2e/Services/Database/DatabaseConsoleTest.php +++ b/tests/e2e/Services/Database/DatabaseConsoleClientTest.php @@ -7,7 +7,7 @@ use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Client; use Tests\E2E\Scopes\SideConsole; -class DatabaseConsoleTest extends Scope +class DatabaseConsoleClientTest extends Scope { use ProjectCustom; use SideConsole; @@ -61,6 +61,7 @@ class DatabaseConsoleTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 11); $this->assertEquals($response['body']['range'], '24h'); $this->assertIsArray($response['body']['documents.count']); $this->assertIsArray($response['body']['collections.count']); @@ -113,6 +114,7 @@ class DatabaseConsoleTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 6); $this->assertEquals($response['body']['range'], '24h'); $this->assertIsArray($response['body']['documents.count']); $this->assertIsArray($response['body']['documents.create']); diff --git a/tests/e2e/Services/Functions/FunctionsConsoleTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php similarity index 96% rename from tests/e2e/Services/Functions/FunctionsConsoleTest.php rename to tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index 669a42c2ee..d8bdb30b65 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -7,7 +7,7 @@ use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Client; use Tests\E2E\Scopes\SideConsole; -class FunctionsConsoleTest extends Scope +class FunctionsConsoleClientTest extends Scope { use ProjectCustom; use SideConsole; @@ -81,6 +81,7 @@ class FunctionsConsoleTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 4); $this->assertEquals($response['body']['range'], '24h'); $this->assertIsArray($response['body']['functions.executions']); $this->assertIsArray($response['body']['functions.failures']); diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 4d83efb416..1842891492 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -215,24 +215,16 @@ class ProjectsConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(count($response['body']), 8); $this->assertNotEmpty($response['body']); - $this->assertArrayHasKey('collections', $response['body']); - $this->assertArrayHasKey('documents', $response['body']); - $this->assertArrayHasKey('network', $response['body']); - $this->assertArrayHasKey('requests', $response['body']); - $this->assertArrayHasKey('storage', $response['body']); - $this->assertArrayHasKey('users', $response['body']); - $this->assertIsArray($response['body']['collections']['data']); - $this->assertIsInt($response['body']['collections']['total']); - $this->assertIsArray($response['body']['documents']['data']); - $this->assertIsInt($response['body']['documents']['total']); - $this->assertIsArray($response['body']['network']['data']); - $this->assertIsInt($response['body']['network']['total']); - $this->assertIsArray($response['body']['requests']['data']); - $this->assertIsInt($response['body']['requests']['total']); - $this->assertIsInt($response['body']['storage']['total']); - $this->assertIsArray($response['body']['users']['data']); - $this->assertIsInt($response['body']['users']['total']); + $this->assertEquals('30d', $response['body']['range']); + $this->assertIsArray($response['body']['requests']); + $this->assertIsArray($response['body']['network']); + $this->assertIsArray($response['body']['functions']); + $this->assertIsArray($response['body']['documents']); + $this->assertIsArray($response['body']['collections']); + $this->assertIsArray($response['body']['users']); + $this->assertIsArray($response['body']['storage']); /** * Test for FAILURE From 4454a518b78b891917332d18f39a266a047def2f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 13:49:25 +0530 Subject: [PATCH 28/35] feat(tests): added tests for storage usage --- .../Storage/StorageConsoleClientTest.php | 86 ++++++++++++++++++- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index b2c9582960..6960661777 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -2,13 +2,91 @@ namespace Tests\E2E\Services\Storage; +use Tests\E2E\Client; use Tests\E2E\Scopes\Scope; -use Tests\E2E\Scopes\ProjectConsole; -use Tests\E2E\Scopes\SideClient; +use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SideConsole; class StorageConsoleClientTest extends Scope { + use SideConsole; use StorageBase; - use ProjectConsole; - use SideClient; + use ProjectCustom; + + public function testGetStorageUsage() + { + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/storage/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + /** + * Test for SUCCESS + */ + + $response = $this->client->call(Client::METHOD_GET, '/storage/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 3); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['storage']); + $this->assertIsArray($response['body']['files']); + } + + public function testGetStorageBucketUsage() + { + /** + * Test for FAILURE + */ + + $response = $this->client->call(Client::METHOD_GET, '/storage/default/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + // TODO: Uncomment once we implement check for missing bucketId in the usage endpoint. + + // $response = $this->client->call(Client::METHOD_GET, '/storage/randomBucketId/usage', array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $this->getProject()['$id'] + // ], $this->getHeaders()), [ + // 'range' => '24h' + // ]); + + // $this->assertEquals($response['headers']['status-code'], 404); + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/storage/default/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 5); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['files.create']); + $this->assertIsArray($response['body']['files.read']); + $this->assertIsArray($response['body']['files.update']); + $this->assertIsArray($response['body']['files.delete']); + } } \ No newline at end of file From 3aedad2061d020a2fc392273c167a6b7ab4d9812 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 14:13:14 +0530 Subject: [PATCH 29/35] feat(tests): added tests for users usage --- .../Utopia/Response/Model/UsageUsers.php | 2 +- .../Functions/FunctionsConsoleClientTest.php | 2 +- .../Services/Users/UsersConsoleClientTest.php | 83 +++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/Services/Users/UsersConsoleClientTest.php diff --git a/src/Appwrite/Utopia/Response/Model/UsageUsers.php b/src/Appwrite/Utopia/Response/Model/UsageUsers.php index 913c4f33fe..8813615062 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageUsers.php +++ b/src/Appwrite/Utopia/Response/Model/UsageUsers.php @@ -62,7 +62,7 @@ class UsageUsers extends Model ->addRule('sessions.provider.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).', - 'default' => null, + 'default' => [], 'example' => new stdClass, 'array' => true ]) diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index d8bdb30b65..ee15a81dcb 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -1,6 +1,6 @@ client->call(Client::METHOD_GET, '/users/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '32h', + 'provider' => 'email' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h', + 'provider' => 'some-random-provider' + ]); + + $this->assertEquals($response['headers']['status-code'], 400); + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h', + 'provider' => 'email' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 9); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['users.count']); + $this->assertIsArray($response['body']['users.create']); + $this->assertIsArray($response['body']['users.read']); + $this->assertIsArray($response['body']['users.update']); + $this->assertIsArray($response['body']['users.delete']); + $this->assertIsArray($response['body']['sessions.create']); + $this->assertIsArray($response['body']['sessions.provider.create']); + $this->assertIsArray($response['body']['sessions.delete']); + + $response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals(count($response['body']), 9); + $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['users.count']); + $this->assertIsArray($response['body']['users.create']); + $this->assertIsArray($response['body']['users.read']); + $this->assertIsArray($response['body']['users.update']); + $this->assertIsArray($response['body']['users.delete']); + $this->assertIsArray($response['body']['sessions.create']); + $this->assertIsArray($response['body']['sessions.provider.create']); + $this->assertIsArray($response['body']['sessions.delete']); + } +} \ No newline at end of file From 66294445ce47a2a7010ff63dc1c75ae25f47fb17 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 29 Aug 2021 14:24:50 +0530 Subject: [PATCH 30/35] feat(tests): added tests for users usage --- app/controllers/api/storage.php | 2 ++ src/Appwrite/Utopia/Response/Model/UsageBuckets.php | 7 +++++++ tests/e2e/Services/Storage/StorageConsoleClientTest.php | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index a11d50b5e3..ee6210d25d 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -759,6 +759,7 @@ App::get('/v1/storage/:bucketId/usage') ]; $metrics = [ + "storage.buckets.$bucketId.files.count", "storage.buckets.$bucketId.files.create", "storage.buckets.$bucketId.files.read", "storage.buckets.$bucketId.files.update", @@ -787,6 +788,7 @@ App::get('/v1/storage/:bucketId/usage') $usage = new Document([ 'range' => $range, + 'files.count' => $stats["storage.buckets.$bucketId.files.count"], 'files.create' => $stats["storage.buckets.$bucketId.files.create"], 'files.read' => $stats["storage.buckets.$bucketId.files.read"], 'files.update' => $stats["storage.buckets.$bucketId.files.update"], diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php index 11fb2e2f8c..8c66ff47c4 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -17,6 +17,13 @@ class UsageBuckets extends Model 'default' => '', 'example' => '30d', ]) + ->addRule('files.count', [ + 'type' => Response::MODEL_METRIC_LIST, + 'description' => 'Aggregated stats for total number of files in this bucket.', + 'default' => [], + 'example' => new stdClass, + 'array' => true + ]) ->addRule('files.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for files created.', diff --git a/tests/e2e/Services/Storage/StorageConsoleClientTest.php b/tests/e2e/Services/Storage/StorageConsoleClientTest.php index 6960661777..4ec1a6ce9a 100644 --- a/tests/e2e/Services/Storage/StorageConsoleClientTest.php +++ b/tests/e2e/Services/Storage/StorageConsoleClientTest.php @@ -30,7 +30,6 @@ class StorageConsoleClientTest extends Scope /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/storage/usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] @@ -82,8 +81,9 @@ class StorageConsoleClientTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(count($response['body']), 5); + $this->assertEquals(count($response['body']), 6); $this->assertEquals($response['body']['range'], '24h'); + $this->assertIsArray($response['body']['files.count']); $this->assertIsArray($response['body']['files.create']); $this->assertIsArray($response['body']['files.read']); $this->assertIsArray($response['body']['files.update']); From 723bddf95ab876521be0803e9869407b21484745 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 30 Aug 2021 13:14:45 +0530 Subject: [PATCH 31/35] feat(compose): disable debug mode --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 12bc51b802..539799aa9a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,7 +39,7 @@ services: build: context: . args: - - DEBUG=true + - DEBUG=false - TESTING=true - VERSION=dev ports: From ad81e414d99787b4516bdad56f5c3e7f59748506 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 1 Sep 2021 00:34:01 +0530 Subject: [PATCH 32/35] feat(response): add metric and metric list to response models --- src/Appwrite/Utopia/Response.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 0c4c6bf0aa..7e012bc6c0 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -33,6 +33,7 @@ use Appwrite\Utopia\Response\Model\Team; use Appwrite\Utopia\Response\Model\Locale; use Appwrite\Utopia\Response\Model\Log; use Appwrite\Utopia\Response\Model\Membership; +use Appwrite\Utopia\Response\Model\Metric; use Appwrite\Utopia\Response\Model\Permissions; use Appwrite\Utopia\Response\Model\Phone; use Appwrite\Utopia\Response\Model\Platform; @@ -194,6 +195,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) + ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metric', self::MODEL_METRIC)) // Entities ->setModel(new Collection()) ->setModel(new Attribute()) @@ -229,6 +231,7 @@ class Response extends SwooleResponse ->setModel(new UsageBuckets()) ->setModel(new UsageFunctions()) ->setModel(new UsageProject()) + ->setModel(new Metric()) // Verification // Recovery // Tests (keep last) From ac0ff8ea0fcfc60375b61001a72ed6a4dded9bb0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 1 Sep 2021 02:25:13 +0530 Subject: [PATCH 33/35] feat(response): use recursive function for swagger/openapi spec generation --- app/controllers/api/database.php | 2 +- app/controllers/api/storage.php | 2 +- app/controllers/api/users.php | 3 +- .../Specification/Format/OpenAPI3.php | 28 ++++++++++++--- .../Specification/Format/Swagger2.php | 34 ++++++++++++++----- src/Appwrite/Utopia/Response.php | 5 ++- 6 files changed, 55 insertions(+), 19 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index e2af571bf7..be0a346047 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -344,7 +344,7 @@ App::get('/v1/database/:collectionId/usage') ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'database') - ->label('sdk.method', 'getUsage') + ->label('sdk.method', 'getCollectionUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USAGE_COLLECTION) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index ee6210d25d..69ed5ba536 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -723,7 +723,7 @@ App::get('/v1/storage/:bucketId/usage') ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'storage') - ->label('sdk.method', 'getUsage') + ->label('sdk.method', 'getBucketUsage') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USAGE_BUCKETS) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 7fcdffcc8b..48c67daa82 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -18,6 +18,7 @@ use Utopia\Database\Validator\UID; use DeviceDetector\DeviceDetector; use Appwrite\Database\Validator\CustomId; use Appwrite\Utopia\Response\Model; +use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; @@ -621,7 +622,7 @@ App::get('/v1/users/usage') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USAGE_USERS) ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) - ->param('provider', '', new WhiteList(['email', 'anonymous', 'oauth2-google', 'oauth2-apple'], true), 'Provider Name.', true) + ->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(function($value) { return "oauth-".$value; }, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true) ->inject('response') ->inject('dbForInternal') ->inject('register') diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 353369eb65..32e2347c0e 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -21,6 +21,28 @@ class OpenAPI3 extends Format return 'Open API 3'; } + /** + * Get Used Models + * + * Recursively get all used models + * + * @param object $model + * @param array $models + * + * @return void + */ + protected function getUsedModels($model, array &$usedModels) + { + if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float'])) { + $usedModels[] = $model; + return; + } + if (!is_object($model)) return; + foreach ($model->getRules() as $rule) { + $this->getUsedModels($rule['type'], $usedModels); + } + } + /** * Parse * @@ -352,11 +374,7 @@ class OpenAPI3 extends Format $output['paths'][$url][\strtolower($route->getMethod())] = $temp; } foreach ($this->models as $model) { - foreach ($model->getRules() as $rule) { - if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) { - $usedModels[] = $rule['type']; - } - } + $this->getUsedModels($model, $usedModels); } foreach ($this->models as $model) { if (!in_array($model->getType(), $usedModels) && $model->getType() !== 'error') { diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index d357283a02..75cac416d7 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -21,6 +21,28 @@ class Swagger2 extends Format return 'Swagger 2'; } + /** + * Get Used Models + * + * Recursively get all used models + * + * @param object $model + * @param array $models + * + * @return void + */ + protected function getUsedModels($model, array &$usedModels) + { + if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float'])) { + $usedModels[] = $model; + return; + } + if (!is_object($model)) return; + foreach ($model->getRules() as $rule) { + $this->getUsedModels($rule['type'], $usedModels); + } + } + /** * Parse * @@ -354,15 +376,11 @@ class Swagger2 extends Format $output['paths'][$url][\strtolower($route->getMethod())] = $temp; } foreach ($this->models as $model) { - foreach ($model->getRules() as $rule) { - if ( - in_array($model->getType(), $usedModels) - && !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float']) - ) { - $usedModels[] = $rule['type']; - } - } + $this->getUsedModels($model, $usedModels); } + + // var_dump($usedModels); + foreach ($this->models as $model) { if (!in_array($model->getType(), $usedModels)) { continue; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 7e012bc6c0..329dc86432 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -51,7 +51,6 @@ use Appwrite\Utopia\Response\Model\UsageFunctions; use Appwrite\Utopia\Response\Model\UsageProject; use Appwrite\Utopia\Response\Model\UsageStorage; use Appwrite\Utopia\Response\Model\UsageUsers; -use Appwrite\Utopia\Response\Model\UsersUsage; use stdClass; /** @@ -195,7 +194,7 @@ class Response extends SwooleResponse ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) - ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metric', self::MODEL_METRIC)) + ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) // Entities ->setModel(new Collection()) ->setModel(new Attribute()) @@ -224,6 +223,7 @@ class Response extends SwooleResponse ->setModel(new Language()) ->setModel(new Currency()) ->setModel(new Phone()) + ->setModel(new Metric()) ->setModel(new UsageDatabase()) ->setModel(new UsageCollection()) ->setModel(new UsageUsers()) @@ -231,7 +231,6 @@ class Response extends SwooleResponse ->setModel(new UsageBuckets()) ->setModel(new UsageFunctions()) ->setModel(new UsageProject()) - ->setModel(new Metric()) // Verification // Recovery // Tests (keep last) From dd9cddc525b8bfe8178d68daffce673f0184b0f4 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 1 Sep 2021 16:54:36 +0530 Subject: [PATCH 34/35] feat(review): fix review comments --- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- src/Appwrite/Specification/Format/Swagger2.php | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 32e2347c0e..5095c5395f 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -27,7 +27,7 @@ class OpenAPI3 extends Format * Recursively get all used models * * @param object $model - * @param array $models + * @param array $models * * @return void */ diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 75cac416d7..2a3be45d17 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -27,7 +27,7 @@ class Swagger2 extends Format * Recursively get all used models * * @param object $model - * @param array $models + * @param array $models * * @return void */ @@ -379,8 +379,6 @@ class Swagger2 extends Format $this->getUsedModels($model, $usedModels); } - // var_dump($usedModels); - foreach ($this->models as $model) { if (!in_array($model->getType(), $usedModels)) { continue; From eccc19e5dc46fa6c087106a77b10da899d19339a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 6 Sep 2021 12:13:20 +0530 Subject: [PATCH 35/35] Apply suggestions from code review Co-authored-by: Damodar Lohani --- app/controllers/api/database.php | 4 ++-- app/controllers/api/functions.php | 2 +- app/controllers/api/projects.php | 2 +- app/controllers/api/storage.php | 2 +- app/controllers/api/users.php | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index be0a346047..5a8a599900 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -270,7 +270,7 @@ App::get('/v1/database/usage') if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '15m', + 'period' => '30m', 'limit' => 48, ], '7d' => [ @@ -369,7 +369,7 @@ App::get('/v1/database/:collectionId/usage') if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '15m', + 'period' => '30m', 'limit' => 48, ], '7d' => [ diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index fadc323832..2a67c6d4c6 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -171,7 +171,7 @@ App::get('/v1/functions/:functionId/usage') if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '15m', + 'period' => '30m', 'limit' => 48, ], '7d' => [ diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 0d1c3b713a..3c7dbb8e5e 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -248,7 +248,7 @@ App::get('/v1/projects/:projectId/usage') if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '15m', + 'period' => '30m', 'limit' => 48, ], '7d' => [ diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 69ed5ba536..b9b2b8e8a1 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -665,7 +665,7 @@ App::get('/v1/storage/usage') if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '15m', + 'period' => '30m', 'limit' => 48, ], '7d' => [ diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 48c67daa82..c55f5382f5 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -634,7 +634,7 @@ App::get('/v1/users/usage') if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ - 'period' => '15m', + 'period' => '30m', 'limit' => 48, ], '7d' => [