Merge pull request #7266 from appwrite/feat-project-usage-custom-daterange
feat: project usage custom date range
This commit is contained in:
commit
6925122791
4 changed files with 77 additions and 43 deletions
|
@ -6,6 +6,7 @@ use Utopia\App;
|
||||||
use Utopia\Config\Config;
|
use Utopia\Config\Config;
|
||||||
use Utopia\Database\Database;
|
use Utopia\Database\Database;
|
||||||
use Utopia\Database\Document;
|
use Utopia\Database\Document;
|
||||||
|
use Utopia\Database\Validator\Datetime as DateTimeValidator;
|
||||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
|
@ -26,14 +27,16 @@ App::get('/v1/project/usage')
|
||||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
->label('sdk.response.model', Response::MODEL_USAGE_PROJECT)
|
->label('sdk.response.model', Response::MODEL_USAGE_PROJECT)
|
||||||
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
|
->param('startDate', '', new DateTimeValidator(), 'Starting date for the usage')
|
||||||
|
->param('endDate', '', new DateTimeValidator(), 'End date for the usage')
|
||||||
|
->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject) {
|
||||||
|
|
||||||
$periods = Config::getParam('usage', []);
|
|
||||||
$stats = $total = $usage = [];
|
$stats = $total = $usage = [];
|
||||||
$days = $periods[$range];
|
$format = 'Y-m-d 00:00:00';
|
||||||
|
$firstDay = (new DateTime($startDate))->format($format);
|
||||||
|
$lastDay = (new DateTime($endDate))->format($format);
|
||||||
|
|
||||||
$metrics = [
|
$metrics = [
|
||||||
'total' => [
|
'total' => [
|
||||||
|
@ -51,7 +54,22 @@ App::get('/v1/project/usage')
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$total, &$stats) {
|
$factor = match ($period) {
|
||||||
|
'1h' => 3600,
|
||||||
|
'1d' => 86400,
|
||||||
|
};
|
||||||
|
|
||||||
|
$limit = match ($period) {
|
||||||
|
'1h' => (new DateTime($endDate))->diff(new DateTime($startDate))->h,
|
||||||
|
'1d' => (new DateTime($endDate))->diff(new DateTime($startDate))->days
|
||||||
|
};
|
||||||
|
|
||||||
|
$format = match ($period) {
|
||||||
|
'1h' => 'Y-m-d\TH:00:00.000P',
|
||||||
|
'1d' => 'Y-m-d\T00:00:00.000P',
|
||||||
|
};
|
||||||
|
|
||||||
|
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) {
|
||||||
foreach ($metrics['total'] as $metric) {
|
foreach ($metrics['total'] as $metric) {
|
||||||
$result = $dbForProject->findOne('stats', [
|
$result = $dbForProject->findOne('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
|
@ -61,12 +79,11 @@ App::get('/v1/project/usage')
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($metrics['period'] as $metric) {
|
foreach ($metrics['period'] as $metric) {
|
||||||
$limit = $days['limit'];
|
|
||||||
$period = $days['period'];
|
|
||||||
$results = $dbForProject->find('stats', [
|
$results = $dbForProject->find('stats', [
|
||||||
Query::equal('metric', [$metric]),
|
Query::equal('metric', [$metric]),
|
||||||
Query::equal('period', [$period]),
|
Query::equal('period', [$period]),
|
||||||
Query::limit($limit),
|
Query::greaterThanEqual('time', $firstDay),
|
||||||
|
Query::lessThan('time', $lastDay),
|
||||||
Query::orderDesc('time'),
|
Query::orderDesc('time'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -79,25 +96,20 @@ App::get('/v1/project/usage')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$format = match ($days['period']) {
|
foreach ($metrics['period'] as $metric) {
|
||||||
'1h' => 'Y-m-d\TH:00:00.000P',
|
$usage[$metric] = [];
|
||||||
'1d' => 'Y-m-d\T00:00:00.000P',
|
$leap = time() - ($limit * $factor);
|
||||||
};
|
while ($leap < time()) {
|
||||||
|
$leap += $factor;
|
||||||
foreach ($metrics['period'] as $metric) {
|
$formatDate = date($format, $leap);
|
||||||
$usage[$metric] = [];
|
$usage[$metric][] = [
|
||||||
$leap = time() - ($days['limit'] * $days['factor']);
|
|
||||||
while ($leap < time()) {
|
|
||||||
$leap += $days['factor'];
|
|
||||||
$formatDate = date($format, $leap);
|
|
||||||
$usage[$metric][] = [
|
|
||||||
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
'value' => $stats[$metric][$formatDate]['value'] ?? 0,
|
||||||
'date' => $formatDate,
|
'date' => $formatDate,
|
||||||
];
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
$response->dynamic(new Document([
|
$response->dynamic(new Document([
|
||||||
'range' => $range,
|
|
||||||
'requests' => ($usage[METRIC_NETWORK_REQUESTS]),
|
'requests' => ($usage[METRIC_NETWORK_REQUESTS]),
|
||||||
'network' => ($usage[METRIC_NETWORK_INBOUND] + $usage[METRIC_NETWORK_OUTBOUND]),
|
'network' => ($usage[METRIC_NETWORK_INBOUND] + $usage[METRIC_NETWORK_OUTBOUND]),
|
||||||
'executionsTotal' => $total[METRIC_EXECUTIONS],
|
'executionsTotal' => $total[METRIC_EXECUTIONS],
|
||||||
|
@ -111,7 +123,6 @@ App::get('/v1/project/usage')
|
||||||
|
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
|
||||||
App::post('/v1/project/variables')
|
App::post('/v1/project/variables')
|
||||||
->desc('Create Variable')
|
->desc('Create Variable')
|
||||||
->groups(['api'])
|
->groups(['api'])
|
||||||
|
|
|
@ -10,12 +10,6 @@ class UsageProject extends Model
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
->addRule('range', [
|
|
||||||
'type' => self::TYPE_INTEGER,
|
|
||||||
'description' => 'The time range of the usage stats.',
|
|
||||||
'default' => '',
|
|
||||||
'example' => '30d',
|
|
||||||
])
|
|
||||||
->addRule('executionsTotal', [
|
->addRule('executionsTotal', [
|
||||||
'type' => self::TYPE_INTEGER,
|
'type' => self::TYPE_INTEGER,
|
||||||
'description' => 'Total aggregated number of function executions.',
|
'description' => 'Total aggregated number of function executions.',
|
||||||
|
|
|
@ -7,10 +7,10 @@ use Tests\E2E\Scopes\ProjectCustom;
|
||||||
use Tests\E2E\Scopes\Scope;
|
use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\SideServer;
|
use Tests\E2E\Scopes\SideServer;
|
||||||
use CURLFile;
|
use CURLFile;
|
||||||
|
use DateTime;
|
||||||
use Tests\E2E\Services\Functions\FunctionsBase;
|
use Tests\E2E\Services\Functions\FunctionsBase;
|
||||||
use Utopia\Database\Helpers\Permission;
|
use Utopia\Database\Helpers\Permission;
|
||||||
use Utopia\Database\Helpers\Role;
|
use Utopia\Database\Helpers\Role;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
|
||||||
|
|
||||||
class UsageTest extends Scope
|
class UsageTest extends Scope
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,19 @@ class UsageTest extends Scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getToday(): string
|
||||||
|
{
|
||||||
|
$date = new DateTime();
|
||||||
|
return $date->format(self::$formatTz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getTomorrow(): string
|
||||||
|
{
|
||||||
|
$date = new DateTime();
|
||||||
|
$date->modify('+1 day');
|
||||||
|
return $date->format(self::$formatTz);
|
||||||
|
}
|
||||||
|
|
||||||
public function testPrepareUsersStats(): array
|
public function testPrepareUsersStats(): array
|
||||||
{
|
{
|
||||||
$project = $this->getProject(true);
|
$project = $this->getProject(true);
|
||||||
|
@ -107,8 +120,13 @@ class UsageTest extends Scope
|
||||||
|
|
||||||
$res = $this->client->call(
|
$res = $this->client->call(
|
||||||
Client::METHOD_GET,
|
Client::METHOD_GET,
|
||||||
'/project/usage?range=24h',
|
'/project/usage',
|
||||||
$consoleHeaders
|
$consoleHeaders,
|
||||||
|
[
|
||||||
|
'period' => '1h',
|
||||||
|
'startDate' => self::getToday(),
|
||||||
|
'endDate' => self::getTomorrow(),
|
||||||
|
]
|
||||||
);
|
);
|
||||||
$res = $res['body'];
|
$res = $res['body'];
|
||||||
|
|
||||||
|
@ -265,11 +283,16 @@ class UsageTest extends Scope
|
||||||
|
|
||||||
$res = $this->client->call(
|
$res = $this->client->call(
|
||||||
Client::METHOD_GET,
|
Client::METHOD_GET,
|
||||||
'/project/usage?range=30d',
|
'/project/usage',
|
||||||
array_merge(
|
array_merge(
|
||||||
$data['headers'],
|
$data['headers'],
|
||||||
$data['consoleHeaders']
|
$data['consoleHeaders']
|
||||||
)
|
),
|
||||||
|
[
|
||||||
|
'period' => '1d',
|
||||||
|
'startDate' => self::getToday(),
|
||||||
|
'endDate' => self::getTomorrow(),
|
||||||
|
]
|
||||||
);
|
);
|
||||||
$res = $res['body'];
|
$res = $res['body'];
|
||||||
|
|
||||||
|
@ -470,8 +493,13 @@ class UsageTest extends Scope
|
||||||
|
|
||||||
$res = $this->client->call(
|
$res = $this->client->call(
|
||||||
Client::METHOD_GET,
|
Client::METHOD_GET,
|
||||||
'/project/usage?range=30d',
|
'/project/usage',
|
||||||
$data['consoleHeaders']
|
$data['consoleHeaders'],
|
||||||
|
[
|
||||||
|
'period' => '1d',
|
||||||
|
'startDate' => self::getToday(),
|
||||||
|
'endDate' => self::getTomorrow(),
|
||||||
|
]
|
||||||
);
|
);
|
||||||
$res = $res['body'];
|
$res = $res['body'];
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use Tests\E2E\Scopes\Scope;
|
||||||
use Tests\E2E\Scopes\ProjectConsole;
|
use Tests\E2E\Scopes\ProjectConsole;
|
||||||
use Tests\E2E\Scopes\SideClient;
|
use Tests\E2E\Scopes\SideClient;
|
||||||
use Tests\E2E\Client;
|
use Tests\E2E\Client;
|
||||||
|
use Tests\E2E\General\UsageTest;
|
||||||
use Utopia\Database\DateTime;
|
use Utopia\Database\DateTime;
|
||||||
use Utopia\Database\Helpers\ID;
|
use Utopia\Database\Helpers\ID;
|
||||||
|
|
||||||
|
@ -440,20 +441,20 @@ class ProjectsConsoleClientTest extends Scope
|
||||||
*/
|
*/
|
||||||
public function testGetProjectUsage($data): array
|
public function testGetProjectUsage($data): array
|
||||||
{
|
{
|
||||||
$id = $data['projectId'] ?? '';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for SUCCESS
|
* Test for SUCCESS
|
||||||
*/
|
*/
|
||||||
$response = $this->client->call(Client::METHOD_GET, '/project/usage', array_merge([
|
$response = $this->client->call(Client::METHOD_GET, '/project/usage', array_merge([
|
||||||
'content-type' => 'application/json',
|
'content-type' => 'application/json',
|
||||||
'x-appwrite-project' => $this->getProject()['$id'],
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
], $this->getHeaders()));
|
], $this->getHeaders()), [
|
||||||
|
'startDate' => UsageTest::getToday(),
|
||||||
|
'endDate' => UsageTest::getTomorrow(),
|
||||||
|
]);
|
||||||
|
|
||||||
$this->assertEquals(200, $response['headers']['status-code']);
|
$this->assertEquals(200, $response['headers']['status-code']);
|
||||||
$this->assertEquals(9, count($response['body']));
|
$this->assertEquals(8, count($response['body']));
|
||||||
$this->assertNotEmpty($response['body']);
|
$this->assertNotEmpty($response['body']);
|
||||||
$this->assertEquals('30d', $response['body']['range']);
|
|
||||||
$this->assertIsArray($response['body']['requests']);
|
$this->assertIsArray($response['body']['requests']);
|
||||||
$this->assertIsArray($response['body']['network']);
|
$this->assertIsArray($response['body']['network']);
|
||||||
$this->assertIsNumeric($response['body']['executionsTotal']);
|
$this->assertIsNumeric($response['body']['executionsTotal']);
|
||||||
|
|
Loading…
Reference in a new issue