diff --git a/app/config/collections.php b/app/config/collections.php index cab98fb8ca..2b806ac319 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1598,8 +1598,8 @@ $collections = [ ], [ '$collection' => Database::SYSTEM_COLLECTION_RULES, - 'label' => 'Command', - 'key' => 'command', + 'label' => 'Entrypoint', + 'key' => 'entrypoint', 'type' => Database::SYSTEM_VAR_TYPE_TEXT, 'default' => '', 'required' => false, diff --git a/app/config/runtimes.php b/app/config/runtimes.php index d34cfc52e1..bdcf9e01f4 100644 --- a/app/config/runtimes.php +++ b/app/config/runtimes.php @@ -15,7 +15,11 @@ $allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', $node = new Runtime('node', 'Node.js'); $node->addVersion('NG-Latest', 'node:16-alpine-nx', 'node-runtime', [System::X86, System::PPC, System::ARM]); +$deno = new Runtime('deno', 'Deno'); +$deno->addVersion('NG-Latest', 'deno:latest-alpine-nx', 'deno-runtime', [System::X86, System::PPC, System::ARM]); + $runtimes->add($node); +$runtimes->add($deno); $runtimes = $runtimes->getAll(true, $allowList); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 6f35d9016a..5a9dfe2c16 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -434,13 +434,13 @@ App::post('/v1/functions/:functionId/tags') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TAG) ->param('functionId', '', new UID(), 'Function unique ID.') - ->param('command', '', new Text('1028'), 'Code execution command.') + ->param('entrypoint', '', new Text('1028'), 'Entrypoint File.') ->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', false) ->inject('request') ->inject('response') ->inject('projectDB') ->inject('usage') - ->action(function ($functionId, $command, $file, $request, $response, $projectDB, $usage) { + ->action(function ($functionId, $entrypoint, $file, $request, $response, $projectDB, $usage) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Database $projectDB */ @@ -495,7 +495,7 @@ App::post('/v1/functions/:functionId/tags') ], 'functionId' => $function->getId(), 'dateCreated' => time(), - 'command' => $command, + 'entrypoint' => $entrypoint, 'path' => $path, 'size' => $size ]); diff --git a/app/executor.php b/app/executor.php index 98ed8460d2..1eadd63250 100644 --- a/app/executor.php +++ b/app/executor.php @@ -423,10 +423,12 @@ function execute(string $trigger, string $projectId, string $executionId, string \curl_setopt($ch, CURLOPT_POST, true); \curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'path' => '/usr/code', - 'file' => 'index.js', + 'file' => $tag->getAttribute('entrypoint', ''), 'env' => $vars, - 'payload' => $data + 'payload' => $data, + 'timeout' => $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)) ])); + \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); \curl_setopt($ch, CURLOPT_TIMEOUT, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900))); \curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); @@ -518,9 +520,7 @@ function execute(string $trigger, string $projectId, string $executionId, string return [ 'status' => $functionStatus, - 'exitCode' => $exitCode, - 'stdout' => $stdout, - 'stderr' => $stderr, + 'response' => $stdout, 'time' => $executionTime ]; } diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml index 554139876e..c829d3e864 100644 --- a/app/views/console/functions/function.phtml +++ b/app/views/console/functions/function.phtml @@ -100,7 +100,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);   - +
@@ -599,7 +599,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
@@ -608,7 +608,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
@@ -634,8 +634,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true); - - + + diff --git a/package-lock.json b/package-lock.json index 2dd7d37b11..e63f4954ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "appwrite-server", "version": "0.1.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, "packages": { "": { @@ -2880,7 +2880,7 @@ "object-assign": "^4.0.1", "read-pkg-up": "^1.0.1", "redent": "^1.0.0", - "trim-newlines": "^4.0.0" + "trim-newlines": "^1.0.0" }, "engines": { "node": ">=0.10.0" @@ -7353,7 +7353,7 @@ "object-assign": "^4.0.1", "read-pkg-up": "^1.0.1", "redent": "^1.0.0", - "trim-newlines": "^4.0.0" + "trim-newlines": "^1.0.0" }, "dependencies": { "minimist": { @@ -8489,6 +8489,15 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -8517,15 +8526,6 @@ } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", @@ -8699,8 +8699,7 @@ } }, "trim-newlines": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", + "version": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", "integrity": "sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==", "dev": true }, diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 5f334eb7de..e751f06939 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -174,10 +174,10 @@ let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let p if(typeof limit!=='undefined'){payload['limit']=limit;} if(typeof offset!=='undefined'){payload['offset']=offset;} if(typeof orderType!=='undefined'){payload['orderType']=orderType;} -const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createTag:(functionId,command,code)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');} -if(typeof command==='undefined'){throw new AppwriteException('Missing required parameter: "command"');} +const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createTag:(functionId,entrypoint,code)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');} +if(typeof entrypoint==='undefined'){throw new AppwriteException('Missing required parameter: "entrypoint"');} if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');} -let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof command!=='undefined'){payload['command']=command;} +let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof entrypoint!=='undefined'){payload['entrypoint']=entrypoint;} if(typeof code!=='undefined'){payload['code']=code;} const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),getTag:(functionId,tagId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');} if(typeof tagId==='undefined'){throw new AppwriteException('Missing required parameter: "tagId"');} diff --git a/public/dist/scripts/app-dep.js b/public/dist/scripts/app-dep.js index 4a5f301275..f506e690de 100644 --- a/public/dist/scripts/app-dep.js +++ b/public/dist/scripts/app-dep.js @@ -174,10 +174,10 @@ let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let p if(typeof limit!=='undefined'){payload['limit']=limit;} if(typeof offset!=='undefined'){payload['offset']=offset;} if(typeof orderType!=='undefined'){payload['orderType']=orderType;} -const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createTag:(functionId,command,code)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');} -if(typeof command==='undefined'){throw new AppwriteException('Missing required parameter: "command"');} +const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createTag:(functionId,entrypoint,code)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');} +if(typeof entrypoint==='undefined'){throw new AppwriteException('Missing required parameter: "entrypoint"');} if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');} -let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof command!=='undefined'){payload['command']=command;} +let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof entrypoint!=='undefined'){payload['entrypoint']=entrypoint;} if(typeof code!=='undefined'){payload['code']=code;} const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),getTag:(functionId,tagId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');} if(typeof tagId==='undefined'){throw new AppwriteException('Missing required parameter: "tagId"');} diff --git a/public/scripts/dependencies/appwrite.js b/public/scripts/dependencies/appwrite.js index c538693f63..a1e6efb129 100644 --- a/public/scripts/dependencies/appwrite.js +++ b/public/scripts/dependencies/appwrite.js @@ -1580,28 +1580,28 @@ * learn more about code packaging in the [Appwrite Cloud Functions * tutorial](/docs/functions). * - * Use the "command" param to set the entry point used to execute your code. + * Use the "entrypoint" param to set the entry point used to execute your code. * * @param {string} functionId - * @param {string} command + * @param {string} entrypoint * @param {File} code * @throws {AppwriteException} * @returns {Promise} */ - createTag: (functionId, command, code) => __awaiter(this, void 0, void 0, function* () { + createTag: (functionId, entrypoint, code) => __awaiter(this, void 0, void 0, function* () { if (typeof functionId === 'undefined') { throw new AppwriteException('Missing required parameter: "functionId"'); } - if (typeof command === 'undefined') { - throw new AppwriteException('Missing required parameter: "command"'); + if (typeof entrypoint === 'undefined') { + throw new AppwriteException('Missing required parameter: "entrypoint"'); } if (typeof code === 'undefined') { throw new AppwriteException('Missing required parameter: "code"'); } let path = '/functions/{functionId}/tags'.replace('{functionId}', functionId); let payload = {}; - if (typeof command !== 'undefined') { - payload['command'] = command; + if (typeof entrypoint !== 'undefined') { + payload['entrypoint'] = entrypoint; } if (typeof code !== 'undefined') { payload['code'] = code; diff --git a/src/Appwrite/Utopia/Response/Model/SyncExecution.php b/src/Appwrite/Utopia/Response/Model/SyncExecution.php index 4bdca8b922..d7d667ef40 100644 --- a/src/Appwrite/Utopia/Response/Model/SyncExecution.php +++ b/src/Appwrite/Utopia/Response/Model/SyncExecution.php @@ -16,24 +16,12 @@ class SyncExecution extends Model 'default' => '', 'example' => '5e5ea5c16897e', ]) - ->addRule('exitCode', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'Execution Exit Code.', - 'default' => 0, - 'example' => 0, - ]) - ->addRule('stdout', [ + ->addRule('response', [ 'type' => self::TYPE_STRING, - 'description' => 'Execution Stdout.', + 'description' => 'Execution Response.', 'default' => '', 'example' => 'Hello World!', ]) - ->addRule('stderr', [ - 'type' => self::TYPE_STRING, - 'description' => 'Execution Stderr.', - 'default' => '', - 'example' => 'An error occoured: ....... example', - ]) ->addRule('time', [ 'type' => self::TYPE_INTEGER, 'description' => 'Execution Time.', diff --git a/src/Appwrite/Utopia/Response/Model/Tag.php b/src/Appwrite/Utopia/Response/Model/Tag.php index b48e157291..fde42cbfbf 100644 --- a/src/Appwrite/Utopia/Response/Model/Tag.php +++ b/src/Appwrite/Utopia/Response/Model/Tag.php @@ -28,9 +28,9 @@ class Tag extends Model 'default' => 0, 'example' => 1592981250, ]) - ->addRule('command', [ + ->addRule('entrypoint', [ 'type' => self::TYPE_STRING, - 'description' => 'The entrypoint command in use to execute the tag code.', + 'description' => 'The entrypoint file to use to execute the tag code.', 'default' => '', 'example' => 'enabled', ]) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 454a149c3c..10ad410c10 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -75,7 +75,7 @@ class FunctionsCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ - 'command' => 'php index.php', + 'entrypoint' => 'index.php', 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/php.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'), ]); @@ -158,7 +158,7 @@ class FunctionsCustomClientTest extends Scope 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apikey, ], [ - 'command' => 'php index.php', + 'entrypoint' => 'index.php', 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/php-fn.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'), //different tarball names intentional ]); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 775eca06e2..c87091eb97 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -182,7 +182,7 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'command' => 'php index.php', + 'entrypoint' => 'index.php', 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/php.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'), ]); @@ -191,7 +191,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(201, $tag['headers']['status-code']); $this->assertNotEmpty($tag['body']['$id']); $this->assertIsInt($tag['body']['dateCreated']); - $this->assertEquals('php index.php', $tag['body']['command']); + $this->assertEquals('php index.php', $tag['body']['entrypoint']); $this->assertGreaterThan(10000, $tag['body']['size']); /** @@ -454,7 +454,7 @@ class FunctionsCustomServerTest extends Scope { $name = 'php-8.0'; $code = realpath(__DIR__ . '/../../../resources/functions').'/timeout.tar.gz'; - $command = 'php index.php'; + $entrypoint = 'php index.php'; $timeout = 2; $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ @@ -477,7 +477,7 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'command' => $command, + 'entrypoint' => $entrypoint, 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), ]); @@ -532,7 +532,7 @@ class FunctionsCustomServerTest extends Scope { $name = 'php-8.0'; $code = realpath(__DIR__ . '/../../../resources/functions').'/php-fn.tar.gz'; - $command = 'php index.php'; + $entrypoint = 'index.php'; $timeout = 2; $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ @@ -555,7 +555,7 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'command' => $command, + 'entrypoint' => $entrypoint, 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), ]); diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 0d3678eaf2..5d61193032 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -384,7 +384,7 @@ class WebhooksCustomServerTest extends Scope 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'command' => 'php index.php', + 'entrypoint' => 'index.php', 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/timeout.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'), ]);