Merge pull request #1504 from appwrite/feat-new-usage-endpoints

Feat new usage endpoints
This commit is contained in:
Damodar Lohani 2021-09-07 10:56:36 +05:45 committed by GitHub
commit 5cc74bf4cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1632 additions and 187 deletions

View file

@ -28,6 +28,7 @@ use Utopia\Database\Exception\Structure as StructureException;
use Appwrite\Utopia\Response;
use Appwrite\Database\Validator\CustomId;
use DeviceDetector\DeviceDetector;
use Utopia\Database\Validator\Authorization;
$attributesCallback = function ($collectionId, $attribute, $response, $dbForInternal, $database, $audits, $usage) {
/** @var Utopia\Database\Document $attribute*/
@ -246,6 +247,186 @@ App::get('/v1/database/collections/:collectionId')
$response->dynamic($collection, Response::MODEL_COLLECTION);
});
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_USAGE_DATABASE)
->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 $dbForConsole */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Registry\Registry $register */
$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,
],
];
$metrics = [
'database.documents.count',
'database.collections.count',
'database.collections.create',
'database.collections.read',
'database.collections.update',
'database.collections.delete',
'database.documents.create',
'database.documents.read',
'database.documents.update',
'database.documents.delete'
];
$stats = [];
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,
'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"],
]);
}
$response->dynamic($usage, Response::MODEL_USAGE_DATABASE);
});
App::get('/v1/database/:collectionId/usage')
->desc('Get usage stats for a collection')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'database')
->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)
->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);
}
$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,
],
];
$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 = [];
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,
'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"]
]);
}
$response->dynamic($usage, Response::MODEL_USAGE_COLLECTION);
});
App::get('/v1/database/collections/:collectionId/logs')
->desc('List Collection Logs')
->groups(['api', 'database'])

View file

@ -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_USAGE_FUNCTIONS)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
->inject('response')
@ -164,99 +167,62 @@ 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',
'period' => '30m',
'limit' => 48,
],
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
'period' => '1d',
'limit' => 7,
],
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
'period' => '1d',
'limit' => 30,
],
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
'period' => '1d',
'limit' => 90,
],
];
$metrics = [
"functions.$functionId.executions",
"functions.$functionId.failures",
"functions.$functionId.compute"
];
$stats = [];
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]);
$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([
$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,
'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_USAGE_FUNCTIONS);
});
App::put('/v1/functions/:functionId')

View file

@ -217,12 +217,15 @@ App::get('/v1/projects/:projectId')
});
App::get('/v1/projects/:projectId/usage')
->desc('Get Project')
->desc('Get usage stats for a project')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->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_USAGE_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response')
@ -241,7 +244,7 @@ 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' => [
@ -264,77 +267,49 @@ App::get('/v1/projects/:projectId/usage')
$dbForInternal->setNamespace('project_' . $projectId . '_internal');
Authorization::disable();
$metrics = [
'requests',
'network',
'executions',
'users.count',
'database.documents.count',
'database.collections.count',
'storage.total'
];
$metrics = ['requests', 'network', 'executions'];
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 = [];
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
$stats[$metric][] = [
'value' => $requestDoc->getAttribute('value'),
'date' => $requestDoc->getAttribute('time'),
];
}
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]);
}
});
$stats[$metric] = array_reverse($stats[$metric]);
}
$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_USAGE_PROJECT);
});
App::patch('/v1/projects/:projectId')

View file

@ -10,6 +10,7 @@ use Utopia\Validator\HexColor;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Filesystem;
use Appwrite\ClamAV\Network;
use Utopia\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,159 @@ App::delete('/v1/storage/files/:fileId')
;
$response->noContent();
});
App::get('/v1/storage/usage')
->desc('Get usage stats for storage')
->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_USAGE_STORAGE)
->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 */
$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,
],
];
$metrics = [
"storage.total",
"storage.files.count"
];
$stats = [];
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,
'storage' => $stats['storage.total'],
'files' => $stats['storage.files.count']
]);
}
$response->dynamic($usage, Response::MODEL_USAGE_STORAGE);
});
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', '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)
->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,
],
];
$metrics = [
"storage.buckets.$bucketId.files.count",
"storage.buckets.$bucketId.files.create",
"storage.buckets.$bucketId.files.read",
"storage.buckets.$bucketId.files.update",
"storage.buckets.$bucketId.files.delete"
];
$stats = [];
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,
'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"],
'files.delete' => $stats["storage.buckets.$bucketId.files.delete"]
]);
}
$response->dynamic($usage, Response::MODEL_USAGE_BUCKETS);
});

View file

@ -17,6 +17,11 @@ 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\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
App::post('/v1/users')
->desc('Create User')
@ -605,3 +610,91 @@ App::delete('/v1/users/:userId')
;
$response->noContent();
});
App::get('/v1/users/usage')
->desc('Get usage stats for the users API')
->groups(['api', 'users'])
->label('scope', 'users.read')
->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_USAGE_USERS)
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', 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')
->action(function ($range, $provider, $response, $dbForInternal) {
/** @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,
],
];
$metrics = [
"users.count",
"users.create",
"users.read",
"users.update",
"users.delete",
"users.sessions.create",
"users.sessions.$provider.create",
"users.sessions.delete"
];
$stats = [];
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,
'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.provider.create' => $stats["users.sessions.$provider.create"],
'sessions.delete' => $stats["users.sessions.delete"]
]);
}
$response->dynamic($usage, Response::MODEL_USAGE_USERS);
});

View file

@ -83,4 +83,4 @@
"php": "8.0"
}
}
}
}

12
composer.lock generated
View file

@ -355,16 +355,16 @@
},
{
"name": "composer/package-versions-deprecated",
"version": "1.11.99.2",
"version": "1.11.99.3",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
"reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c"
"reference": "fff576ac850c045158a250e7e27666e146e78d18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c6522afe5540d5fc46675043d3ed5a45a740b27c",
"reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18",
"reference": "fff576ac850c045158a250e7e27666e146e78d18",
"shasum": ""
},
"require": {
@ -408,7 +408,7 @@
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
"issues": "https://github.com/composer/package-versions-deprecated/issues",
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.2"
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3"
},
"funding": [
{
@ -424,7 +424,7 @@
"type": "tidelift"
}
],
"time": "2021-05-24T07:46:03+00:00"
"time": "2021-08-17T13:49:14+00:00"
},
{
"name": "dragonmantank/cron-expression",

View file

@ -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') {

View file

@ -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,9 @@ 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);
}
foreach ($this->models as $model) {
if (!in_array($model->getType(), $usedModels)) {
continue;

View file

@ -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;
@ -43,6 +44,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\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 stdClass;
/**
@ -56,8 +64,17 @@ class Response extends SwooleResponse
const MODEL_LOG = 'log';
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';
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';
@ -177,6 +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, 'metrics', self::MODEL_METRIC, true, false))
// Entities
->setModel(new Collection())
->setModel(new Attribute())
@ -205,6 +223,14 @@ 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())
->setModel(new UsageStorage())
->setModel(new UsageBuckets())
->setModel(new UsageFunctions())
->setModel(new UsageProject())
// Verification
// Recovery
// Tests (keep last)

View file

@ -0,0 +1,47 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Metric extends Model
{
public function __construct()
{
$this
->addRule('value', [
'type' => self::TYPE_INTEGER,
'description' => 'The value of this metric at the timestamp.',
'default' => -1,
'example' => 1,
])
->addRule('timestamp', [
'type' => self::TYPE_INTEGER,
'description' => 'The UNIX timestamp at which this metric was aggregated.',
'default' => 0,
'example' => 1592981250
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Metric';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_METRIC;
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageBuckets extends Model
{
public function __construct()
{
$this
->addRule('range', [
'type' => self::TYPE_STRING,
'description' => 'The time range of the usage stats.',
'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.',
'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 'UsageBuckets';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_BUCKETS;
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageCollection extends Model
{
public function __construct()
{
$this
->addRule('range', [
'type' => self::TYPE_STRING,
'description' => 'The time range of the usage stats.',
'default' => '',
'example' => '30d',
])
->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 'UsageCollection';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_COLLECTION;
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageDatabase extends Model
{
public function __construct()
{
$this
->addRule('range', [
'type' => self::TYPE_STRING,
'description' => 'The time range of the usage stats.',
'default' => '',
'example' => '30d',
])
->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 'UsageDatabase';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_DATABASE;
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageFunctions extends Model
{
public function __construct()
{
$this
->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 'UsageFunctions';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_FUNCTIONS;
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageProject extends Model
{
public function __construct()
{
$this
->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('storage', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for the occupied storage size (in bytes).',
'default' => [],
'example' => new stdClass,
'array' => true
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'UsageProject';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_PROJECT;
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageStorage extends Model
{
public function __construct()
{
$this
->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 (in bytes).',
'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 Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_STORAGE;
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use stdClass;
class UsageUsers extends Model
{
public function __construct()
{
$this
->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.provider.create', [
'type' => Response::MODEL_METRIC_LIST,
'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).',
'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 'UsageUsers';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_USAGE_USERS;
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Tests\E2E\Scopes;
trait SideConsole
{
public function getHeaders():array
{
return [
'origin' => 'http://localhost',
'cookie' => 'a_session_console='. $this->getRoot()['session'],
'x-appwrite-mode' => 'admin'
];
}
/**
* @return string
*/
public function getSide()
{
return 'console';
}
}

View file

@ -0,0 +1,125 @@
<?php
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 DatabaseConsoleClientTest extends Scope
{
use ProjectCustom;
use SideConsole;
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 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
*/
$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(count($response['body']), 11);
$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 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']
], $this->getHeaders()), [
'range' => '24h'
]);
$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']);
$this->assertIsArray($response['body']['documents.read']);
$this->assertIsArray($response['body']['documents.update']);
$this->assertIsArray($response['body']['documents.delete']);
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Tests\E2E\Services\Functions;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Client;
use Tests\E2E\Scopes\SideConsole;
class FunctionsConsoleClientTest extends Scope
{
use ProjectCustom;
use SideConsole;
public function testCreateFunction():array
{
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'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(201, $function['headers']['status-code']);
return [
'functionId' => $function['body']['$id']
];
}
/**
* @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, '/functions/'.$data['functionId'].'/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']), 4);
$this->assertEquals($response['body']['range'], '24h');
$this->assertIsArray($response['body']['functions.executions']);
$this->assertIsArray($response['body']['functions.failures']);
$this->assertIsArray($response['body']['functions.compute']);
}
}

View file

@ -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

View file

@ -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']), 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']);
$this->assertIsArray($response['body']['files.delete']);
}
}

View file

@ -0,0 +1,83 @@
<?php
namespace Tests\E2E\Services\Users;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Client;
use Tests\E2E\Scopes\SideConsole;
class UsersConsoleClientTest extends Scope
{
use ProjectCustom;
use SideConsole;
public function testGetUsersUsage()
{
/**
* Test for FAILURE
*/
$response = $this->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']);
}
}