Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups

# Conflicts:
#	app/controllers/api/databases.php
#	composer.lock
This commit is contained in:
fogelito
2024-09-26 10:42:15 +03:00
19 changed files with 832 additions and 64 deletions
+6
View File
@@ -319,10 +319,16 @@ These are the current metrics we collect usage stats for:
| users | Total number of users per project|
| executions | Total number of executions per project |
| databases | Total number of databases per project |
| databases.storage | Total amount of storage used by all databases per project (in bytes) |
| databases.storage_disk | Total amount of storage used by all database per project on disk (in bytes) |
| collections | Total number of collections per project |
| {databaseInternalId}.collections | Total number of collections per database|
| {databaseInternalId}.storage | Sum of database storage (in bytes) |
| {databaseInternalId}.storage_disk | Sum of database storage on disk (in bytes) |
| documents | Total number of documents per project |
| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
| {databaseInternalId}.{collectionInternalId}.storage | Sum of database storage used by the collection (in bytes) |
| {databsaeInternalId}.{collectionInternalId}.storage_disk | Sum of database storage used by the collection on disk (in bytes) |
| buckets | Total number of buckets per project |
| files | Total number of files per project |
| {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) |
+50 -2
View File
@@ -36879,6 +36879,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total databases storage in bytes.",
"x-example": 0,
"format": "int32"
},
"databases": {
"type": "array",
"description": "Aggregated number of databases per period.",
@@ -36902,6 +36908,14 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "An array of the aggregated number of databases storage in bytes per period.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
}
},
"required": [
@@ -36909,9 +36923,11 @@
"databasesTotal",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"databases",
"collections",
"documents"
"documents",
"storage"
]
},
"usageDatabase": {
@@ -36935,6 +36951,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total storage used in bytes.",
"x-example": 0,
"format": "int32"
},
"collections": {
"type": "array",
"description": "Aggregated number of collections per period.",
@@ -36950,14 +36972,24 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "Aggregated storage used in bytes per period.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
}
},
"required": [
"range",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"collections",
"documents"
"documents",
"storage"
]
},
"usageCollection": {
@@ -37498,6 +37530,12 @@
"x-example": 0,
"format": "int32"
},
"databasesStorageTotal": {
"type": "integer",
"description": "Total aggregated sum of databases storage size (in bytes).",
"x-example": 0,
"format": "int32"
},
"usersTotal": {
"type": "integer",
"description": "Total aggregated number of users.",
@@ -37594,6 +37632,14 @@
},
"x-example": []
},
"databasesStorageBreakdown": {
"type": "array",
"description": "An array of the aggregated breakdown of storage usage by databases.",
"items": {
"$ref": "#\/components\/schemas\/metricBreakdown"
},
"x-example": []
},
"executionsMbSecondsBreakdown": {
"type": "array",
"description": "Aggregated breakdown in totals of execution mbSeconds by functions.",
@@ -37623,6 +37669,7 @@
"executionsTotal",
"documentsTotal",
"databasesTotal",
"databasesStorageTotal",
"usersTotal",
"filesStorageTotal",
"functionsStorageTotal",
@@ -37637,6 +37684,7 @@
"executions",
"executionsBreakdown",
"bucketsBreakdown",
"databasesStorageBreakdown",
"executionsMbSecondsBreakdown",
"buildsMbSecondsBreakdown",
"functionsStorageBreakdown"
+50 -2
View File
@@ -36879,6 +36879,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total databases storage in bytes.",
"x-example": 0,
"format": "int32"
},
"databases": {
"type": "array",
"description": "Aggregated number of databases per period.",
@@ -36902,6 +36908,14 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "An array of the aggregated number of databases storage in bytes per period.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
}
},
"required": [
@@ -36909,9 +36923,11 @@
"databasesTotal",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"databases",
"collections",
"documents"
"documents",
"storage"
]
},
"usageDatabase": {
@@ -36935,6 +36951,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total storage used in bytes.",
"x-example": 0,
"format": "int32"
},
"collections": {
"type": "array",
"description": "Aggregated number of collections per period.",
@@ -36950,14 +36972,24 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "Aggregated storage used in bytes per period.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
}
},
"required": [
"range",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"collections",
"documents"
"documents",
"storage"
]
},
"usageCollection": {
@@ -37498,6 +37530,12 @@
"x-example": 0,
"format": "int32"
},
"databasesStorageTotal": {
"type": "integer",
"description": "Total aggregated sum of databases storage size (in bytes).",
"x-example": 0,
"format": "int32"
},
"usersTotal": {
"type": "integer",
"description": "Total aggregated number of users.",
@@ -37594,6 +37632,14 @@
},
"x-example": []
},
"databasesStorageBreakdown": {
"type": "array",
"description": "An array of the aggregated breakdown of storage usage by databases.",
"items": {
"$ref": "#\/components\/schemas\/metricBreakdown"
},
"x-example": []
},
"executionsMbSecondsBreakdown": {
"type": "array",
"description": "Aggregated breakdown in totals of execution mbSeconds by functions.",
@@ -37623,6 +37669,7 @@
"executionsTotal",
"documentsTotal",
"databasesTotal",
"databasesStorageTotal",
"usersTotal",
"filesStorageTotal",
"functionsStorageTotal",
@@ -37637,6 +37684,7 @@
"executions",
"executionsBreakdown",
"bucketsBreakdown",
"databasesStorageBreakdown",
"executionsMbSecondsBreakdown",
"buildsMbSecondsBreakdown",
"functionsStorageBreakdown"
+53 -2
View File
@@ -37402,6 +37402,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total databases storage in bytes.",
"x-example": 0,
"format": "int32"
},
"databases": {
"type": "array",
"description": "Aggregated number of databases per period.",
@@ -37428,6 +37434,15 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "An array of the aggregated number of databases storage in bytes per period.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
}
},
"required": [
@@ -37435,9 +37450,11 @@
"databasesTotal",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"databases",
"collections",
"documents"
"documents",
"storage"
]
},
"usageDatabase": {
@@ -37461,6 +37478,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total storage used in bytes.",
"x-example": 0,
"format": "int32"
},
"collections": {
"type": "array",
"description": "Aggregated number of collections per period.",
@@ -37478,14 +37501,25 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "Aggregated storage used in bytes per period.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
}
},
"required": [
"range",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"collections",
"documents"
"documents",
"storage"
]
},
"usageCollection": {
@@ -38053,6 +38087,12 @@
"x-example": 0,
"format": "int32"
},
"databasesStorageTotal": {
"type": "integer",
"description": "Total aggregated sum of databases storage size (in bytes).",
"x-example": 0,
"format": "int32"
},
"usersTotal": {
"type": "integer",
"description": "Total aggregated number of users.",
@@ -38155,6 +38195,15 @@
},
"x-example": []
},
"databasesStorageBreakdown": {
"type": "array",
"description": "An array of the aggregated breakdown of storage usage by databases.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metricBreakdown"
},
"x-example": []
},
"executionsMbSecondsBreakdown": {
"type": "array",
"description": "Aggregated breakdown in totals of execution mbSeconds by functions.",
@@ -38187,6 +38236,7 @@
"executionsTotal",
"documentsTotal",
"databasesTotal",
"databasesStorageTotal",
"usersTotal",
"filesStorageTotal",
"functionsStorageTotal",
@@ -38201,6 +38251,7 @@
"executions",
"executionsBreakdown",
"bucketsBreakdown",
"databasesStorageBreakdown",
"executionsMbSecondsBreakdown",
"buildsMbSecondsBreakdown",
"functionsStorageBreakdown"
+53 -2
View File
@@ -37402,6 +37402,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total databases storage in bytes.",
"x-example": 0,
"format": "int32"
},
"databases": {
"type": "array",
"description": "Aggregated number of databases per period.",
@@ -37428,6 +37434,15 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "An array of the aggregated number of databases storage in bytes per period.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
}
},
"required": [
@@ -37435,9 +37450,11 @@
"databasesTotal",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"databases",
"collections",
"documents"
"documents",
"storage"
]
},
"usageDatabase": {
@@ -37461,6 +37478,12 @@
"x-example": 0,
"format": "int32"
},
"storageTotal": {
"type": "integer",
"description": "Total aggregated number of total storage used in bytes.",
"x-example": 0,
"format": "int32"
},
"collections": {
"type": "array",
"description": "Aggregated number of collections per period.",
@@ -37478,14 +37501,25 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"storage": {
"type": "array",
"description": "Aggregated storage used in bytes per period.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
}
},
"required": [
"range",
"collectionsTotal",
"documentsTotal",
"storageTotal",
"collections",
"documents"
"documents",
"storage"
]
},
"usageCollection": {
@@ -38053,6 +38087,12 @@
"x-example": 0,
"format": "int32"
},
"databasesStorageTotal": {
"type": "integer",
"description": "Total aggregated sum of databases storage size (in bytes).",
"x-example": 0,
"format": "int32"
},
"usersTotal": {
"type": "integer",
"description": "Total aggregated number of users.",
@@ -38155,6 +38195,15 @@
},
"x-example": []
},
"databasesStorageBreakdown": {
"type": "array",
"description": "An array of the aggregated breakdown of storage usage by databases.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metricBreakdown"
},
"x-example": []
},
"executionsMbSecondsBreakdown": {
"type": "array",
"description": "Aggregated breakdown in totals of execution mbSeconds by functions.",
@@ -38187,6 +38236,7 @@
"executionsTotal",
"documentsTotal",
"databasesTotal",
"databasesStorageTotal",
"usersTotal",
"filesStorageTotal",
"functionsStorageTotal",
@@ -38201,6 +38251,7 @@
"executions",
"executionsBreakdown",
"bucketsBreakdown",
"databasesStorageBreakdown",
"executionsMbSecondsBreakdown",
"buildsMbSecondsBreakdown",
"functionsStorageBreakdown"
+31 -5
View File
@@ -5,6 +5,7 @@ use Appwrite\Detector\Detector;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Utopia\Database\Validator\CustomId;
@@ -452,7 +453,8 @@ App::post('/v1/databases')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) {
->inject('queueForUsage')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) {
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
@@ -502,6 +504,7 @@ App::post('/v1/databases')
}
$queueForEvents->setParam('databaseId', $database->getId());
$queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -733,7 +736,8 @@ App::delete('/v1/databases/:databaseId')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
->inject('queueForUsage')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -756,6 +760,9 @@ App::delete('/v1/databases/:databaseId')
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE));
$queueForUsage
->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation
$response->noContent();
});
@@ -2350,7 +2357,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -2435,6 +2443,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setContext('database', $db)
->setPayload($response->output($attribute, $model));
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
});
@@ -2810,8 +2821,10 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->inject('dbForProject')
->inject('user')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) {
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
@@ -3026,6 +3039,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->setContext('database', $database)
->setPayload($response->getPayload(), sensitive: $relationships);
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
});
App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
@@ -3642,8 +3658,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->inject('dbForProject')
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) {
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Usage $queueForUsage, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -3728,6 +3745,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->setContext('database', $database)
->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships);
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
});
@@ -3753,6 +3773,7 @@ App::get('/v1/databases/usage')
METRIC_DATABASES,
METRIC_COLLECTIONS,
METRIC_DOCUMENTS,
METRIC_DATABASES_STORAGE
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
@@ -3803,9 +3824,11 @@ App::get('/v1/databases/usage')
'databasesTotal' => $usage[$metrics[0]]['total'],
'collectionsTotal' => $usage[$metrics[1]]['total'],
'documentsTotal' => $usage[$metrics[2]]['total'],
'storageTotal' => $usage[$metrics[3]]['total'],
'databases' => $usage[$metrics[0]]['data'],
'collections' => $usage[$metrics[1]]['data'],
'documents' => $usage[$metrics[2]]['data'],
'storage' => $usage[$metrics[3]]['data'],
]), Response::MODEL_USAGE_DATABASES);
});
@@ -3837,6 +3860,7 @@ App::get('/v1/databases/:databaseId/usage')
$metrics = [
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS),
str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE)
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
@@ -3887,8 +3911,10 @@ App::get('/v1/databases/:databaseId/usage')
'range' => $range,
'collectionsTotal' => $usage[$metrics[0]]['total'],
'documentsTotal' => $usage[$metrics[1]]['total'],
'storageTotal' => $usage[$metrics[2]]['total'],
'collections' => $usage[$metrics[0]]['data'],
'documents' => $usage[$metrics[1]]['data'],
'storage' => $usage[$metrics[2]]['data'],
]), Response::MODEL_USAGE_DATABASE);
});
+3 -2
View File
@@ -1718,7 +1718,7 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.request.type', Response::CONTENT_TYPE_JSON)
->param('functionId', '', new UID(), 'Function ID.')
->param('body', '', new Payload(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true)
->param('async', false, new Boolean(true), 'Execute code in the background. Default value is false.', true)
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true)
@@ -1733,7 +1733,8 @@ App::post('/v1/functions/:functionId/executions')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
$async = \strval($async) === 'true' || \strval($async) === '1';
if (!$async && !is_null($scheduledAt)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.');
+21
View File
@@ -47,6 +47,7 @@ App::get('/v1/project/usage')
METRIC_USERS,
METRIC_BUCKETS,
METRIC_FILES_STORAGE,
METRIC_DATABASES_STORAGE,
METRIC_DEPLOYMENTS_STORAGE,
METRIC_BUILDS_STORAGE
],
@@ -56,6 +57,7 @@ App::get('/v1/project/usage')
METRIC_NETWORK_OUTBOUND,
METRIC_USERS,
METRIC_EXECUTIONS,
METRIC_DATABASES_STORAGE,
METRIC_EXECUTIONS_MB_SECONDS,
METRIC_BUILDS_MB_SECONDS
]
@@ -182,6 +184,23 @@ App::get('/v1/project/usage')
];
}, $dbForProject->find('buckets'));
$databasesStorageBreakdown = array_map(function ($database) use ($dbForProject) {
$id = $database->getId();
$name = $database->getAttribute('name');
$metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
return [
'resourceId' => $id,
'name' => $name,
'value' => $value['value'] ?? 0,
];
}, $dbForProject->find('databases'));
$functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
@@ -269,6 +288,7 @@ App::get('/v1/project/usage')
'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS],
'documentsTotal' => $total[METRIC_DOCUMENTS],
'databasesTotal' => $total[METRIC_DATABASES],
'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE],
'usersTotal' => $total[METRIC_USERS],
'bucketsTotal' => $total[METRIC_BUCKETS],
'filesStorageTotal' => $total[METRIC_FILES_STORAGE],
@@ -279,6 +299,7 @@ App::get('/v1/project/usage')
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
'bucketsBreakdown' => $bucketsBreakdown,
'databasesStorageBreakdown' => $databasesStorageBreakdown,
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
'functionsStorageBreakdown' => $functionsStorageBreakdown,
+1 -1
View File
@@ -95,7 +95,7 @@ $databaseListener = function (string $event, Document $document, Document $proje
$databaseInternalId = $parts[1] ?? 0;
$queueForUsage
->addMetric(METRIC_COLLECTIONS, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value)
;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
+6
View File
@@ -241,10 +241,16 @@ const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provid
const METRIC_SESSIONS = 'sessions';
const METRIC_DATABASES = 'databases';
const METRIC_COLLECTIONS = 'collections';
const METRIC_DATABASES_STORAGE = 'databases.storage';
const METRIC_DATABASES_STORAGE_DISK = 'databases.storage_disk';
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
const METRIC_DATABASE_ID_STORAGE_DISK = '{databaseInternalId}.databases.storage_disk';
const METRIC_DOCUMENTS = 'documents';
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE_DISK = '{databaseInternalId}.{collectionInternalId}.databases.storage_disk';
const METRIC_BUCKETS = 'buckets';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
+2
View File
@@ -531,6 +531,8 @@ $image = $this->getParam('image', '');
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_CONFIG
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
appwrite-worker-messaging:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
+7 -1
View File
@@ -51,7 +51,7 @@
"utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.53.*",
"utopia-php/database": "0.53.5-rc1",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
@@ -97,5 +97,11 @@
"platform": {
"php": "8.3"
}
},
"repositories": {
"utopia-php/database": {
"type": "vcs",
"url": "https://github.com/utopia-php/database"
}
}
}
Generated
+67 -43
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3a25a9050c489b1a2cb4e320af1586d5",
"content-hash": "7066d9ca32e7a1a60614effdc4701970",
"packages": [
{
"name": "adhocore/jwt",
@@ -65,16 +65,16 @@
},
{
"name": "appwrite/appwrite",
"version": "11.1.0",
"version": "10.1.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-for-php.git",
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3"
"reference": "da579af70723cfc117b5af84375bdef117e27312"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/da579af70723cfc117b5af84375bdef117e27312",
"reference": "da579af70723cfc117b5af84375bdef117e27312",
"shasum": ""
},
"require": {
@@ -83,8 +83,7 @@
"php": ">=7.1.0"
},
"require-dev": {
"mockery/mockery": "^1.6.6",
"phpunit/phpunit": "^10"
"phpunit/phpunit": "3.7.35"
},
"type": "library",
"autoload": {
@@ -100,10 +99,10 @@
"support": {
"email": "team@appwrite.io",
"issues": "https://github.com/appwrite/sdk-for-php/issues",
"source": "https://github.com/appwrite/sdk-for-php/tree/11.1.0",
"source": "https://github.com/appwrite/sdk-for-php/tree/10.1.0",
"url": "https://appwrite.io/support"
},
"time": "2024-06-26T07:03:23+00:00"
"time": "2023-11-20T09:56:12+00:00"
},
{
"name": "appwrite/php-clamav",
@@ -1724,16 +1723,16 @@
},
{
"name": "utopia-php/database",
"version": "0.53.4",
"version": "0.53.5-rc1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "36a0e89d983afc1368635282e04fa762220a1d2a"
"reference": "689ba22063bf46def385da8695ba7a921e81e38d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/36a0e89d983afc1368635282e04fa762220a1d2a",
"reference": "36a0e89d983afc1368635282e04fa762220a1d2a",
"url": "https://api.github.com/repos/utopia-php/database/zipball/689ba22063bf46def385da8695ba7a921e81e38d",
"reference": "689ba22063bf46def385da8695ba7a921e81e38d",
"shasum": ""
},
"require": {
@@ -1760,7 +1759,38 @@
"Utopia\\Database\\": "src/Database"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"Tests\\E2E\\": "tests/e2e",
"Tests\\Unit\\": "tests/unit"
}
},
"scripts": {
"build": [
"Composer\\Config::disableProcessTimeout",
"docker compose build"
],
"start": [
"Composer\\Config::disableProcessTimeout",
"docker compose up -d"
],
"test": [
"Composer\\Config::disableProcessTimeout",
"docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml"
],
"lint": [
"./vendor/bin/pint --test"
],
"format": [
"./vendor/bin/pint"
],
"check": [
"./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 512M"
],
"coverage": [
"./vendor/bin/coverage-check ./tmp/clover.xml 90"
]
},
"license": [
"MIT"
],
@@ -1773,10 +1803,10 @@
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.53.4"
"source": "https://github.com/utopia-php/database/tree/0.53.5-rc1",
"issues": "https://github.com/utopia-php/database/issues"
},
"time": "2024-09-10T10:19:57+00:00"
"time": "2024-09-24T08:43:10+00:00"
},
{
"name": "utopia-php/domains",
@@ -2175,35 +2205,27 @@
},
{
"name": "utopia-php/migration",
"version": "0.6.1",
"version": "0.5.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "6a064179dd0a7278bfbaf65d9abdef19d6d2bbe0"
"reference": "f18d44d4459f78c292dac9edde856fd156fe497a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/6a064179dd0a7278bfbaf65d9abdef19d6d2bbe0",
"reference": "6a064179dd0a7278bfbaf65d9abdef19d6d2bbe0",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a",
"reference": "f18d44d4459f78c292dac9edde856fd156fe497a",
"shasum": ""
},
"require": {
"appwrite/appwrite": "11.1.*",
"ext-curl": "*",
"ext-openssl": "*",
"php": "8.3.*",
"utopia-php/database": "0.53.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",
"utopia-php/storage": "0.18.*"
"appwrite/appwrite": "10.1.0",
"php": "8.*"
},
"require-dev": {
"ext-pdo": "*",
"laravel/pint": "1.17.*",
"phpstan/phpstan": "1.11.*",
"phpunit/phpunit": "11.2.*",
"utopia-php/cli": "0.16.*",
"vlucas/phpdotenv": "5.6.*"
"laravel/pint": "1.*",
"phpunit/phpunit": "9.*",
"utopia-php/cli": "^0.18.0",
"vlucas/phpdotenv": "5.*"
},
"type": "library",
"autoload": {
@@ -2225,9 +2247,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.6.1"
"source": "https://github.com/utopia-php/migration/tree/0.5.2"
},
"time": "2024-09-24T09:54:09+00:00"
"time": "2024-07-22T09:27:07+00:00"
},
{
"name": "utopia-php/mongo",
@@ -3323,16 +3345,16 @@
},
{
"name": "laravel/pint",
"version": "v1.18.0",
"version": "v1.17.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "eb2bcbd85034c1a508cf409b7ee318eb3137f090"
"reference": "9d77be916e145864f10788bb94531d03e1f7b482"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/eb2bcbd85034c1a508cf409b7ee318eb3137f090",
"reference": "eb2bcbd85034c1a508cf409b7ee318eb3137f090",
"url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482",
"reference": "9d77be916e145864f10788bb94531d03e1f7b482",
"shasum": ""
},
"require": {
@@ -3385,7 +3407,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2024-09-24T15:48:42+00:00"
"time": "2024-09-03T15:00:28+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -7004,7 +7026,9 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"utopia-php/database": 5
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+185
View File
@@ -4,6 +4,7 @@ namespace Appwrite\Platform\Workers;
use Appwrite\Extend\Exception;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
@@ -11,6 +12,10 @@ use Utopia\Platform\Action;
use Utopia\Queue\Message;
use Utopia\System\System;
const METRIC_COLLECTION_LEVEL_STORAGE = 4;
const METRIC_DATABASE_LEVEL_STORAGE = 3;
const METRIC_PROJECT_LEVEL_STORAGE = 2;
class UsageDump extends Action
{
protected array $stats = [];
@@ -70,6 +75,15 @@ class UsageDump extends Action
continue;
}
if (str_contains($key, METRIC_DATABASES_STORAGE)) {
try {
$this->handleDatabaseStorage($key, $dbForProject);
} catch (\Exception $e) {
console::error('[' . DateTime::now() . '] failed to calculate database storage for key [' . $key . '] ' . $e->getMessage());
}
continue;
}
foreach ($this->periods as $period => $format) {
$time = 'inf' === $period ? null : date($format, time());
$id = \md5("{$time}_{$period}_{$key}");
@@ -107,4 +121,175 @@ class UsageDump extends Action
}
}
}
private function handleDatabaseStorage(string $key, Database $dbForProject): void
{
$data = explode('.', $key);
$start = microtime(true);
$updateMetric = function (Database $dbForProject, int $value, string $key, string $period, string|null $time) {
$id = \md5("{$time}_{$period}_{$key}");
try {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'period' => $period,
'time' => $time,
'metric' => $key,
'value' => $value,
'region' => System::getEnv('_APP_REGION', 'default'),
]));
} catch (Duplicate $th) {
if ($value < 0) {
$dbForProject->decreaseDocumentAttribute(
'stats',
$id,
'value',
abs($value)
);
} else {
$dbForProject->increaseDocumentAttribute(
'stats',
$id,
'value',
$value
);
}
}
};
foreach ($this->periods as $period => $format) {
$time = 'inf' === $period ? null : date($format, time());
$id = \md5("{$time}_{$period}_{$key}");
$value = 0;
$diskValue = 0;
$previousValue = 0;
try {
$previousValue = ($dbForProject->getDocument('stats', $id))->getAttribute('value', 0);
} catch (\Exception $e) {
// No previous value
}
switch (count($data)) {
// Collection Level
case METRIC_COLLECTION_LEVEL_STORAGE:
Console::log('[' . DateTime::now() . '] Collection Level Storage Calculation [' . $key . ']');
$databaseInternalId = $data[0];
$collectionInternalId = $data[1];
try {
$value = $dbForProject->getSizeOfCollection('database_'.$databaseInternalId.'_collection_'.$collectionInternalId);
$diskValue = $dbForProject->getSizeOfCollectionOnDisk('database_'.$databaseInternalId.'_collection_'.$collectionInternalId);
} catch (\Exception $e) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
}
// Compare with previous value
$diff = $value - $previousValue;
$diskDiff = $diskValue - $previousValue;
if ($diff === 0 && $diskDiff === 0) {
break;
}
// Update Collection
$updateMetric($dbForProject, $diff, $key, $period, $time);
$updateMetric($dbForProject, $diskDiff, $key . '_disk', $period, $time);
// Update Database
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $databaseKey . '_disk', $period, $time);
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $projectKey . '_disk', $period, $time);
break;
// Database Level
case METRIC_DATABASE_LEVEL_STORAGE:
Console::log('[' . DateTime::now() . '] Database Level Storage Calculation [' . $key . ']');
$databaseInternalId = $data[0];
$collections = [];
try {
$collections = $dbForProject->find('database_' . $databaseInternalId);
} catch (\Exception $e) {
// Database not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
}
foreach ($collections as $collection) {
try {
$value += $dbForProject->getSizeOfCollection('database_'.$databaseInternalId.'_collection_'.$collection->getInternalId());
$diskValue += $dbForProject->getSizeOfCollectionOnDisk('database_'.$databaseInternalId.'_collection_'.$collection->getInternalId());
} catch (\Exception $e) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
}
}
$diff = $value - $previousValue;
$diskDiff = $diskValue - $previousValue;
if ($diff === 0 && $diskDiff === 0) {
break;
}
// Update Database
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $databaseKey . '_disk', $period, $time);
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $projectKey . '_disk', $period, $time);
break;
// Project Level
case METRIC_PROJECT_LEVEL_STORAGE:
Console::log('[' . DateTime::now() . '] Project Level Storage Calculation [' . $key . ']');
// Get all project databases
$databases = $dbForProject->find('database');
// Recalculate all databases
foreach ($databases as $database) {
$collections = $dbForProject->find('database_' . $database->getInternalId());
foreach ($collections as $collection) {
try {
$value += $dbForProject->getSizeOfCollection('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId());
$diskValue += $dbForProject->getSizeOfCollectionOnDisk('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId());
} catch (\Exception $e) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
}
}
}
$diff = $value - $previousValue;
$diskDiff = $diskValue - $previousValue;
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $projectKey . '_disk', $period, $time);
break;
}
}
$end = microtime(true);
console::log('[' . DateTime::now() . '] DB Storage Calculation [' . $key . '] took ' . (($end - $start) * 1000) . ' milliseconds');
}
}
@@ -28,6 +28,12 @@ class UsageDatabase extends Model
'default' => 0,
'example' => 0,
])
->addRule('storageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of total storage used in bytes.',
'default' => 0,
'example' => 0,
])
->addRule('collections', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of collections per period.',
@@ -42,6 +48,13 @@ class UsageDatabase extends Model
'example' => [],
'array' => true
])
->addRule('storage', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated storage used in bytes per period.',
'default' => [],
'example' => [],
'array' => true
])
;
}
@@ -34,6 +34,12 @@ class UsageDatabases extends Model
'default' => 0,
'example' => 0,
])
->addRule('storageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of total databases storage in bytes.',
'default' => 0,
'example' => 0,
])
->addRule('databases', [
'type' => Response::MODEL_METRIC,
'description' => 'Aggregated number of databases per period.',
@@ -55,6 +61,13 @@ class UsageDatabases extends Model
'example' => [],
'array' => true
])
->addRule('storage', [
'type' => Response::MODEL_METRIC,
'description' => 'An array of the aggregated number of databases storage in bytes per period.',
'default' => [],
'example' => [],
'array' => true
])
;
}
@@ -28,6 +28,12 @@ class UsageProject extends Model
'default' => 0,
'example' => 0,
])
->addRule('databasesStorageTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated sum of databases storage size (in bytes).',
'default' => 0,
'example' => 0,
])
->addRule('usersTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total aggregated number of users.',
@@ -118,6 +124,13 @@ class UsageProject extends Model
'example' => [],
'array' => true
])
->addRule('databasesStorageBreakdown', [
'type' => Response::MODEL_METRIC_BREAKDOWN,
'description' => 'An array of the aggregated breakdown of storage usage by databases.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('executionsMbSecondsBreakdown', [
'type' => Response::MODEL_METRIC_BREAKDOWN,
'description' => 'Aggregated breakdown in totals of execution mbSeconds by functions.',
+257 -3
View File
@@ -143,7 +143,7 @@ class UsageTest extends Scope
);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(20, count($response['body']));
$this->assertEquals(22, count($response['body']));
$this->validateDates($response['body']['network']);
$this->validateDates($response['body']['requests']);
$this->validateDates($response['body']['users']);
@@ -324,7 +324,7 @@ class UsageTest extends Scope
]
);
$this->assertEquals(20, count($response['body']));
$this->assertEquals(22, count($response['body']));
$this->assertEquals(1, count($response['body']['requests']));
$this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']);
$this->validateDates($response['body']['requests']);
@@ -545,7 +545,7 @@ class UsageTest extends Scope
]
);
$this->assertEquals(20, count($response['body']));
$this->assertEquals(22, count($response['body']));
$this->assertEquals(1, count($response['body']['requests']));
$this->assertEquals(1, count($response['body']['network']));
$this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']);
@@ -590,6 +590,260 @@ class UsageTest extends Scope
return $data;
}
public function testDatabaseStoragePrepare(): array
{
$response = $this->client->call(
Client::METHOD_POST,
'/databases',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'databaseId' => 'unique()',
'name' => 'dbStorageStats',
]
);
$this->assertNotEmpty($response['body']['$id']);
$databaseId = $response['body']['$id'];
$response = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'collectionId' => 'unique()',
'name' => 'collectionStorageStats',
'documentSecurity' => false,
'permissions' => [
Permission::read(Role::any()),
Permission::create(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]
);
$this->assertNotEmpty($response['body']['$id']);
$collectionId = $response['body']['$id'];
$response = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes' . '/string',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'key' => 'data',
'size' => 100000,
'required' => true,
]
);
return [
'databaseId' => $databaseId,
'collectionId' => $collectionId,
];
}
// /** @depends testDatabaseStoragePrepare */
// #[Retry(count: 1)]
// public function testDatabaseStorageStatsCreateDocument(array $data): array
// {
// $databaseId = $data['databaseId'];
// $collectionId = $data['collectionId'];
// $originalProjectMetrics = $this->client->call(
// Client::METHOD_GET,
// '/project/usage',
// $this->getConsoleHeaders(),
// [
// 'period' => '1d',
// 'startDate' => self::getToday(),
// 'endDate' => self::getTomorrow(),
// ]
// );
// $this->assertEquals(200, $originalProjectMetrics['headers']['status-code']);
// $this->assertArrayHasKey('databasesStorageTotal', $originalProjectMetrics['body']);
// $originalProjectMetrics = $originalProjectMetrics['body'];
// $originalDatabaseMetrics = $this->client->call(
// Client::METHOD_GET,
// '/databases/' . $databaseId . '/usage?range=30d',
// $this->getConsoleHeaders()
// );
// $this->assertEquals(200, $originalDatabaseMetrics['headers']['status-code']);
// $this->assertArrayHasKey('storageTotal', $originalDatabaseMetrics['body']);
// $originalDatabaseMetrics = $originalDatabaseMetrics['body'];
// // Create documents
// for ($i = 0; $i < 100; $i++) {
// $response = $this->client->call(
// Client::METHOD_POST,
// '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents',
// array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id']
// ], $this->getHeaders()),
// [
// 'documentId' => 'unique()',
// 'data' => ['data' => str_repeat('a', 10000)],
// ]
// );
// $this->assertEquals(201, $response['headers']['status-code']);
// }
// sleep(self::WAIT);
// for ($i = 0; $i < 3; $i++) {
// try {
// $newProjectMetrics = $this->client->call(
// Client::METHOD_GET,
// '/project/usage',
// $this->getConsoleHeaders(),
// [
// 'period' => '1d',
// 'startDate' => self::getToday(),
// 'endDate' => self::getTomorrow(),
// ]
// );
// $this->assertEquals(200, $newProjectMetrics['headers']['status-code']);
// $this->assertArrayHasKey('databasesStorageTotal', $newProjectMetrics['body']);
// $this->assertGreaterThan($originalProjectMetrics['databasesStorageTotal'], $newProjectMetrics['body']['databasesStorageTotal']);
// $newProjectMetrics = $newProjectMetrics['body'];
// $newDatabaseMetrics = $this->client->call(
// Client::METHOD_GET,
// '/databases/' . $databaseId . '/usage?range=30d',
// $this->getConsoleHeaders()
// );
// $this->assertEquals(200, $newDatabaseMetrics['headers']['status-code']);
// $this->assertArrayHasKey('storageTotal', $newDatabaseMetrics['body']);
// $this->assertGreaterThan($originalDatabaseMetrics['storageTotal'], $newDatabaseMetrics['body']['storageTotal']);
// $newDatabaseMetrics = $newDatabaseMetrics['body'];
// return [
// 'databaseId' => $databaseId,
// 'collectionId' => $collectionId,
// 'currentProjectMetrics' => $newProjectMetrics,
// 'currentDatabaseMetrics' => $newDatabaseMetrics,
// ];
// } catch (ExpectationFailedException $e) {
// if ($i === 2) {
// throw $e;
// }
// sleep(self::WAIT);
// continue;
// }
// }
// }
// /** @depends testDatabaseStorageStatsCreateDocument */
// #[Retry(count: 1)]
// public function testDatabaseStorageStatsDeleteDocument(array $data): array
// {
// $databaseId = $data['databaseId'];
// $collectionId = $data['collectionId'];
// $currentProjectMetrics = $data['currentProjectMetrics'];
// $currentDatabaseMetrics = $data['currentDatabaseMetrics'];
// $documents = $this->client->call(
// Client::METHOD_GET,
// '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents',
// array_merge([
// 'x-appwrite-project' => $this->getProject()['$id']
// ], $this->getHeaders()),
// [
// 'queries' => [
// Query::limit(50)->toString()
// ]
// ]
// );
// foreach ($documents['body']['documents'] as $document) {
// $response = $this->client->call(
// Client::METHOD_DELETE,
// '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document['$id'],
// array_merge([
// 'x-appwrite-project' => $this->getProject()['$id']
// ], $this->getHeaders())
// );
// $this->assertEquals(204, $response['headers']['status-code']);
// }
// sleep(self::WAIT);
// for ($i = 0; $i < 3; $i++) {
// try {
// $newProjectMetrics = $this->client->call(
// Client::METHOD_GET,
// '/project/usage',
// $this->getConsoleHeaders(),
// [
// 'period' => '1d',
// 'startDate' => self::getToday(),
// 'endDate' => self::getTomorrow(),
// ]
// );
// $this->assertEquals(200, $newProjectMetrics['headers']['status-code']);
// $this->assertArrayHasKey('databasesStorageTotal', $newProjectMetrics['body']);
// $this->assertLessThan($currentProjectMetrics['databasesStorageTotal'], $newProjectMetrics['body']['databasesStorageTotal']);
// $newProjectMetrics = $newProjectMetrics['body'];
// $newDatabaseMetrics = $this->client->call(
// Client::METHOD_GET,
// '/databases/' . $databaseId . '/usage?range=30d',
// $this->getConsoleHeaders()
// );
// $this->assertEquals(200, $newDatabaseMetrics['headers']['status-code']);
// $this->assertArrayHasKey('storageTotal', $newDatabaseMetrics['body']);
// $this->assertLessThan($currentDatabaseMetrics['storageTotal'], $newDatabaseMetrics['body']['storageTotal']);
// $newDatabaseMetrics = $newDatabaseMetrics['body'];
// return [
// 'databaseId' => $databaseId,
// 'collectionId' => $collectionId,
// 'currentProjectMetrics' => $newProjectMetrics,
// 'currentDatabaseMetrics' => $newDatabaseMetrics,
// ];
// } catch (ExpectationFailedException $e) {
// if ($i === 2) {
// throw $e;
// }
// sleep(self::WAIT);
// continue;
// }
// }
// $newProjectMetrics = $this->client->call(
// Client::METHOD_GET,
// '/project/usage',
// $this->getConsoleHeaders(),
// [
// 'period' => '1d',
// 'startDate' => self::getToday(),
// 'endDate' => self::getTomorrow(),
// ]
// );
// }
/** @depends testDatabaseStats */
public function testPrepareFunctionsStats(array $data): array
@@ -224,7 +224,7 @@ class DatabasesConsoleClientTest extends Scope
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(5, count($response['body']));
$this->assertEquals(7, count($response['body']));
$this->assertEquals('24h', $response['body']['range']);
$this->assertIsNumeric($response['body']['documentsTotal']);
$this->assertIsNumeric($response['body']['collectionsTotal']);