diff --git a/app/controllers/general.php b/app/controllers/general.php index 8defeb72f6..ff670c68ca 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -330,7 +330,16 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo throw $th; } } finally { + $fileSize = 0; + $file = $request->getFiles('file'); + if (!empty($file)) { + $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; + } + $queueForUsage + ->addMetric(METRIC_NETWORK_REQUESTS, 1) + ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize) + ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()) ->addMetric(METRIC_EXECUTIONS, 1) ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index f4e45c293a..32e9eb6454 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -6,6 +6,7 @@ use Appwrite\Functions\Specification; use Appwrite\Tests\Retry; use CURLFile; use DateTime; +use PHPUnit\Framework\ExpectationFailedException; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; @@ -13,6 +14,7 @@ use Tests\E2E\Scopes\SideServer; use Tests\E2E\Services\Functions\FunctionsBase; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; class UsageTest extends Scope @@ -761,7 +763,7 @@ class UsageTest extends Scope /** @depends testPrepareFunctionsStats */ #[Retry(count: 1)] - public function testFunctionsStats(array $data): void + public function testFunctionsStats(array $data): array { $functionId = $data['functionId']; $executionTime = $data['executionTime']; @@ -818,6 +820,126 @@ class UsageTest extends Scope $this->validateDates($response['body']['executionsTime']); $this->assertGreaterThan(0, $response['body']['buildsTime'][array_key_last($response['body']['buildsTime'])]['value']); $this->validateDates($response['body']['buildsTime']); + + return $data; + } + + /** @depends testFunctionsStats */ + public function testCustomDomainsFunctionStats(array $data): void + { + $functionId = $data['functionId']; + + $response = $this->client->call(Client::METHOD_PUT, '/functions/' . $functionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'name' => 'Test', + 'execute' => ['any'] + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('resourceId', [$functionId])->toString(), + Query::equal('resourceType', ['function'])->toString(), + ], + ]); + + $this->assertEquals(200, $rules['headers']['status-code']); + $this->assertEquals(1, $rules['body']['total']); + $this->assertCount(1, $rules['body']['rules']); + $this->assertNotEmpty($rules['body']['rules'][0]['domain']); + + $domain = $rules['body']['rules'][0]['domain']; + + $response = $this->client->call( + Client::METHOD_GET, + '/functions/' . $functionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(19, count($response['body'])); + $this->assertEquals('30d', $response['body']['range']); + + $functionsMetrics = $response['body']; + + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1h', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); + + $this->assertEquals(200, $response['headers']['status-code']); + + $projectMetrics = $response['body']; + + // Create custom domain execution + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + + sleep(self::WAIT + 20); + $tries = 0; + + while (true) { + try { + // Compare new values with old values + $response = $this->client->call( + Client::METHOD_GET, + '/functions/' . $functionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(19, count($response['body'])); + $this->assertEquals('30d', $response['body']['range']); + + // Check if the new values are greater than the old values + $this->assertEquals($functionsMetrics['executionsTotal'] + 1, $response['body']['executionsTotal']); + $this->assertGreaterThan($functionsMetrics['executionsTimeTotal'], $response['body']['executionsTimeTotal']); + $this->assertGreaterThan($functionsMetrics['executionsMbSecondsTotal'], $response['body']['executionsMbSecondsTotal']); + + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1h', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($projectMetrics['executionsTotal'] + 1, $response['body']['executionsTotal']); + $this->assertGreaterThan($projectMetrics['executionsMbSecondsTotal'], $response['body']['executionsMbSecondsTotal']); + + break; + } catch (ExpectationFailedException $th) { + if ($tries >= 5) { + throw $th; + } else { + $tries++; + sleep(5); + } + } + } } public function tearDown(): void diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 576dba3118..0607f40924 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -5,6 +5,7 @@ namespace Tests\E2E\Services\Functions; use Appwrite\Functions\Specification; use Appwrite\Tests\Retry; use CURLFile; +use PHPUnit\Framework\ExpectationFailedException; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; @@ -2435,16 +2436,31 @@ class FunctionsCustomServerTest extends Scope // Await Aggregation sleep(App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h' - ]); + $tries = 0; + while (true) { + try { + $response = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'range' => '24h' + ]); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(19, count($response['body'])); - $this->assertEquals('24h', $response['body']['range']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(19, count($response['body'])); + $this->assertEquals('24h', $response['body']['range']); + $this->assertEquals(1, $response['body']['executionsTotal']); + + break; + } catch (ExpectationFailedException $th) { + if ($tries >= 5) { + throw $th; + } else { + $tries++; + sleep(5); + } + } + } // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [