diff --git a/app/init/constants.php b/app/init/constants.php index f2127cd666..57763df911 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -351,6 +351,8 @@ const METRIC_BUILDS_STORAGE = 'builds.storage'; const METRIC_BUILDS_COMPUTE = 'builds.compute'; const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success'; const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed'; +const METRIC_SCREENSHOTS_SUCCESS = 'screenshots.success'; +const METRIC_SCREENSHOTS_FAILED = 'screenshots.failed'; const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds'; const METRIC_EXECUTIONS = 'executions'; const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; @@ -363,6 +365,8 @@ const METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED = '{resourceType}.{resourceInternal const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE = '{resourceType}.{resourceInternalId}.builds.compute'; const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_SUCCESS = '{resourceType}.{resourceInternalId}.builds.compute.success'; const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_FAILED = '{resourceType}.{resourceInternalId}.builds.compute.failed'; +const METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_SUCCESS = '{resourceType}.{resourceInternalId}.screenshots.success'; +const METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_FAILED = '{resourceType}.{resourceInternalId}.screenshots.failed'; const METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS = '{resourceType}.{resourceInternalId}.builds.mbSeconds'; const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds'; const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage'; @@ -376,6 +380,8 @@ const METRIC_RESOURCE_TYPE_BUILDS_FAILED = '{resourceType}.builds.failed'; const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE = '{resourceType}.builds.compute'; const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_SUCCESS = '{resourceType}.builds.compute.success'; const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_FAILED = '{resourceType}.builds.compute.failed'; +const METRIC_RESOURCE_TYPE_SCREENSHOTS_SUCCESS = '{resourceType}.screenshots.success'; +const METRIC_RESOURCE_TYPE_SCREENSHOTS_FAILED = '{resourceType}.screenshots.failed'; const METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS = '{resourceType}.builds.mbSeconds'; const METRIC_RESOURCE_TYPE_BUILDS = '{resourceType}.builds'; const METRIC_RESOURCE_TYPE_BUILDS_STORAGE = '{resourceType}.builds.storage'; diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php index 065fe477eb..fa7e394710 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php @@ -3,9 +3,12 @@ namespace Appwrite\Platform\Modules\Functions\Workers; use Ahc\Jwt\JWT; +use Appwrite\Event\Message\Usage as UsageMessage; +use Appwrite\Event\Publisher\Usage as UsagePublisher; use Appwrite\Event\Realtime; use Appwrite\Permission; use Appwrite\Role; +use Appwrite\Usage\Context; use Exception; use Utopia\Compression\Compression; use Utopia\Config\Config; @@ -43,6 +46,7 @@ class Screenshots extends Action ->inject('dbForProject') ->inject('project') ->inject('deviceForFiles') + ->inject('publisherForUsage') ->callback($this->action(...)); } @@ -52,7 +56,8 @@ class Screenshots extends Action Database $dbForPlatform, Database $dbForProject, Document $project, - Device $deviceForFiles + Device $deviceForFiles, + UsagePublisher $publisherForUsage ): void { Console::log('Screenshot action started'); @@ -257,6 +262,13 @@ class Screenshots extends Action 'deploymentScreenshotDark' => $deployment->getAttribute('screenshotDark', ''), 'deploymentScreenshotLight' => $deployment->getAttribute('screenshotLight', ''), ])); + + $this->publishUsage( + project: $project, + site: $site, + publisherForUsage: $publisherForUsage, + metric: METRIC_SCREENSHOTS_SUCCESS + ); } catch (\Throwable $th) { Console::warning("Screenshot failed to generate:"); Console::warning($th->getMessage()); @@ -265,10 +277,51 @@ class Screenshots extends Action $date = \date('H:i:s'); $this->appendToLogs($dbForProject, $deployment->getId(), $queueForRealtime, "[$date] [appwrite] Screenshot capturing failed. Deployment will continue. \n"); + $this->publishUsage( + project: $project, + site: $site, + publisherForUsage: $publisherForUsage, + metric: METRIC_SCREENSHOTS_FAILED + ); + throw $th; } } + protected function publishUsage( + Document $project, + Document $site, + UsagePublisher $publisherForUsage, + string $metric + ): void { + [$resourceMetric, $resourceIdMetric] = match ($metric) { + METRIC_SCREENSHOTS_SUCCESS => [ + METRIC_RESOURCE_TYPE_SCREENSHOTS_SUCCESS, + METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_SUCCESS, + ], + METRIC_SCREENSHOTS_FAILED => [ + METRIC_RESOURCE_TYPE_SCREENSHOTS_FAILED, + METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_FAILED, + ], + default => throw new \InvalidArgumentException('Unknown screenshot metric: ' . $metric), + }; + + $usage = (new Context()) + ->addMetric($metric, 1) + ->addMetric(str_replace('{resourceType}', RESOURCE_TYPE_SITES, $resourceMetric), 1) + ->addMetric(str_replace( + ['{resourceType}', '{resourceInternalId}'], + [RESOURCE_TYPE_SITES, $site->getSequence()], + $resourceIdMetric + ), 1); + + $publisherForUsage->enqueue(new UsageMessage( + project: $project, + metrics: $usage->getMetrics(), + reduce: $usage->getReduce() + )); + } + protected function appendToLogs(Database $dbForProject, string $deploymentId, Realtime $queueForRealtime, string $logs) { $deployment = $dbForProject->getDocument('deployments', $deploymentId); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php index a6768462d1..a1af4bd8fd 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php @@ -87,6 +87,8 @@ class Get extends Base str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_SUCCESS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_SCREENSHOTS_FAILED), str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_REQUESTS), str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_INBOUND), str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_OUTBOUND), @@ -139,6 +141,9 @@ class Get extends Base $buildsTimeTotal = $usage[$metrics[4]]['total'] ?? 0; $buildsTotal = $usage[$metrics[2]]['total'] ?? 0; + $screenshotsSuccessTotal = $usage[$metrics[11]]['total'] ?? 0; + $screenshotsFailedTotal = $usage[$metrics[12]]['total'] ?? 0; + $screenshotsTotal = $screenshotsSuccessTotal + $screenshotsFailedTotal; $response->dynamic(new Document([ 'range' => $range, 'deploymentsTotal' => $usage[$metrics[0]]['total'], @@ -153,9 +158,12 @@ class Get extends Base 'executionsMbSecondsTotal' => $usage[$metrics[8]]['total'], 'buildsSuccessTotal' => $usage[$metrics[9]]['total'], 'buildsFailedTotal' => $usage[$metrics[10]]['total'], - 'requestsTotal' => $usage[$metrics[11]]['total'], - 'inboundTotal' => $usage[$metrics[12]]['total'], - 'outboundTotal' => $usage[$metrics[13]]['total'], + 'screenshotsSuccessTotal' => $screenshotsSuccessTotal, + 'screenshotsFailedTotal' => $screenshotsFailedTotal, + 'screenshotsSuccessRate' => $screenshotsTotal === 0 ? 0 : $screenshotsSuccessTotal / $screenshotsTotal, + 'requestsTotal' => $usage[$metrics[13]]['total'], + 'inboundTotal' => $usage[$metrics[14]]['total'], + 'outboundTotal' => $usage[$metrics[15]]['total'], 'deployments' => $usage[$metrics[0]]['data'], 'deploymentsStorage' => $usage[$metrics[1]]['data'], 'builds' => $usage[$metrics[2]]['data'], @@ -167,9 +175,11 @@ class Get extends Base 'executionsMbSeconds' => $usage[$metrics[8]]['data'], 'buildsSuccess' => $usage[$metrics[9]]['data'], 'buildsFailed' => $usage[$metrics[10]]['data'], - 'requests' => $usage[$metrics[11]]['data'], - 'inbound' => $usage[$metrics[12]]['data'], - 'outbound' => $usage[$metrics[13]]['data'], + 'screenshotsSuccess' => $usage[$metrics[11]]['data'], + 'screenshotsFailed' => $usage[$metrics[12]]['data'], + 'requests' => $usage[$metrics[13]]['data'], + 'inbound' => $usage[$metrics[14]]['data'], + 'outbound' => $usage[$metrics[15]]['data'], ]), Response::MODEL_USAGE_SITE); } } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php index a90cb0cab9..3b3c759fd5 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/XList.php @@ -74,6 +74,8 @@ class XList extends Base str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS), str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS_SUCCESS), str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_BUILDS_FAILED), + str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_SCREENSHOTS_SUCCESS), + str_replace("{resourceType}", RESOURCE_TYPE_SITES, METRIC_RESOURCE_TYPE_SCREENSHOTS_FAILED), METRIC_SITES_REQUESTS, METRIC_SITES_INBOUND, METRIC_SITES_OUTBOUND, @@ -122,6 +124,10 @@ class XList extends Base ]; } } + $screenshotsSuccessTotal = $usage[$metrics[12]]['total'] ?? 0; + $screenshotsFailedTotal = $usage[$metrics[13]]['total'] ?? 0; + $screenshotsTotal = $screenshotsSuccessTotal + $screenshotsFailedTotal; + $response->dynamic(new Document([ 'range' => $range, 'sitesTotal' => $usage[$metrics[0]]['total'], @@ -136,9 +142,12 @@ class XList extends Base 'executionsMbSecondsTotal' => $usage[$metrics[9]]['total'], 'buildsSuccessTotal' => $usage[$metrics[10]]['total'], 'buildsFailedTotal' => $usage[$metrics[11]]['total'], - 'requestsTotal' => $usage[$metrics[12]]['total'], - 'inboundTotal' => $usage[$metrics[13]]['total'], - 'outboundTotal' => $usage[$metrics[14]]['total'], + 'screenshotsSuccessTotal' => $screenshotsSuccessTotal, + 'screenshotsFailedTotal' => $screenshotsFailedTotal, + 'screenshotsSuccessRate' => $screenshotsTotal === 0 ? 0 : $screenshotsSuccessTotal / $screenshotsTotal, + 'requestsTotal' => $usage[$metrics[14]]['total'], + 'inboundTotal' => $usage[$metrics[15]]['total'], + 'outboundTotal' => $usage[$metrics[16]]['total'], 'sites' => $usage[$metrics[0]]['data'], 'deployments' => $usage[$metrics[1]]['data'], 'deploymentsStorage' => $usage[$metrics[2]]['data'], @@ -151,9 +160,11 @@ class XList extends Base 'executionsMbSeconds' => $usage[$metrics[9]]['data'], 'buildsSuccess' => $usage[$metrics[10]]['data'], 'buildsFailed' => $usage[$metrics[11]]['data'], - 'requests' => $usage[$metrics[12]]['data'], - 'inbound' => $usage[$metrics[13]]['data'], - 'outbound' => $usage[$metrics[14]]['data'], + 'screenshotsSuccess' => $usage[$metrics[12]]['data'], + 'screenshotsFailed' => $usage[$metrics[13]]['data'], + 'requests' => $usage[$metrics[14]]['data'], + 'inbound' => $usage[$metrics[15]]['data'], + 'outbound' => $usage[$metrics[16]]['data'], ]), Response::MODEL_USAGE_SITES); } } diff --git a/src/Appwrite/Utopia/Response/Model/UsageSite.php b/src/Appwrite/Utopia/Response/Model/UsageSite.php index c45dc831cd..27828426d3 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageSite.php +++ b/src/Appwrite/Utopia/Response/Model/UsageSite.php @@ -49,6 +49,38 @@ class UsageSite extends UsageFunction 'example' => [], 'array' => true ]) + ->addRule('screenshotsSuccessTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of successful site screenshots.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('screenshotsFailedTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of failed site screenshots.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('screenshotsSuccessRate', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Success rate of site screenshots from 0 to 1.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('screenshotsSuccess', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of successful site screenshots per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('screenshotsFailed', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of failed site screenshots per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageSites.php b/src/Appwrite/Utopia/Response/Model/UsageSites.php index fea87c7718..241e08e6ba 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageSites.php +++ b/src/Appwrite/Utopia/Response/Model/UsageSites.php @@ -122,6 +122,38 @@ class UsageSites extends Model 'example' => [], 'array' => true ]) + ->addRule('screenshotsSuccessTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of successful site screenshots.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('screenshotsFailedTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of failed site screenshots.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('screenshotsSuccessRate', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Success rate of site screenshots from 0 to 1.', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('screenshotsSuccess', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of successful site screenshots per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) + ->addRule('screenshotsFailed', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated number of failed site screenshots per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('deployments', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated number of sites deployment per period.', diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index f6eb963967..0d2c0a7ed7 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -1675,11 +1675,14 @@ class UsageTest extends Scope ); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(30, count($response['body'])); + $this->assertEquals(35, count($response['body'])); $this->assertEquals('30d', $response['body']['range']); $this->assertIsArray($response['body']['deployments']); $this->assertEquals($deploymentsSuccess, $response['body']['buildsSuccessTotal']); $this->assertEquals($deploymentsFailed, $response['body']['buildsFailedTotal']); + $this->assertGreaterThanOrEqual(1, $response['body']['screenshotsSuccessTotal']); + $this->assertEquals(0, $response['body']['screenshotsFailedTotal']); + $this->assertEquals(1.0, $response['body']['screenshotsSuccessRate']); $this->assertIsArray($response['body']['deploymentsStorage']); $this->assertIsNumeric($response['body']['deploymentsStorageTotal']); $this->assertIsNumeric($response['body']['buildsMbSecondsTotal']); @@ -1692,6 +1695,8 @@ class UsageTest extends Scope $this->assertIsArray($response['body']['executionsMbSeconds']); $this->assertIsArray($response['body']['buildsSuccess']); $this->assertIsArray($response['body']['buildsFailed']); + $this->assertIsArray($response['body']['screenshotsSuccess']); + $this->assertIsArray($response['body']['screenshotsFailed']); $this->assertIsArray($response['body']['requests']); $this->assertIsArray($response['body']['inbound']); $this->assertIsArray($response['body']['outbound']); @@ -1709,7 +1714,7 @@ class UsageTest extends Scope ); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(31, count($response['body'])); + $this->assertEquals(36, count($response['body'])); $this->assertEquals($response['body']['range'], '30d'); $this->assertIsArray($response['body']['sites']); $this->assertIsArray($response['body']['deployments']); @@ -1722,6 +1727,11 @@ class UsageTest extends Scope $this->assertIsArray($response['body']['executionsMbSeconds']); $this->assertIsArray($response['body']['buildsSuccess']); $this->assertIsArray($response['body']['buildsFailed']); + $this->assertGreaterThanOrEqual(1, $response['body']['screenshotsSuccessTotal']); + $this->assertEquals(0, $response['body']['screenshotsFailedTotal']); + $this->assertEquals(1.0, $response['body']['screenshotsSuccessRate']); + $this->assertIsArray($response['body']['screenshotsSuccess']); + $this->assertIsArray($response['body']['screenshotsFailed']); $this->assertIsArray($response['body']['requests']); $this->assertIsArray($response['body']['inbound']); $this->assertIsArray($response['body']['outbound']);