appwrite/tests/e2e/Services/Functions/FunctionsBase.php

437 lines
17 KiB
PHP
Raw Normal View History

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;
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: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 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-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([
'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));
}, 100000, 500);
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-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-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-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-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;
}
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;
}
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
{
$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()), [
'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(),
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-03-04 12:45:44 +00:00
protected function getDeploymentDownload(string $functionId, string $deploymentId, string $type): mixed
{
$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
]);
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-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;
}
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
}