chore: composer update

This commit is contained in:
loks0n
2025-01-28 11:55:14 +00:00
6 changed files with 534 additions and 277 deletions
+1 -1
View File
@@ -58,7 +58,7 @@
"utopia-php/image": "0.7.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.13.*",
"utopia-php/messaging": "0.14.*",
"utopia-php/migration": "0.6.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.1",
Generated
+7 -7
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": "f56baa0fb5b78799aa64cfdbb8d92d44",
"content-hash": "84ab61b29ade142659ec0de1e098d752",
"packages": [
{
"name": "adhocore/jwt",
@@ -3878,16 +3878,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.13.0",
"version": "0.14.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "0e3e57351fe4fe875ef3ab9a01a7fff5f022de90"
"reference": "4ba356a3aa382802727f7e13e0f0152bcc1fc535"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/0e3e57351fe4fe875ef3ab9a01a7fff5f022de90",
"reference": "0e3e57351fe4fe875ef3ab9a01a7fff5f022de90",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/4ba356a3aa382802727f7e13e0f0152bcc1fc535",
"reference": "4ba356a3aa382802727f7e13e0f0152bcc1fc535",
"shasum": ""
},
"require": {
@@ -3923,9 +3923,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.13.0"
"source": "https://github.com/utopia-php/messaging/tree/0.14.1"
},
"time": "2024-12-05T08:36:07+00:00"
"time": "2025-01-28T06:14:28+00:00"
},
{
"name": "utopia-php/migration",
+149 -75
View File
@@ -22,6 +22,8 @@ use Utopia\Messaging\Adapter\Push\APNS;
use Utopia\Messaging\Adapter\Push as PushAdapter;
use Utopia\Messaging\Adapter\Push\FCM;
use Utopia\Messaging\Adapter\SMS as SMSAdapter;
use Utopia\Messaging\Adapter\SMS\Fast2SMS;
use Utopia\Messaging\Adapter\SMS\GEOSMS;
use Utopia\Messaging\Adapter\SMS\Mock;
use Utopia\Messaging\Adapter\SMS\Msg91;
use Utopia\Messaging\Adapter\SMS\Telesign;
@@ -46,6 +48,8 @@ class Messaging extends Action
{
private ?Local $localDevice = null;
private ?SMSAdapter $adapter = null;
public static function getName(): string
{
return 'messaging';
@@ -56,6 +60,9 @@ class Messaging extends Action
*/
public function __construct()
{
$this->adapter = $this->createInternalSMSAdapter();
$this
->desc('Messaging worker')
->inject('message')
@@ -381,100 +388,38 @@ class Messaging extends Action
private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Log $log): void
{
if (empty(System::getEnv('_APP_SMS_PROVIDER')) || empty(System::getEnv('_APP_SMS_FROM'))) {
throw new \Exception('Skipped SMS processing. Missing "_APP_SMS_PROVIDER" or "_APP_SMS_FROM" environment variables.');
if ($this->adapter === null) {
Console::warning('Skipped SMS processing. SMS adapter is not set.');
return;
}
if ($project->isEmpty()) {
throw new \Exception('Project not set in payload');
}
Console::log('Project: ' . $project->getId());
Console::log('Processing project: ' . $project->getId());
$denyList = System::getEnv('_APP_SMS_PROJECTS_DENY_LIST', '');
$denyList = explode(',', $denyList);
if (\in_array($project->getId(), $denyList)) {
Console::error('Project is in the deny list. Skipping...');
return;
}
$smsDSN = new DSN(System::getEnv('_APP_SMS_PROVIDER'));
$host = $smsDSN->getHost();
$password = $smsDSN->getPassword();
$user = $smsDSN->getUser();
$log->addTag('type', $host);
$from = System::getEnv('_APP_SMS_FROM');
$provider = new Document([
'$id' => ID::unique(),
'provider' => $host,
'type' => MESSAGE_TYPE_SMS,
'name' => 'Internal SMS',
'enabled' => true,
'credentials' => match ($host) {
'twilio' => [
'accountSid' => $user,
'authToken' => $password,
// Twilio Messaging Service SIDs always start with MG
// https://www.twilio.com/docs/messaging/services
'messagingServiceSid' => \str_starts_with($from, 'MG') ? $from : null
],
'textmagic' => [
'username' => $user,
'apiKey' => $password
],
'telesign' => [
'customerId' => $user,
'apiKey' => $password
],
'msg91' => [
'senderId' => $user,
'authKey' => $password,
'templateId' => $smsDSN->getParam('templateId', $from),
],
'vonage' => [
'apiKey' => $user,
'apiSecret' => $password
],
default => null
},
'options' => match ($host) {
'twilio' => [
'from' => \str_starts_with($from, 'MG') ? null : $from
],
default => [
'from' => $from
]
}
]);
$adapter = $this->getSmsAdapter($provider);
$batches = \array_chunk(
$from = System::getEnv('_APP_SMS_FROM', '');
$sms = new SMS(
$recipients,
$adapter->getMaxMessagesPerRequest()
$message->getAttribute('data')['content'],
$from
);
batch(\array_map(function ($batch) use ($message, $provider, $adapter) {
return function () use ($batch, $message, $provider, $adapter) {
$message->setAttribute('to', $batch);
$data = $this->buildSmsMessage($message, $provider);
try {
$adapter->send($data);
} catch (\Throwable $th) {
throw new \Exception('Failed sending to targets with error: ' . $th->getMessage());
}
};
}, $batches));
try {
$result = $this->adapter->send($sms);
} catch (\Throwable $th) {
throw new \Exception('Failed sending to targets with error: ' . $th->getMessage());
}
}
private function getSmsAdapter(Document $provider): ?SMSAdapter
{
$credentials = $provider->getAttribute('credentials');
@@ -504,6 +449,12 @@ class Messaging extends Action
$credentials['apiKey'] ?? '',
$credentials['apiSecret'] ?? ''
),
'fast2sms' => new Fast2SMS(
$credentials['apiKey'] ?? '',
$credentials['senderId'] ?? '',
$credentials['messageId'] ?? '',
$credentials['useDLT'] ?? true
),
default => null
};
}
@@ -720,4 +671,127 @@ class Messaging extends Action
return $this->localDevice;
}
private function createInternalSMSAdapter(): ?SMSAdapter
{
if (empty(System::getEnv('_APP_SMS_PROVIDER')) || empty(System::getEnv('_APP_SMS_FROM'))) {
Console::warning('Skipped SMS processing. Missing "_APP_SMS_PROVIDER" or "_APP_SMS_FROM" environment variables.');
return null;
}
$providers = System::getEnv('_APP_SMS_PROVIDER', '');
$dsns = [];
if (!empty($providers)) {
$providers = explode(',', $providers);
foreach ($providers as $provider) {
$dsns[] = new DSN($provider);
}
}
if (count($dsns) === 1) {
$provider = $this->createProviderFromDSN($dsns[0]);
$adapter = $this->getSmsAdapter($provider);
return $adapter;
}
$defaultDSN = null;
$localDSNs = [];
/** @var DSN $dsn */
foreach ($dsns as $dsn) {
if ($dsn->getParam('local', '') === 'default') {
$defaultDSN = $dsn;
} else {
$localDSNs[] = $dsn;
}
}
if ($defaultDSN === null) {
throw new \Exception('No default SMS provider found');
}
$defaultProvider = $this->createProviderFromDSN($defaultDSN);
$adapter = $this->getSmsAdapter($defaultProvider);
$geosms = new GEOSMS($adapter);
/** @var DSN $localDSN */
foreach ($localDSNs as $localDSN) {
try {
$provider = $this->createProviderFromDSN($localDSN);
$adapter = $this->getSmsAdapter($provider);
} catch (\Exception) {
Console::warning('Unable to create adapter: ' . $localDSN->getHost());
continue;
}
$callingCode = $localDSN->getParam('local', '');
if (empty($callingCode)) {
Console::warning('Unable to register adapter: ' . $localDSN->getHost() . '. Missing `local` parameter.');
continue;
}
$geosms->setLocal($callingCode, $adapter);
}
return $geosms;
}
private function createProviderFromDSN(DSN $dsn): Document
{
$host = $dsn->getHost();
$password = $dsn->getPassword();
$user = $dsn->getUser();
$from = System::getEnv('_APP_SMS_FROM');
$provider = new Document([
'$id' => ID::unique(),
'provider' => $host,
'type' => MESSAGE_TYPE_SMS,
'name' => 'Internal SMS',
'enabled' => true,
'credentials' => match ($host) {
'twilio' => [
'accountSid' => $user,
'authToken' => $password,
// Twilio Messaging Service SIDs always start with MG
// https://www.twilio.com/docs/messaging/services
'messagingServiceSid' => \str_starts_with($from, 'MG') ? $from : null
],
'textmagic' => [
'username' => $user,
'apiKey' => $password
],
'telesign' => [
'customerId' => $user,
'apiKey' => $password
],
'msg91' => [
'senderId' => $user,
'authKey' => $password,
'templateId' => $dsn->getParam('templateId', $from),
],
'vonage' => [
'apiKey' => $user,
'apiSecret' => $password
],
'fast2sms' => [
'senderId' => $user,
'apiKey' => $password,
'messageId' => $dsn->getParam('messageId'),
'useDLT' => $dsn->getParam('useDLT'),
],
default => null
},
'options' => match ($host) {
'twilio' => [
'from' => \str_starts_with($from, 'MG') ? null : $from
],
default => [
'from' => $from
]
}
]);
return $provider;
}
}
+10 -6
View File
@@ -30,13 +30,16 @@ class Usage extends Action
*/
public function __construct()
{
$this
->desc('Usage worker')
->inject('message')
->inject('project')
->inject('getProjectDB')
->inject('queueForUsageDump')
->callback([$this, 'action']);
->desc('Usage worker')
->inject('message')
->inject('project')
->inject('getProjectDB')
->inject('queueForUsageDump')
->callback(function (Message $message, Document $project, callable $getProjectDB, UsageDump $queueForUsageDump) {
$this->action($message, $project, $getProjectDB, $queueForUsageDump);
});
$this->aggregationInterval = (int) System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20');
$this->lastTriggeredTime = time();
@@ -58,6 +61,7 @@ class Usage extends Action
throw new Exception('Missing payload');
}
if (empty($project->getAttribute('database'))) {
var_dump($payload);
return;
+173 -188
View File
@@ -7,7 +7,7 @@ use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\NotFound;
use Utopia\Database\Exception\Duplicate;
use Utopia\Platform\Action;
use Utopia\Queue\Message;
use Utopia\System\System;
@@ -38,7 +38,9 @@ class UsageDump extends Action
$this
->inject('message')
->inject('getProjectDB')
->callback([$this, 'action']);
->callback(function (Message $message, callable $getProjectDB) {
$this->action($message, $getProjectDB);
});
}
/**
@@ -55,247 +57,230 @@ class UsageDump extends Action
throw new Exception('Missing payload');
}
try {
foreach ($payload['stats'] ?? [] as $stats) {
$project = new Document($stats['project'] ?? []);
$numberOfKeys = !empty($stats['keys']) ? \count($stats['keys']) : 0;
$receivedAt = $stats['receivedAt'] ?? 'NONE';
if ($numberOfKeys === 0) {
continue;
}
foreach ($payload['stats'] ?? [] as $stats) {
$project = new Document($stats['project'] ?? []);
/**
* End temp bug fallback
*/
$numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0;
$receivedAt = $stats['receivedAt'] ?? 'NONE';
if ($numberOfKeys === 0) {
continue;
}
console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys);
try {
$dbForProject = $getProjectDB($project);
$projectDocuments = [];
$databaseCache = [];
$collectionSizeCache = [];
Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys . ' Started');
$start = \microtime(true);
foreach ($stats['keys'] ?? [] as $key => $value) {
if ($value == 0) {
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());
$time = 'inf' === $period ? null : date($format, time());
$id = \md5("{$time}_{$period}_{$key}");
if (\str_contains($key, METRIC_DATABASES_STORAGE)) {
$this->handleDatabaseStorage(
$id,
$key,
$time,
$period,
$dbForProject,
$projectDocuments,
$databaseCache,
$collectionSizeCache
);
continue;
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
);
}
}
$projectDocuments[] = new Document([
'$id' => $id,
'period' => $period,
'time' => $time,
'metric' => $key,
'value' => $value,
'region' => System::getEnv('_APP_REGION', 'default'),
]);
}
}
$dbForProject->createOrUpdateDocumentsWithIncrease(
collection: 'stats',
attribute: 'value',
documents: $projectDocuments
);
$end = \microtime(true);
Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys. ' Time: '.($end - $start).'s');
} catch (\Exception $e) {
console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage());
}
} catch (\Exception $e) {
Console::error('[' . DateTime::now() . '] Error processing stats: ' . $e->getMessage());
}
}
private function handleDatabaseStorage(
string $id,
string $key,
?string $time,
string $period,
Database $dbForProject,
array &$projectDocuments,
array &$databaseCache,
array &$collectionSizeCache,
): void {
$data = \explode('.', $key);
$value = 0;
$previousValue = 0;
private function handleDatabaseStorage(string $key, Database $dbForProject): void
{
$data = explode('.', $key);
$start = microtime(true);
try {
$previousValue = $dbForProject
->getDocument('stats', $id)
->getAttribute('value', 0);
} catch (\Exception) {
// No previous value
}
$updateMetric = function (Database $dbForProject, int $value, string $key, string $period, string|null $time) {
$id = \md5("{$time}_{$period}_{$key}");
switch (\count($data)) {
case METRIC_COLLECTION_LEVEL_STORAGE:
$databaseInternalId = $data[0];
$collectionInternalId = $data[1];
$collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}";
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;
$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];
if (!isset($collectionSizeCache[$collectionId])) {
try {
$collectionSizeCache[$collectionId] = $dbForProject->getSizeOfCollection($collectionId);
$value = $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collectionInternalId);
} catch (\Exception $e) {
if (!$e instanceof NotFound) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
$collectionSizeCache[$collectionId] = 0;
}
}
$value = $collectionSizeCache[$collectionId];
// Compare with previous value
$diff = $value - $previousValue;
$diff = $value - $previousValue;
if ($diff === 0) {
if ($diff === 0) {
break;
}
// Update Collection
$updateMetric($dbForProject, $diff, $key, $period, $time);
// Update Database
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
break;
}
// Database Level
case METRIC_DATABASE_LEVEL_STORAGE:
Console::log('[' . DateTime::now() . '] Database Level Storage Calculation [' . $key . ']');
$databaseInternalId = $data[0];
$keys = [
$key,
\str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE),
METRIC_DATABASES_STORAGE
];
foreach ($keys as $metric) {
$projectDocuments[] = $this->createStatsDocument($id, $period, $time, $metric, $diff);
}
break;
case METRIC_DATABASE_LEVEL_STORAGE:
$databaseInternalId = $data[0];
$databaseId = "database_{$databaseInternalId}";
if (!isset($databaseCache[$databaseId])) {
$collections = [];
try {
$databaseCache[$databaseId] = $dbForProject->find($databaseId);
$collections = $dbForProject->find('database_' . $databaseInternalId);
} catch (\Exception $e) {
if (!$e instanceof NotFound) {
// Database not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
$databaseCache[$databaseId] = [];
}
}
foreach ($databaseCache[$databaseId] as $collection) {
$collectionId = "{$databaseId}_collection_{$collection->getInternalId()}";
if (!isset($collectionSizeCache[$collectionId])) {
foreach ($collections as $collection) {
try {
$collectionSizeCache[$collectionId] = $dbForProject->getSizeOfCollection($collectionId);
$value += $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId());
} catch (\Exception $e) {
if (!$e instanceof NotFound) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
$collectionSizeCache[$collectionId] = 0;
}
}
$value += $collectionSizeCache[$collectionId];
}
$diff = $value - $previousValue;
if ($diff === 0) {
$diff = $value - $previousValue;
if ($diff === 0) {
break;
}
// Update Database
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $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');
$keys = [
\str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE),
METRIC_DATABASES_STORAGE
];
// Recalculate all databases
foreach ($databases as $database) {
$collections = $dbForProject->find('database_' . $database->getInternalId());
foreach ($keys as $metric) {
$projectDocuments[] = $this->createStatsDocument($id, $period, $time, $metric, $diff);
}
break;
case METRIC_PROJECT_LEVEL_STORAGE:
if (!isset($databaseCache['*'])) {
try {
$databaseCache['*'] = $dbForProject->find('databases');
} catch (\Exception $e) {
if (!$e instanceof NotFound) {
throw $e;
}
$databaseCache['*'] = [];
}
}
foreach ($databaseCache['*'] as $database) {
$databaseId = "database_{$database->getInternalId()}";
if (!isset($databaseCache[$databaseId])) {
try {
$databaseCache[$databaseId] = $dbForProject->find($databaseId);
} catch (\Exception $e) {
if (!$e instanceof NotFound) {
throw $e;
}
$databaseCache[$databaseId] = [];
}
}
foreach ($databaseCache[$databaseId] as $collection) {
$collectionId = "{$databaseId}_collection_{$collection->getInternalId()}";
if (!isset($collectionSizeCache[$collectionId])) {
foreach ($collections as $collection) {
try {
$collectionSizeCache[$collectionId] = $dbForProject->getSizeOfCollection($collectionId);
$value += $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
} catch (\Exception $e) {
if (!$e instanceof NotFound) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
throw $e;
}
$collectionSizeCache[$collectionId] = 0;
}
}
$value += $collectionSizeCache[$collectionId];
}
}
$diff = $value - $previousValue;
if ($diff === 0) {
$diff = $value - $previousValue;
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
break;
}
$keys = [
METRIC_DATABASES_STORAGE
];
foreach ($keys as $metric) {
$projectDocuments[] = $this->createStatsDocument($id, $period, $time, $metric, $diff);
}
break;
}
}
}
private function createStatsDocument(
string $id,
string $period,
?string $time,
string $key,
int $diff,
): Document {
return new Document([
'$id' => $id,
'period' => $period,
'time' => $time,
'metric' => $key,
'value' => $diff,
'region' => System::getEnv('_APP_REGION', 'default'),
]);
$end = microtime(true);
console::log('[' . DateTime::now() . '] DB Storage Calculation [' . $key . '] took ' . (($end - $start) * 1000) . ' milliseconds');
}
}
+194
View File
@@ -651,6 +651,200 @@ class UsageTest extends Scope
];
}
// /** @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
{