diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 5b06eb1a22..e77426ad76 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -643,7 +643,7 @@ App::get('/v1/avatars/screenshots') ->label('scope', 'avatars.read') ->label('cache', true) ->label('cache.resourceType', 'avatar/screenshot') - ->label('cache.resource', 'screenshot/{request.url}/{request.width}/{request.height}/{request.theme}/{request.userAgent}/{request.fullpage}/{request.locale}/{request.timezone}/{request.latitude}/{request.longitude}/{request.accuracy}/{request.touch}/{request.permissions}/{request.sleep}/{request.quality}/{request.output}') + ->label('cache.resource', 'screenshot/{request.url}/{request.width}/{request.height}/{request.scale}/{request.theme}/{request.userAgent}/{request.fullpage}/{request.locale}/{request.timezone}/{request.latitude}/{request.longitude}/{request.accuracy}/{request.touch}/{request.permissions}/{request.sleep}/{request.quality}/{request.output}') ->label('sdk', new Method( namespace: 'avatars', group: null, @@ -662,6 +662,7 @@ App::get('/v1/avatars/screenshots') ->param('url', '', new URL(['http', 'https']), 'Website URL which you want to capture.') ->param('headers', [], new Assoc(), 'HTTP headers to send with the browser request. Defaults to empty.', true) ->param('viewport', '1280x720', new Text(20), 'Browser viewport size. Pass a string like "1280x720" or "1920x1080". Defaults to "1280x720".', true) + ->param('scale', 1, new Range(0.1, 3, Range::TYPE_FLOAT), 'Browser scale factor. Pass a number between 0.1 to 3. Defaults to 1.', true) ->param('theme', 'light', new WhiteList(['light', 'dark']), 'Browser theme. Pass "light" or "dark". Defaults to "light".', true) ->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true) ->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true) @@ -678,7 +679,7 @@ App::get('/v1/avatars/screenshots') ->param('quality', -1, new Range(-1, 100), 'Screenshot quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true) ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) ->inject('response') - ->action(function (string $url, array $headers, string $viewport, string $theme, string $userAgent, bool $fullpage, string $locale, string $timezone, float $latitude, float $longitude, float $accuracy, bool $touch, array $permissions, int $sleep, int $width, int $height, int $quality, string $output, Response $response) { + ->action(function (string $url, array $headers, string $viewport, float $scale, string $theme, string $userAgent, bool $fullpage, string $locale, string $timezone, float $latitude, float $longitude, float $accuracy, bool $touch, array $permissions, int $sleep, int $width, int $height, int $quality, string $output, Response $response) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -719,7 +720,7 @@ App::get('/v1/avatars/screenshots') } // Create the config with headers as an object - // The custom browser service accepts: url, theme, headers, sleep, viewport, userAgent, fullPage, locale, timezoneId, geolocation, hasTouch + // The custom browser service accepts: url, theme, headers, sleep, viewport, userAgent, fullPage, locale, timezoneId, geolocation, hasTouch, scale $config = [ 'url' => $url, 'theme' => $theme, @@ -731,6 +732,11 @@ App::get('/v1/avatars/screenshots') ] ]; + // Add scale if not default + if ($scale != 1) { + $config['scale'] = $scale; + } + // Add fullPage to viewport if enabled if ($fullpage) { $config['viewport']['fullPage'] = true; @@ -779,6 +785,11 @@ App::get('/v1/avatars/screenshots') 'sleep' => $config['sleep'], 'viewport' => $config['viewport'] // Keep as object ]; + + // Add scale if not default + if ($scale != 1) { + $finalConfig['scale'] = $scale; + } // Add optional parameters that were set, preserving arrays as arrays if (!empty($userAgent)) { diff --git a/tests/e2e/Services/Avatars/AvatarsBase.php b/tests/e2e/Services/Avatars/AvatarsBase.php index 68bf24250f..9003f45208 100644 --- a/tests/e2e/Services/Avatars/AvatarsBase.php +++ b/tests/e2e/Services/Avatars/AvatarsBase.php @@ -836,6 +836,19 @@ trait AvatarsBase $this->assertEquals('image/png', $response['headers']['content-type']); $this->assertNotEmpty($response['body']); + // Test with scale parameter + $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999), + 'width' => 800, + 'height' => 600, + 'scale' => 2.0, + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('image/png', $response['headers']['content-type']); + $this->assertNotEmpty($response['body']); + // Test with userAgent parameter $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ 'x-appwrite-project' => $this->getProject()['$id'], @@ -953,6 +966,7 @@ trait AvatarsBase 'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999), 'width' => 800, 'height' => 600, + 'scale' => 1.5, 'theme' => 'dark', 'userAgent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', 'fullpage' => true, @@ -983,13 +997,35 @@ trait AvatarsBase $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ 'x-appwrite-project' => $this->getProject()['$id'], ], [ - 'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999), + 'url' => 'https://test' . time() . '.com', 'width' => 800, 'height' => 600, 'theme' => 'invalid-theme', ]); $this->assertEquals(400, $response['headers']['status-code']); + // Test invalid scale parameter (too small) + $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'url' => 'https://test' . time() . '.com', + 'width' => 800, + 'height' => 600, + 'scale' => 0.05, // Too small (min 0.1) + ]); + $this->assertEquals(400, $response['headers']['status-code']); + + // Test invalid scale parameter (too large) + $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'url' => 'https://test' . time() . '.com', + 'width' => 800, + 'height' => 600, + 'scale' => 5.0, // Too large (max 3.0) + ]); + $this->assertEquals(400, $response['headers']['status-code']); + // Test invalid userAgent parameter (too long) $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index 085f0eaecb..e7b4c2d993 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -247,6 +247,7 @@ class AvatarsTest extends Scope 'url' => 'https://appwrite.io', 'width' => 800, 'height' => 600, + 'scale' => 1.5, 'theme' => 'dark', 'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'fullpage' => true, diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 4b0d2630e6..9047ed4510 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -1781,8 +1781,8 @@ trait Base } }'; case self::GET_SCREENSHOT: - return 'query getScreenshot($url: String!, $width: Int, $height: Int, $theme: String, $userAgent: String, $fullpage: Boolean, $locale: String, $timezone: String, $latitude: Float, $longitude: Float, $accuracy: Float, $touch: Boolean, $permissions: [String!]) { - avatarsGetScreenshot(url: $url, width: $width, height: $height, theme: $theme, userAgent: $userAgent, fullpage: $fullpage, locale: $locale, timezone: $timezone, latitude: $latitude, longitude: $longitude, accuracy: $accuracy, touch: $touch, permissions: $permissions) { + return 'query getScreenshot($url: String!, $width: Int, $height: Int, $scale: Float, $theme: String, $userAgent: String, $fullpage: Boolean, $locale: String, $timezone: String, $latitude: Float, $longitude: Float, $accuracy: Float, $touch: Boolean, $permissions: [String!]) { + avatarsGetScreenshot(url: $url, width: $width, height: $height, scale: $scale, theme: $theme, userAgent: $userAgent, fullpage: $fullpage, locale: $locale, timezone: $timezone, latitude: $latitude, longitude: $longitude, accuracy: $accuracy, touch: $touch, permissions: $permissions) { status } }';