diff --git a/CHANGES.md b/CHANGES.md index 55c0e4e2a..370c45596 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +# Version 1.2.1 +## Features + +## Changes + +## Bugs +- Add flutter-web as a platform type [#4992](https://github.com/appwrite/appwrite/pull/4992) + # Version 1.2.0 ## Features - Added GraphQL API [#974](https://github.com/appwrite/appwrite/pull/974) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index f8e07c12f..10a6f55c0 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1115,7 +1115,7 @@ App::post('/v1/projects/:projectId/platforms') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PLATFORM) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.') + ->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true) ->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true) diff --git a/app/init.php b/app/init.php index b05950d67..14072b0df 100644 --- a/app/init.php +++ b/app/init.php @@ -34,6 +34,7 @@ use Appwrite\GraphQL\Promises\Adapter\Swoole; use Appwrite\GraphQL\Schema; use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\IP; +use Appwrite\Network\Validator\Origin; use Appwrite\Network\Validator\URL; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Usage\Stats; @@ -754,7 +755,7 @@ App::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), 'name' => 'Current Host', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => $request->getHostname(), ], Document::SET_TYPE_APPEND); @@ -766,7 +767,7 @@ App::setResource('clients', function ($request, $console, $project) { fn ($node) => $node['hostname'], \array_filter( $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); @@ -777,7 +778,7 @@ App::setResource('clients', function ($request, $console, $project) { fn ($node) => $node['hostname'], \array_filter( $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ) ) @@ -910,7 +911,7 @@ App::setResource('console', function () { [ '$collection' => ID::custom('platforms'), 'name' => 'Localhost', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => 'localhost', ], // Current host is added on app init ], diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php index 9ee30a26a..3c3fff3cc 100644 --- a/src/Appwrite/Network/Validator/Origin.php +++ b/src/Appwrite/Network/Validator/Origin.php @@ -14,6 +14,7 @@ class Origin extends Validator public const CLIENT_TYPE_FLUTTER_MACOS = 'flutter-macos'; public const CLIENT_TYPE_FLUTTER_WINDOWS = 'flutter-windows'; public const CLIENT_TYPE_FLUTTER_LINUX = 'flutter-linux'; + public const CLIENT_TYPE_FLUTTER_WEB = 'flutter-web'; public const CLIENT_TYPE_APPLE_IOS = 'apple-ios'; public const CLIENT_TYPE_APPLE_MACOS = 'apple-macos'; public const CLIENT_TYPE_APPLE_WATCHOS = 'apple-watchos'; @@ -69,6 +70,7 @@ class Origin extends Validator switch ($type) { case self::CLIENT_TYPE_WEB: + case self::CLIENT_TYPE_FLUTTER_WEB: $this->clients[] = (isset($platform['hostname'])) ? $platform['hostname'] : ''; break; diff --git a/src/Appwrite/Utopia/Response/Model/Platform.php b/src/Appwrite/Utopia/Response/Model/Platform.php index 013c2d3e4..fa105299e 100644 --- a/src/Appwrite/Utopia/Response/Model/Platform.php +++ b/src/Appwrite/Utopia/Response/Model/Platform.php @@ -43,7 +43,7 @@ class Platform extends Model 'type' => self::TYPE_STRING, 'description' => 'Platform type. Possible values are: web, flutter-ios, flutter-android, ios, android, and unity.', 'default' => '', - 'example' => 'My Web App', + 'example' => 'web', ]) ->addRule('key', [ 'type' => self::TYPE_STRING, diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 5f0915aa0..0766da77d 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -1906,6 +1906,27 @@ class ProjectsConsoleClientTest extends Scope $data = array_merge($data, ['platformFultterAndroidId' => $response['body']['$id']]); + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'type' => 'flutter-web', + 'name' => 'Flutter App (Web)', + 'key' => '', + 'store' => '', + 'hostname' => 'flutter.appwrite.io', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('flutter-web', $response['body']['type']); + $this->assertEquals('Flutter App (Web)', $response['body']['name']); + $this->assertEquals('', $response['body']['key']); + $this->assertEquals('', $response['body']['store']); + $this->assertEquals('flutter.appwrite.io', $response['body']['hostname']); + + $data = array_merge($data, ['platformFultterWebId' => $response['body']['$id']]); + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2024,7 +2045,7 @@ class ProjectsConsoleClientTest extends Scope ], $this->getHeaders()), []); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(7, $response['body']['total']); + $this->assertEquals(8, $response['body']['total']); /** * Test for FAILURE @@ -2088,6 +2109,22 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals('', $response['body']['store']); $this->assertEquals('', $response['body']['hostname']); + $platformFultterWebId = $data['platformFultterWebId'] ?? ''; + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/platforms/' . $platformFultterWebId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($platformFultterWebId, $response['body']['$id']); + $this->assertEquals('flutter-web', $response['body']['type']); + $this->assertEquals('Flutter App (Web)', $response['body']['name']); + $this->assertEquals('', $response['body']['key']); + $this->assertEquals('', $response['body']['store']); + $this->assertEquals('flutter.appwrite.io', $response['body']['hostname']); + $platformAppleIosId = $data['platformAppleIosId'] ?? ''; $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/platforms/' . $platformAppleIosId, array_merge([ @@ -2235,6 +2272,27 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals('', $response['body']['store']); $this->assertEquals('', $response['body']['hostname']); + $platformFultterWebId = $data['platformFultterWebId'] ?? ''; + + $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $id . '/platforms/' . $platformFultterWebId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Flutter App (Web) 2', + 'key' => '', + 'store' => '', + 'hostname' => 'flutter2.appwrite.io', + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($platformFultterWebId, $response['body']['$id']); + $this->assertEquals('flutter-web', $response['body']['type']); + $this->assertEquals('Flutter App (Web) 2', $response['body']['name']); + $this->assertEquals('', $response['body']['key']); + $this->assertEquals('', $response['body']['store']); + $this->assertEquals('flutter2.appwrite.io', $response['body']['hostname']); + $platformAppleIosId = $data['platformAppleIosId'] ?? ''; $response = $this->client->call(Client::METHOD_PUT, '/projects/' . $id . '/platforms/' . $platformAppleIosId, array_merge([ @@ -2395,6 +2453,23 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); + $platformFultterWebId = $data['platformFultterWebId'] ?? ''; + + $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/platforms/' . $platformFultterWebId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/platforms/' . $platformFultterWebId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(404, $response['headers']['status-code']); + $platformAppleIosId = $data['platformAppleIosId'] ?? ''; $response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/platforms/' . $platformAppleIosId, array_merge([ diff --git a/tests/unit/Network/Validators/OriginTest.php b/tests/unit/Network/Validators/OriginTest.php index 2392ae4cf..cfbccfafd 100644 --- a/tests/unit/Network/Validators/OriginTest.php +++ b/tests/unit/Network/Validators/OriginTest.php @@ -14,21 +14,27 @@ class OriginTest extends TestCase [ '$collection' => ID::custom('platforms'), 'name' => 'Production', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => 'appwrite.io', ], [ '$collection' => ID::custom('platforms'), 'name' => 'Development', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => 'appwrite.test', ], [ '$collection' => ID::custom('platforms'), 'name' => 'Localhost', - 'type' => 'web', + 'type' => Origin::CLIENT_TYPE_WEB, 'hostname' => 'localhost', ], + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Flutter', + 'type' => Origin::CLIENT_TYPE_FLUTTER_WEB, + 'hostname' => 'appwrite.flutter', + ], ]); $this->assertEquals($validator->isValid('https://localhost'), true); @@ -43,6 +49,10 @@ class OriginTest extends TestCase $this->assertEquals($validator->isValid('http://appwrite.test'), true); $this->assertEquals($validator->isValid('http://appwrite.test:80'), true); + $this->assertEquals($validator->isValid('https://appwrite.flutter'), true); + $this->assertEquals($validator->isValid('http://appwrite.flutter'), true); + $this->assertEquals($validator->isValid('http://appwrite.flutter:80'), true); + $this->assertEquals($validator->isValid('https://example.com'), false); $this->assertEquals($validator->isValid('http://example.com'), false); $this->assertEquals($validator->isValid('http://example.com:80'), false);