2020-05-05 20:37:59 +00:00
< ? php
namespace Tests\E2E\Services\Functions ;
2024-09-22 19:14:58 +00:00
use Appwrite\Tests\Async ;
2024-09-19 11:35:52 +00:00
use CURLFile ;
2020-05-05 20:37:59 +00:00
use Tests\E2E\Client ;
2026-02-10 05:04:24 +00:00
use Utopia\Console ;
2025-02-15 17:53:02 +00:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Query ;
use Utopia\System\System ;
2020-05-05 20:37:59 +00:00
trait FunctionsBase
{
2024-09-22 19:14:58 +00:00
use Async ;
2024-10-08 07:54:40 +00:00
protected string $stdout = '' ;
protected string $stderr = '' ;
2022-02-19 13:57:48 +00:00
2024-09-19 11:35:52 +00:00
protected function setupFunction ( mixed $params ) : string
2022-05-23 14:54:50 +00:00
{
2024-09-19 11:35:52 +00:00
$function = $this -> client -> call ( Client :: METHOD_POST , '/functions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]), $params );
2024-09-19 19:31:00 +00:00
2024-09-19 19:21:00 +00:00
$this -> assertEquals ( $function [ 'headers' ][ 'status-code' ], 201 , 'Setup function failed with status code: ' . $function [ 'headers' ][ 'status-code' ] . ' and response: ' . json_encode ( $function [ 'body' ], JSON_PRETTY_PRINT ));
2024-09-19 11:35:52 +00:00
2024-09-19 13:17:38 +00:00
$functionId = $function [ 'body' ][ '$id' ];
2024-09-19 19:31:00 +00:00
2024-09-19 11:35:52 +00:00
return $functionId ;
2022-02-19 13:57:48 +00:00
}
2020-05-05 20:37:59 +00:00
2024-09-19 11:35:52 +00:00
protected function setupDeployment ( string $functionId , mixed $params ) : string
2024-07-29 12:39:11 +00:00
{
2024-09-19 11:35:52 +00:00
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/deployments' , array_merge ([
2024-09-19 13:17:38 +00:00
'content-type' => 'multipart/form-data' ,
2024-09-19 11:35:52 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]), $params );
2024-09-19 19:21:00 +00:00
$this -> assertEquals ( $deployment [ 'headers' ][ 'status-code' ], 202 , 'Setup deployment failed with status code: ' . $deployment [ 'headers' ][ 'status-code' ] . ' and response: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
2024-09-19 11:35:52 +00:00
$this -> assertEventually ( function () use ( $functionId , $deploymentId ) {
2024-09-19 19:21:00 +00:00
$deployment = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/deployments/' . $deploymentId , array_merge ([
2024-07-29 12:39:11 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-09-19 19:21:00 +00:00
]));
2024-09-20 08:43:41 +00:00
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ], 'Deployment status is not ready, deployment: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
2025-06-02 16:15:01 +00:00
}, 100000 , 500 );
2024-07-29 12:39:11 +00:00
2025-02-21 21:10:39 +00:00
// Not === so multipart/form-data works fine too
if (( $params [ 'activate' ] ? ? false ) == true ) {
$this -> assertEventually ( function () use ( $functionId , $deploymentId ) {
$function = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]));
2025-03-11 15:26:49 +00:00
$this -> assertEquals ( $deploymentId , $function [ 'body' ][ 'deploymentId' ], 'Deployment is not activated, deployment: ' . json_encode ( $function [ 'body' ], JSON_PRETTY_PRINT ));
2025-02-21 21:10:39 +00:00
}, 100000 , 500 );
}
2024-09-19 11:35:52 +00:00
return $deploymentId ;
}
2024-07-29 12:39:11 +00:00
2024-09-19 11:35:52 +00:00
protected function cleanupFunction ( string $functionId ) : void
{
$function = $this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]));
$this -> assertEquals ( $function [ 'headers' ][ 'status-code' ], 204 );
}
2024-09-19 13:17:38 +00:00
protected function createFunction ( mixed $params ) : mixed
2024-09-19 11:35:52 +00:00
{
$function = $this -> client -> call ( Client :: METHOD_POST , '/functions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
return $function ;
}
2025-03-05 11:29:27 +00:00
protected function updateFunction ( string $functionId , mixed $params ) : mixed
{
$function = $this -> client -> call ( Client :: METHOD_PUT , '/functions/' . $functionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
return $function ;
}
2024-09-19 13:17:38 +00:00
protected function createVariable ( string $functionId , mixed $params ) : mixed
2024-09-19 11:35:52 +00:00
{
$variable = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/variables' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2024-09-19 13:17:38 +00:00
], $this -> getHeaders ()), $params );
2024-09-19 11:35:52 +00:00
return $variable ;
}
2025-02-10 10:54:14 +00:00
protected function getVariable ( string $functionId , string $variableId ) : mixed
{
$variable = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/variables/' . $variableId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $variable ;
}
protected function updateVariable ( string $functionId , string $variableId , mixed $params ) : mixed
{
$variable = $this -> client -> call ( Client :: METHOD_PUT , '/functions/' . $functionId . '/variables/' . $variableId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
return $variable ;
}
protected function listVariables ( string $functionId , mixed $params = []) : mixed
{
$variables = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/variables' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
return $variables ;
}
protected function deleteVariable ( string $functionId , string $variableId ) : mixed
{
$variable = $this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId . '/variables/' . $variableId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $variable ;
}
2024-09-19 13:17:38 +00:00
protected function getFunction ( string $functionId ) : mixed
2024-09-19 11:35:52 +00:00
{
$function = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $function ;
}
2024-09-19 13:17:38 +00:00
protected function getDeployment ( string $functionId , string $deploymentId ) : mixed
2024-09-19 11:35:52 +00:00
{
$deployment = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/deployments/' . $deploymentId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $deployment ;
}
2024-09-20 08:15:05 +00:00
protected function getExecution ( string $functionId , $executionId ) : mixed
2024-09-19 11:35:52 +00:00
{
$execution = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/executions/' . $executionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $execution ;
}
2024-09-20 08:15:05 +00:00
protected function listFunctions ( mixed $params = []) : mixed
2024-09-19 13:17:38 +00:00
{
$functions = $this -> client -> call ( Client :: METHOD_GET , '/functions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
2024-07-29 12:39:11 +00:00
2024-09-19 13:17:38 +00:00
return $functions ;
}
2024-09-20 08:15:05 +00:00
protected function listDeployments ( string $functionId , $params = []) : mixed
2024-09-19 11:35:52 +00:00
{
$deployments = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/deployments' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2024-09-19 13:17:38 +00:00
], $this -> getHeaders ()), $params );
2024-09-19 11:35:52 +00:00
return $deployments ;
}
2024-09-20 08:15:05 +00:00
protected function listExecutions ( string $functionId , mixed $params = []) : mixed
2024-09-19 11:35:52 +00:00
{
$executions = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/executions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2024-09-19 13:17:38 +00:00
], $this -> getHeaders ()), $params );
2024-09-19 11:35:52 +00:00
return $executions ;
2022-02-19 13:57:48 +00:00
}
2020-05-05 20:37:59 +00:00
2024-09-25 11:32:09 +00:00
protected function packageFunction ( string $function ) : CURLFile
2024-07-29 12:39:11 +00:00
{
2024-09-19 19:58:09 +00:00
$folderPath = realpath ( __DIR__ . '/../../../resources/functions' ) . " / $function " ;
$tarPath = " $folderPath /code.tar.gz " ;
2024-10-08 07:54:40 +00:00
Console :: execute ( " cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz . " , '' , $this -> stdout , $this -> stderr );
2024-09-19 19:58:09 +00:00
if ( filesize ( $tarPath ) > 1024 * 1024 * 5 ) {
2024-09-19 11:35:52 +00:00
throw new \Exception ( 'Code package is too large. Use the chunked upload method instead.' );
2024-07-29 12:39:11 +00:00
}
2024-09-19 11:35:52 +00:00
2024-09-19 19:58:09 +00:00
return new CURLFile ( $tarPath , 'application/x-gzip' , \basename ( $tarPath ));
2024-09-19 11:35:52 +00:00
}
2024-09-20 08:15:05 +00:00
protected function createDeployment ( string $functionId , mixed $params = []) : mixed
{
2024-09-19 13:17:38 +00:00
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/deployments' , array_merge ([
2024-09-19 11:35:52 +00:00
'content-type' => 'multipart/form-data' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2024-09-19 13:17:38 +00:00
], $this -> getHeaders ()), $params );
2024-09-19 11:35:52 +00:00
return $deployment ;
2024-07-29 12:39:11 +00:00
}
2025-03-20 14:22:35 +00:00
protected function deleteDeployment ( string $functionId , string $deploymentId ) : mixed
{
$deployment = $this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId . '/deployments/' . $deploymentId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), []);
return $deployment ;
}
2025-02-14 23:36:46 +00:00
protected function createTemplateDeployment ( string $functionId , mixed $params = []) : mixed
{
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/deployments/template' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
return $deployment ;
}
2025-03-08 23:19:54 +00:00
protected function getUsage ( string $functionId , mixed $params ) : mixed
2024-09-19 11:35:52 +00:00
{
$usage = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/usage' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2024-09-19 13:17:38 +00:00
], $this -> getHeaders ()), $params );
2024-09-19 11:35:52 +00:00
return $usage ;
}
protected function getTemplate ( string $templateId )
{
$template = $this -> client -> call ( Client :: METHOD_GET , '/functions/templates/' . $templateId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2025-03-10 11:56:20 +00:00
]));
2024-09-19 11:35:52 +00:00
return $template ;
}
2025-09-19 12:48:44 +00:00
protected function helperGetLatestCommit ( string $owner , string $repository ) : ? string
{
$ch = curl_init ( " https://api.github.com/repos/ { $owner } / { $repository } /commits/main " );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'User-Agent: Appwrite' ,
'Accept: application/vnd.github.v3+json'
]);
$response = curl_exec ( $ch );
$httpCode = curl_getinfo ( $ch , CURLINFO_HTTP_CODE );
curl_close ( $ch );
if ( $httpCode === 200 ) {
$commitData = json_decode ( $response , true );
if ( isset ( $commitData [ 'sha' ])) {
return $commitData [ 'sha' ];
2025-09-18 07:42:46 +00:00
}
}
2025-09-19 12:48:44 +00:00
return null ;
2024-09-19 11:35:52 +00:00
}
2024-09-20 08:15:05 +00:00
protected function createExecution ( string $functionId , mixed $params = []) : mixed
2024-09-19 11:35:52 +00:00
{
$execution = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/executions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), $params );
return $execution ;
}
2024-09-20 08:15:05 +00:00
protected function deleteFunction ( string $functionId ) : mixed
2024-09-19 11:35:52 +00:00
{
2024-09-25 11:32:09 +00:00
$function = $this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId , array_merge ([
2024-09-19 11:35:52 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2024-09-25 11:32:09 +00:00
], $this -> getHeaders ()));
2024-09-19 11:35:52 +00:00
return $function ;
}
2025-02-15 17:53:02 +00:00
2025-02-18 06:42:12 +00:00
protected function setupFunctionDomain ( string $functionId , string $subdomain = '' ) : string
2025-02-15 17:53:02 +00:00
{
2026-01-30 15:48:40 +00:00
$functionsDomain = \explode ( ',' , System :: getEnv ( '_APP_DOMAIN_FUNCTIONS' , '' ))[ 0 ];
2025-02-15 17:53:02 +00:00
$subdomain = $subdomain ? $subdomain : ID :: unique ();
2025-02-22 17:56:51 +00:00
$rule = $this -> client -> call ( Client :: METHOD_POST , '/proxy/rules/function' , array_merge ([
2025-02-15 17:53:02 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2026-01-30 15:48:40 +00:00
'domain' => $subdomain . '.' . $functionsDomain ,
2025-02-22 17:56:51 +00:00
'functionId' => $functionId ,
2025-02-15 17:53:02 +00:00
]);
$this -> assertEquals ( 201 , $rule [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $rule [ 'body' ][ '$id' ]);
$this -> assertNotEmpty ( $rule [ 'body' ][ 'domain' ]);
$domain = $rule [ 'body' ][ 'domain' ];
return $domain ;
}
protected function getFunctionDomain ( string $functionId ) : string
{
$rules = $this -> client -> call ( Client :: METHOD_GET , '/proxy/rules' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'queries' => [
2025-03-07 09:43:54 +00:00
Query :: equal ( 'deploymentResourceId' , [ $functionId ]) -> toString (),
2025-03-08 18:19:05 +00:00
Query :: equal ( 'trigger' , [ 'manual' ]) -> toString (),
2025-02-24 10:55:59 +00:00
Query :: equal ( 'type' , [ 'deployment' ]) -> toString (),
2025-02-15 17:53:02 +00:00
],
]);
$this -> assertEquals ( 200 , $rules [ 'headers' ][ 'status-code' ]);
$this -> assertGreaterThanOrEqual ( 1 , $rules [ 'body' ][ 'total' ]);
$this -> assertGreaterThanOrEqual ( 1 , \count ( $rules [ 'body' ][ 'rules' ]));
$this -> assertNotEmpty ( $rules [ 'body' ][ 'rules' ][ 0 ][ 'domain' ]);
$domain = $rules [ 'body' ][ 'rules' ][ 0 ][ 'domain' ];
return $domain ;
}
2025-02-28 11:04:17 +00:00
2025-03-04 12:45:44 +00:00
protected function getDeploymentDownload ( string $functionId , string $deploymentId , string $type ) : mixed
2025-02-28 11:04:17 +00:00
{
$response = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/deployments/' . $deploymentId . '/download' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
2025-03-04 12:45:44 +00:00
], $this -> getHeaders ()), [
'type' => $type
]);
2025-02-28 11:04:17 +00:00
return $response ;
}
2025-03-05 11:29:27 +00:00
protected function setupDuplicateDeployment ( string $functionId , string $deploymentId ) : string
{
$deployment = $this -> createDuplicateDeployment ( $functionId , $deploymentId );
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
$deploymentId = $deployment [ 'body' ][ '$id' ];
$this -> assertNotEmpty ( $deploymentId );
$this -> assertEventually ( function () use ( $functionId , $deploymentId ) {
$deployment = $this -> getDeployment ( $functionId , $deploymentId );
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ], 'Deployment status is not ready, deployment: ' . json_encode ( $deployment [ 'body' ], JSON_PRETTY_PRINT ));
}, 100000 , 500 );
$this -> assertEventually ( function () use ( $functionId , $deploymentId ) {
$function = $this -> getFunction ( $functionId );
2025-03-11 15:26:49 +00:00
$this -> assertEquals ( $deploymentId , $function [ 'body' ][ 'deploymentId' ], 'Deployment is not activated, deployment: ' . json_encode ( $function [ 'body' ], JSON_PRETTY_PRINT ));
2025-03-05 11:29:27 +00:00
}, 100000 , 500 );
return $deploymentId ;
}
protected function createDuplicateDeployment ( string $functionId , string $deploymentId ) : mixed
{
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/deployments/duplicate' , array_merge ([
'content-type' => 'multipart/form-data' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'deploymentId' => $deploymentId ,
]);
return $deployment ;
}
2025-03-05 11:33:17 +00:00
2025-03-04 15:04:37 +00:00
protected function updateFunctionDeployment ( string $functionId , string $deploymentId ) : mixed
{
$function = $this -> client -> call ( Client :: METHOD_PATCH , '/functions/' . $functionId . '/deployment' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'deploymentId' => $deploymentId
]);
return $function ;
}
2025-03-06 11:20:08 +00:00
protected function cancelDeployment ( string $functionId , string $deploymentId ) : mixed
{
$deployment = $this -> client -> call ( Client :: METHOD_PATCH , '/functions/' . $functionId . '/deployments/' . $deploymentId . '/status' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $deployment ;
}
2025-03-07 22:17:15 +00:00
protected function listSpecifications () : mixed
{
$specifications = $this -> client -> call ( Client :: METHOD_GET , '/functions/specifications' , array_merge ([
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
return $specifications ;
}
2022-05-23 14:54:50 +00:00
}