diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index e933bb844..075dfc649 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -562,15 +562,15 @@ App::post('/v1/functions/:functionId/deployments') if ($activate) { // Remove deploy for all other deployments. - $deployments = $dbForProject->find('deployments', [ + $activeDeployments = $dbForProject->find('deployments', [ new Query('activate', Query::TYPE_EQUAL, [true]), new Query('resourceId', Query::TYPE_EQUAL, [$functionId]), new Query('resourceType', Query::TYPE_EQUAL, ['functions']) ]); - foreach ($deployments as $deployment) { - $deployment->setAttribute('activate', false); - $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + foreach ($activeDeployments as $activeDeployment) { + $activeDeployment->setAttribute('activate', false); + $dbForProject->updateDocument('deployments', $activeDeployment->getId(), $activeDeployment); } } diff --git a/composer.lock b/composer.lock index 870c1c041..3b0f38cf0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "71d2e3bdd2ee9ed2bd4c8ae2e62ea1a2", + "content-hash": "deeea4a225c55fccc04729750dd8c5ec", "packages": [ { "name": "adhocore/jwt", @@ -115,11 +115,11 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.7.2", + "version": "0.7.3", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "5021856c78f61c8dc542ca2fc704a8d1025583e2" + "reference": "a4c20be668c78f8a1ac03148de1c7c61d9bfde74" }, "require": { "php": ">=8.0", @@ -154,7 +154,7 @@ "php", "runtimes" ], - "time": "2022-02-20T20:31:52+00:00" + "time": "2022-02-21T23:55:23+00:00" }, { "name": "chillerlan/php-qrcode", @@ -626,12 +626,12 @@ } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3075,7 +3075,7 @@ "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator", - "reference": "86a8e495b14c6f112de71fa6c500d36dcded8e95" + "reference": "b3813a4c947d65a2aba7ac29cb8681bbbbc862c3" }, "require": { "ext-curl": "*", @@ -3110,7 +3110,7 @@ } ], "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", - "time": "2022-02-22T06:42:40+00:00" + "time": "2022-02-23T11:38:35+00:00" }, { "name": "composer/pcre", @@ -4223,16 +4223,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.11", + "version": "9.2.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "665a1ac0a763c51afc30d6d130dac0813092b17f" + "reference": "deac8540cb7bd40b2b8cfa679b76202834fd04e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/665a1ac0a763c51afc30d6d130dac0813092b17f", - "reference": "665a1ac0a763c51afc30d6d130dac0813092b17f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/deac8540cb7bd40b2b8cfa679b76202834fd04e8", + "reference": "deac8540cb7bd40b2b8cfa679b76202834fd04e8", "shasum": "" }, "require": { @@ -4288,7 +4288,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.11" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.13" }, "funding": [ { @@ -4296,7 +4296,7 @@ "type": "github" } ], - "time": "2022-02-18T12:46:09+00:00" + "time": "2022-02-23T17:02:38+00:00" }, { "name": "phpunit/php-file-iterator", diff --git a/docker-compose.yml b/docker-compose.yml index f9e20fb20..301e8fcb5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -399,11 +399,7 @@ services: appwrite-executor: container_name: appwrite-executor - entrypoint: - - php - - -e - - /usr/src/code/app/executor.php - - -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php + entrypoint: executor stop_signal: SIGINT build: context: . diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 5c28361fa..4b3953a35 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -129,7 +129,7 @@ class Executor $status = $response['headers']['status-code']; if ($status >= 400) { - for ($attempts = 0; $attempts < 5; $attempts++) { + for ($attempts = 0; $attempts < 10; $attempts++) { switch ($status) { case 404: $response = $this->createRuntime( diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index ca4ccadd5..9a2afde73 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1306,6 +1306,111 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); } + public function testCreateCustomSwiftExecution() + { + $name = 'swift-5.5'; + $folder = 'swift'; + $code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz"; + $this->packageCode($folder); + + $entrypoint = 'index.swift'; + $timeout = 5; + + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'functionId' => 'unique()', + 'name' => 'Test '.$name, + 'runtime' => $name, + 'vars' => [ + 'CUSTOM_VARIABLE' => 'variable', + ], + 'events' => [], + 'schedule' => '', + 'timeout' => $timeout, + ]); + + $functionId = $function['body']['$id'] ?? ''; + + $this->assertEquals(201, $function['headers']['status-code']); + + $deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'entrypoint' => $entrypoint, + 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'activate' => true, + ]); + + $deploymentId = $deployment['body']['$id'] ?? ''; + $this->assertEquals(201, $deployment['headers']['status-code']); + + // Allow (slow) build step to run + sleep(300); + + $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => 'foobar', + ]); + + $executionId = $execution['body']['$id'] ?? ''; + + $this->assertEquals(201, $execution['headers']['status-code']); + + $executionId = $execution['body']['$id'] ?? ''; + + sleep(10); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $output = json_decode($executions['body']['stdout'], true); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $executions['body']['status']); + $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); + $this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']); + $this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']); + $this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']); + $this->assertEquals('Swift', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); + $this->assertEquals('5.5', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']); + $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); + $this->assertEquals('variable', $output['CUSTOM_VARIABLE']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_USER_ID']); + $this->assertEmpty($output['APPWRITE_FUNCTION_JWT']); + $this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals($executions['headers']['status-code'], 200); + $this->assertEquals($executions['body']['sum'], 1); + $this->assertIsArray($executions['body']['executions']); + $this->assertCount(1, $executions['body']['executions']); + $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); + $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + + // Cleanup : Delete function + $response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], []); + + $this->assertEquals(204, $response['headers']['status-code']); + } + public function testGetRuntimes() { $runtimes = $this->client->call(Client::METHOD_GET, '/functions/runtimes', array_merge([ diff --git a/tests/resources/functions/swift/index.swift b/tests/resources/functions/swift/index.swift new file mode 100644 index 000000000..67b0f35be --- /dev/null +++ b/tests/resources/functions/swift/index.swift @@ -0,0 +1,17 @@ +func main(req: RequestValue, res: RequestResponse) throws -> RequestResponse { + return res.json(data: [ + "APPWRITE_FUNCTION_ID": req.env["APPWRITE_FUNCTION_ID"], + "APPWRITE_FUNCTION_NAME": req.env["APPWRITE_FUNCTION_NAME"], + "APPWRITE_FUNCTION_DEPLOYMENT": req.env["APPWRITE_FUNCTION_DEPLOYMENT"], + "APPWRITE_FUNCTION_TRIGGER": req.env["APPWRITE_FUNCTION_TRIGGER"], + "APPWRITE_FUNCTION_RUNTIME_NAME": req.env["APPWRITE_FUNCTION_RUNTIME_NAME"], + "APPWRITE_FUNCTION_RUNTIME_VERSION": req.env["APPWRITE_FUNCTION_RUNTIME_VERSION"], + "APPWRITE_FUNCTION_EVENT": req.env["APPWRITE_FUNCTION_EVENT"], + "APPWRITE_FUNCTION_EVENT_DATA": req.env["APPWRITE_FUNCTION_EVENT_DATA"], + "APPWRITE_FUNCTION_DATA": req.env["APPWRITE_FUNCTION_DATA"], + "APPWRITE_FUNCTION_USER_ID": req.env["APPWRITE_FUNCTION_USER_ID"], + "APPWRITE_FUNCTION_JWT": req.env["APPWRITE_FUNCTION_JWT"], + "APPWRITE_FUNCTION_PROJECT_ID": req.env["APPWRITE_FUNCTION_PROJECT_ID"], + "CUSTOM_VARIABLE": req.env["CUSTOM_VARIABLE"] + ]) +} \ No newline at end of file