From b88cd8e5444a4f29c791e069d5ba767ea03e0556 Mon Sep 17 00:00:00 2001 From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:55:41 +0530 Subject: [PATCH] Add subdomain param in functions and tests --- app/config/services.php | 13 ++ .../Http/Functions/CreateFunction.php | 28 +++- tests/e2e/Scopes/ProjectCustom.php | 2 + .../Functions/FunctionsCustomServerTest.php | 22 +++ .../Services/Sites/SitesCustomServerTest.php | 155 ++++++++++++++++++ 5 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 tests/e2e/Services/Sites/SitesCustomServerTest.php diff --git a/app/config/services.php b/app/config/services.php index c4fb98c59a..e8789fcbec 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -173,6 +173,19 @@ return [ 'optional' => false, 'icon' => '', ], + 'sites' => [ + 'key' => 'sites', + 'name' => 'Sites', + 'subtitle' => 'The Sites Service allows you view, create and manage your Cloud Sites.', + 'description' => '/docs/services/sites.md', + 'controller' => 'api/sites.php', + 'sdk' => true, + 'docs' => true, + 'docsUrl' => 'https://appwrite.io/docs/sites', + 'tests' => false, + 'optional' => true, + 'icon' => '', // TODO: Update icon later + ], 'functions' => [ 'key' => 'functions', 'name' => 'Functions', diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/CreateFunction.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/CreateFunction.php index 4074eca1c0..14da817a3f 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/CreateFunction.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/CreateFunction.php @@ -21,6 +21,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Roles; use Utopia\Platform\Action; @@ -88,6 +89,7 @@ class CreateFunction extends Base App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT), App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT) ), 'Runtime specification for the function and builds.', true, ['plan']) + ->param('subdomain', '', new CustomId(), 'Unique custom sub-domain. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->inject('request') ->inject('response') ->inject('dbForProject') @@ -100,8 +102,27 @@ class CreateFunction extends Base ->callback([$this, 'action']); } - public function action(string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) + public function action(string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, string $subdomain, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) { + $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $ruleId = ''; + $routeSubdomain = ''; + $domain = ''; + + if (!empty($functionsDomain)) { + $ruleId = ID::unique(); + $routeSubdomain = $subdomain ?: ID::unique(); + $domain = "{$routeSubdomain}.{$functionsDomain}"; + + $subdomain = Authorization::skip(fn () => $dbForConsole->findOne('rules', [ + Query::equal('domain', [$domain]) + ])); + + if (!empty($subdomain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Subdomain already exists. Please choose a different subdomain.'); + } + } + $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); @@ -241,12 +262,7 @@ class CreateFunction extends Base ->setTemplate($template); } - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); if (!empty($functionsDomain)) { - $ruleId = ID::unique(); - $routeSubdomain = ID::unique(); - $domain = "{$routeSubdomain}.{$functionsDomain}"; - $rule = Authorization::skip( fn () => $dbForConsole->createDocument('rules', new Document([ '$id' => $ruleId, diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index ad2c93790c..69bf680f5e 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -74,6 +74,8 @@ trait ProjectCustom 'files.write', 'buckets.read', 'buckets.write', + 'sites.read', + 'sites.write', 'functions.read', 'functions.write', 'execution.read', diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 41b890caf6..f7a856a76f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -36,6 +36,7 @@ class FunctionsCustomServerTest extends Scope 'buckets.*.delete', ], 'timeout' => 10, + 'subdomain' => 'test' ]); $functionId = $functionId = $function['body']['$id'] ?? ''; @@ -1984,4 +1985,25 @@ class FunctionsCustomServerTest extends Scope $this->cleanupFunction($functionId); } + + /** + * @depends testCreateFunction + */ + public function testUniqueSubdomain(array $data): void + { + $function = $this->createFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'events' => [ + 'buckets.*.create', + 'buckets.*.delete', + ], + 'timeout' => 10, + 'subdomain' => 'test' + ]); + + $this->assertEquals(400, $function['headers']['status-code']); + } } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php new file mode 100644 index 0000000000..25ebfc9283 --- /dev/null +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -0,0 +1,155 @@ +createSite([ + 'siteId' => ID::unique(), + 'name' => 'Test', + 'framework' => 'sveltekit', + 'installCommand' => 'npm install --force', + 'buildCommand' => 'npm run build', + 'outputDirectory' => './build', + 'buildRuntime' => 'node-22', + 'serveRuntime' => 'static-1', + 'subdomain' => 'test' + ]); + + $siteId = $site['body']['$id'] ?? ''; + + $dateValidator = new DatetimeValidator(); + $this->assertEquals(201, $site['headers']['status-code']); + $this->assertNotEmpty($site['body']['$id']); + $this->assertEquals('Test', $site['body']['name']); + $this->assertEquals('sveltekit', $site['body']['framework']); + $this->assertEquals(true, $dateValidator->isValid($site['body']['$createdAt'])); + $this->assertEquals(true, $dateValidator->isValid($site['body']['$updatedAt'])); + $this->assertEquals('npm install --force', $site['body']['installCommand']); + $this->assertEquals('npm run build', $site['body']['buildCommand']); + $this->assertEquals('./build', $site['body']['outputDirectory']); + $this->assertEquals('node-22', $site['body']['buildRuntime']); + $this->assertEquals('static-1', $site['body']['serveRuntime']); + + $variable = $this->createVariable($siteId, [ + 'key' => 'siteKey1', + 'value' => 'siteValue1', + ]); + $variable2 = $this->createVariable($siteId, [ + 'key' => 'siteKey2', + 'value' => 'siteValue2', + ]); + $variable3 = $this->createVariable($siteId, [ + 'key' => 'siteKey3', + 'value' => 'siteValue3', + ]); + + $this->assertEquals(201, $variable['headers']['status-code']); + $this->assertEquals(201, $variable2['headers']['status-code']); + $this->assertEquals(201, $variable3['headers']['status-code']); + + return [ + 'siteId' => $siteId, + ]; + } + + /** + * @depends testCreateSite + */ + public function testGetSite(array $data): array + { + /** + * Test for SUCCESS + */ + $site = $this->getSite($data['siteId']); + + $this->assertEquals($site['headers']['status-code'], 200); + $this->assertEquals($site['body']['name'], 'Test'); + + /** + * Test for FAILURE + */ + $site = $this->getSite('x'); + + $this->assertEquals($site['headers']['status-code'], 404); + + return $data; + } + + /** + * @depends testGetSite + */ + public function testDeleteSite(array $data): array + { + /** + * Test for SUCCESS + */ + $site = $this->deleteSite($data['siteId']); + + $this->assertEquals(204, $site['headers']['status-code']); + $this->assertEmpty($site['body']); + + $site = $this->getSite($data['siteId']); + + $this->assertEquals(404, $site['headers']['status-code']); + + return $data; + } + + /** + * @depends testGetSite + */ + public function testUniqueSubdomain(array $data): void + { + /** + * Test for SUCCESS + */ + $site = $this->createSite([ + 'siteId' => ID::unique(), + 'name' => 'Test', + 'framework' => 'sveltekit', + 'installCommand' => 'npm install --force', + 'buildCommand' => 'npm run build', + 'outputDirectory' => './build', + 'buildRuntime' => 'node-22', + 'serveRuntime' => 'static-1', + 'subdomain' => 'test' + ]); + + $this->assertEquals(201, $site['headers']['status-code']); + + /** + * Test for FAILURE + */ + $site = $this->createSite([ + 'siteId' => ID::unique(), + 'name' => 'Test2', + 'framework' => 'sveltekit', + 'installCommand' => 'npm install --force', + 'buildCommand' => 'npm run build', + 'outputDirectory' => './build', + 'buildRuntime' => 'node-22', + 'serveRuntime' => 'static-1', + 'subdomain' => 'test' + ]); + + $this->assertEquals(400, $site['headers']['status-code']); + + return; + } +}