mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.6.x' into feat-renaming-attributes
This commit is contained in:
@@ -553,6 +553,12 @@ To run end-2-end tests for a specific service use:
|
||||
docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
|
||||
```
|
||||
|
||||
To run one specific test:
|
||||
|
||||
```bash
|
||||
docker compose exec appwrite vendor/bin/phpunit --filter [FunctionName]
|
||||
```
|
||||
|
||||
## Benchmarking
|
||||
|
||||
You can use WRK Docker image to benchmark the server performance. Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied. Replace [APPWRITE_HOSTNAME_OR_IP] with your Appwrite server hostname or IP. Note that localhost is not accessible from inside the WRK container.
|
||||
|
||||
@@ -5,14 +5,6 @@ const TEMPLATE_RUNTIMES = [
|
||||
'name' => 'node',
|
||||
'versions' => ['21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
|
||||
],
|
||||
'PHP' => [
|
||||
'name' => 'php',
|
||||
'versions' => ['8.3', '8.2', '8.1', '8.0']
|
||||
],
|
||||
'RUBY' => [
|
||||
'name' => 'ruby',
|
||||
'versions' => ['3.3', '3.2', '3.1', '3.0']
|
||||
],
|
||||
'PYTHON' => [
|
||||
'name' => 'python',
|
||||
'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8']
|
||||
@@ -21,14 +13,22 @@ const TEMPLATE_RUNTIMES = [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
'versions' => ['1.22']
|
||||
],
|
||||
'PHP' => [
|
||||
'name' => 'php',
|
||||
'versions' => ['8.3', '8.2', '8.1', '8.0']
|
||||
],
|
||||
'BUN' => [
|
||||
'name' => 'bun',
|
||||
'versions' => ['1.0']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
'versions' => ['1.22']
|
||||
]
|
||||
'RUBY' => [
|
||||
'name' => 'ruby',
|
||||
'versions' => ['3.3', '3.2', '3.1', '3.0']
|
||||
],
|
||||
];
|
||||
|
||||
function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = [])
|
||||
@@ -59,13 +59,6 @@ return [
|
||||
'useCases' => ['starter'],
|
||||
'runtimes' => [
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/starter'),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/starter'
|
||||
),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PYTHON'],
|
||||
'pip install -r requirements.txt',
|
||||
@@ -73,14 +66,21 @@ return [
|
||||
'python/starter'
|
||||
),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['GO'], '', 'main.go', 'go/starter'),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
'composer install',
|
||||
'src/index.php',
|
||||
'php/starter'
|
||||
),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['GO'], '', 'main.go', 'go/starter')
|
||||
...getRuntimes(TEMPLATE_RUNTIMES['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/starter">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
'scopes' => ["users.read"]
|
||||
],
|
||||
@@ -106,7 +106,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'UPSTASH_URL',
|
||||
@@ -149,7 +149,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'REDIS_HOST',
|
||||
@@ -191,7 +191,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'NEO4J_URI',
|
||||
@@ -242,7 +242,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'MONGO_URI',
|
||||
@@ -278,7 +278,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PGHOST',
|
||||
@@ -362,7 +362,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'OPENAI_API_KEY',
|
||||
@@ -416,7 +416,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'DISCORD_PUBLIC_KEY',
|
||||
@@ -466,7 +466,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PERSPECTIVE_API_KEY',
|
||||
@@ -513,7 +513,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PANGEA_REDACT_TOKEN',
|
||||
@@ -542,7 +542,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
@@ -568,7 +568,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'GITHUB_TOKEN',
|
||||
@@ -610,7 +610,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -673,7 +673,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -766,7 +766,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -836,6 +836,12 @@ return [
|
||||
'src/main.py',
|
||||
'python/whatsapp_with_vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['PHP'],
|
||||
'composer install',
|
||||
@@ -843,10 +849,10 @@ return [
|
||||
'php/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['DART'],
|
||||
'dart pub get',
|
||||
'lib/main.dart',
|
||||
'dart/whatsapp-with-vonage'
|
||||
TEMPLATE_RUNTIMES['BUN'],
|
||||
'bun install',
|
||||
'src/main.ts',
|
||||
'bun/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['RUBY'],
|
||||
@@ -854,18 +860,12 @@ return [
|
||||
'lib/main.rb',
|
||||
'ruby/whatsapp-with-vonage'
|
||||
),
|
||||
...getRuntimes(
|
||||
TEMPLATE_RUNTIMES['BUN'],
|
||||
'bun install',
|
||||
'src/main.ts',
|
||||
'bun/whatsapp-with-vonage'
|
||||
)
|
||||
],
|
||||
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/whatsapp-with-vonage">file</a>.',
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'VONAGE_API_KEY',
|
||||
@@ -920,7 +920,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'FCM_PROJECT_ID',
|
||||
@@ -987,7 +987,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'SMTP_HOST',
|
||||
@@ -1057,7 +1057,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'STRIPE_SECRET_KEY',
|
||||
@@ -1098,7 +1098,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'STRIPE_SECRET_KEY',
|
||||
@@ -1155,7 +1155,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'HUGGINGFACE_ACCESS_TOKEN',
|
||||
@@ -1188,7 +1188,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'HUGGINGFACE_ACCESS_TOKEN',
|
||||
@@ -1221,7 +1221,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -1279,7 +1279,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -1337,7 +1337,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -1395,7 +1395,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
@@ -1453,7 +1453,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'REPLICATE_API_KEY',
|
||||
@@ -1487,7 +1487,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'TOGETHER_API_KEY',
|
||||
@@ -1529,7 +1529,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PERPLEXITY_API_KEY',
|
||||
@@ -1569,7 +1569,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'REPLICATE_API_KEY',
|
||||
@@ -1603,7 +1603,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'OPENAI_API_KEY',
|
||||
@@ -1666,7 +1666,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'OPENAI_API_KEY',
|
||||
@@ -1729,7 +1729,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'ELEVENLABS_API_KEY',
|
||||
@@ -1784,7 +1784,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'LMNT_API_KEY',
|
||||
@@ -1825,7 +1825,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'ANYSCALE_API_KEY',
|
||||
@@ -1865,7 +1865,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_BUCKET_ID',
|
||||
@@ -1907,7 +1907,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'FAL_API_KEY',
|
||||
@@ -1941,7 +1941,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'LEMON_SQUEEZY_API_KEY',
|
||||
@@ -1996,7 +1996,7 @@ return [
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerBranch' => 'main',
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'APPWRITE_DATABASE_ID',
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -165,7 +165,7 @@ App::post('/v1/functions')
|
||||
->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
|
||||
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
|
||||
->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true)
|
||||
->param('templateBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function template.', true)
|
||||
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -175,7 +175,7 @@ App::post('/v1/functions')
|
||||
->inject('queueForBuilds')
|
||||
->inject('dbForConsole')
|
||||
->inject('gitHub')
|
||||
->action(function (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 $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
||||
->action(function (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, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
|
||||
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
||||
|
||||
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
||||
@@ -190,12 +190,12 @@ App::post('/v1/functions')
|
||||
!empty($templateRepository)
|
||||
&& !empty($templateOwner)
|
||||
&& !empty($templateRootDirectory)
|
||||
&& !empty($templateBranch)
|
||||
&& !empty($templateVersion)
|
||||
) {
|
||||
$template->setAttribute('repositoryName', $templateRepository)
|
||||
->setAttribute('ownerName', $templateOwner)
|
||||
->setAttribute('rootDirectory', $templateRootDirectory)
|
||||
->setAttribute('branch', $templateBranch);
|
||||
->setAttribute('version', $templateVersion);
|
||||
}
|
||||
|
||||
$installation = $dbForConsole->getDocument('installations', $installationId);
|
||||
@@ -284,9 +284,34 @@ App::post('/v1/functions')
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
|
||||
// Redeploy vcs logic
|
||||
if (!empty($providerRepositoryId)) {
|
||||
// Deploy VCS
|
||||
$redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
|
||||
} elseif(!$template->isEmpty()) {
|
||||
// Deploy non-VCS from template
|
||||
$deploymentId = ID::unique();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceInternalId' => $function->getInternalId(),
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $function->getAttribute('entrypoint', ''),
|
||||
'commands' => $function->getAttribute('commands', ''),
|
||||
'type' => 'manual',
|
||||
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
|
||||
'activate' => true,
|
||||
]));
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
}
|
||||
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
@@ -861,7 +886,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Download deployment')
|
||||
->label('scope', 'functions.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'functions')
|
||||
->label('sdk.method', 'getDeploymentDownload')
|
||||
->label('sdk.description', '/docs/references/functions/get-deployment-download.md')
|
||||
@@ -1080,9 +1105,9 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
->inject('deviceForFunctions')
|
||||
->inject('deviceForLocal')
|
||||
->inject('queueForBuilds')
|
||||
->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) {
|
||||
->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, mixed $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) {
|
||||
|
||||
$activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN);
|
||||
$activate = \strval($activate) === 'true' || \strval($activate) === '1';
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
@@ -1180,7 +1205,6 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed moving file');
|
||||
}
|
||||
|
||||
$activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN);
|
||||
$type = $request->getHeader('x-sdk-language') === 'cli' ? 'cli' : 'manual';
|
||||
|
||||
if ($chunksUploaded === $chunks) {
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Messaging;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Platform\Workers\Deletes;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
|
||||
@@ -338,10 +339,12 @@ App::delete('/v1/teams/:teamId')
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->inject('response')
|
||||
->inject('getProjectDB')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForDeletes')
|
||||
->action(function (string $teamId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) {
|
||||
->inject('queueForEvents')
|
||||
->inject('project')
|
||||
->action(function (string $teamId, Response $response, callable $getProjectDB, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Document $project) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
@@ -353,9 +356,14 @@ App::delete('/v1/teams/:teamId')
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB');
|
||||
}
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($team);
|
||||
$deletes = new Deletes();
|
||||
$deletes->deleteMemberships($getProjectDB, $team, $project);
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_TEAM_PROJECTS)
|
||||
->setDocument($team);
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('teamId', $team->getId())
|
||||
|
||||
+2
-2
@@ -172,7 +172,7 @@ const DELETE_TYPE_PROJECTS = 'projects';
|
||||
const DELETE_TYPE_FUNCTIONS = 'functions';
|
||||
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
|
||||
const DELETE_TYPE_USERS = 'users';
|
||||
const DELETE_TYPE_TEAMS = 'teams';
|
||||
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
|
||||
const DELETE_TYPE_EXECUTIONS = 'executions';
|
||||
const DELETE_TYPE_AUDIT = 'audit';
|
||||
const DELETE_TYPE_ABUSE = 'abuse';
|
||||
@@ -750,7 +750,7 @@ $register->set('logger', function () {
|
||||
|
||||
$providerName = $loggingProvider->getScheme();
|
||||
$providerConfig = match ($providerName) {
|
||||
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
|
||||
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()],
|
||||
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
|
||||
default => ['key' => $loggingProvider->getHost()],
|
||||
};
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.0.0-rc14
|
||||
image: appwrite/console:5.0.0-rc15
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="true"
|
||||
stopOnFailure="false"
|
||||
>
|
||||
<extensions>
|
||||
<extension class="Appwrite\Tests\TestHook" />
|
||||
|
||||
@@ -207,7 +207,65 @@ class Builds extends Action
|
||||
}
|
||||
|
||||
try {
|
||||
if ($isNewBuild && $isVcsEnabled) {
|
||||
if($isNewBuild && !$isVcsEnabled) {
|
||||
// Non-vcs+Template
|
||||
|
||||
$templateRepositoryName = $template->getAttribute('repositoryName', '');
|
||||
$templateOwnerName = $template->getAttribute('ownerName', '');
|
||||
$templateVersion = $template->getAttribute('version', '');
|
||||
|
||||
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
|
||||
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
// Clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '-template';
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to clone code repository: ' . $stderr);
|
||||
}
|
||||
|
||||
// Ensure directories
|
||||
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr);
|
||||
|
||||
$tmpPathFile = $tmpTemplateDirectory . '/code.tar.gz';
|
||||
|
||||
$localDevice = new Local();
|
||||
|
||||
if (substr($tmpTemplateDirectory, -1) !== '/') {
|
||||
$tmpTemplateDirectory .= '/';
|
||||
}
|
||||
|
||||
$directorySize = $localDevice->getDirectorySize($tmpTemplateDirectory);
|
||||
$functionsSizeLimit = (int)System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
|
||||
if ($directorySize > $functionsSizeLimit) {
|
||||
throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
|
||||
}
|
||||
|
||||
$tarParamDirectory = \escapeshellarg('/tmp/builds/' . $buildId . '-template' . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory));
|
||||
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
||||
|
||||
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception("Unable to move file");
|
||||
}
|
||||
|
||||
Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $stdout, $stderr);
|
||||
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source));
|
||||
}
|
||||
} elseif ($isNewBuild && $isVcsEnabled) {
|
||||
// VCS and VCS+Temaplte
|
||||
$tmpDirectory = '/tmp/builds/' . $buildId . '/code';
|
||||
$rootDirectory = $function->getAttribute('providerRootDirectory', '');
|
||||
$rootDirectory = \rtrim($rootDirectory, '/');
|
||||
@@ -233,7 +291,8 @@ class Builds extends Action
|
||||
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $cloneVersion, $cloneType, $tmpDirectory, $rootDirectory);
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr);
|
||||
|
||||
Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $buildId), '', $stdout, $stderr);
|
||||
|
||||
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
|
||||
Console::info('Build has been canceled');
|
||||
@@ -263,23 +322,18 @@ class Builds extends Action
|
||||
// Build from template
|
||||
$templateRepositoryName = $template->getAttribute('repositoryName', '');
|
||||
$templateOwnerName = $template->getAttribute('ownerName', '');
|
||||
$templateBranch = $template->getAttribute('branch', '');
|
||||
$templateVersion = $template->getAttribute('version', '');
|
||||
|
||||
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
|
||||
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) {
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
|
||||
// Clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '/template';
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
|
||||
|
||||
$cloneType = GitHub::CLONE_TYPE_BRANCH;
|
||||
if(\str_starts_with($templateBranch, '0.1.')) { // Temporary fix for 1.5. In future versions this only support tag names
|
||||
$cloneType = GitHub::CLONE_TYPE_TAG;
|
||||
}
|
||||
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $cloneType, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
@@ -287,20 +341,20 @@ class Builds extends Action
|
||||
}
|
||||
|
||||
// Ensure directories
|
||||
Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr);
|
||||
Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr);
|
||||
Console::execute('mkdir -p ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
|
||||
|
||||
// Merge template into user repo
|
||||
Console::execute('rsync -av --exclude \'.git\' ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/ ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
|
||||
|
||||
// Commit and push
|
||||
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $stdout, $stderr);
|
||||
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($function->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to push code repository: ' . $stderr);
|
||||
}
|
||||
|
||||
$exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $stdout, $stderr);
|
||||
$exit = Console::execute('cd ' . \escapeshellarg($tmpDirectory) . ' && git rev-parse HEAD', '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to get vcs commit SHA: ' . $stderr);
|
||||
@@ -334,7 +388,7 @@ class Builds extends Action
|
||||
);
|
||||
}
|
||||
|
||||
$tmpPath = '/tmp/builds/' . \escapeshellcmd($buildId);
|
||||
$tmpPath = '/tmp/builds/' . $buildId;
|
||||
$tmpPathFile = $tmpPath . '/code.tar.gz';
|
||||
$localDevice = new Local();
|
||||
|
||||
@@ -348,21 +402,17 @@ class Builds extends Action
|
||||
throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
|
||||
}
|
||||
|
||||
Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr);
|
||||
$tarParamDirectory = '/tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory);
|
||||
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
|
||||
|
||||
$path = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||
$result = $localDevice->transfer($tmpPathFile, $path, $deviceForFunctions);
|
||||
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception("Unable to move file");
|
||||
}
|
||||
|
||||
$deployment->setAttribute('path', $path);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
Console::execute('rm -rf ' . $tmpPath, '', $stdout, $stderr);
|
||||
|
||||
$source = $path;
|
||||
Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $stdout, $stderr);
|
||||
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source));
|
||||
|
||||
@@ -95,12 +95,6 @@ class Deletes extends Action
|
||||
case DELETE_TYPE_USERS:
|
||||
$this->deleteUser($getProjectDB, $document, $project);
|
||||
break;
|
||||
case DELETE_TYPE_TEAMS:
|
||||
$this->deleteMemberships($getProjectDB, $document, $project);
|
||||
if ($project->getId() === 'console') {
|
||||
$this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $document);
|
||||
}
|
||||
break;
|
||||
case DELETE_TYPE_BUCKETS:
|
||||
$this->deleteBucket($getProjectDB, $deviceForFiles, $document, $project);
|
||||
break;
|
||||
@@ -115,6 +109,9 @@ class Deletes extends Action
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DELETE_TYPE_TEAM_PROJECTS:
|
||||
$this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $document);
|
||||
break;
|
||||
case DELETE_TYPE_EXECUTIONS:
|
||||
$this->deleteExecutionLogs($project, $getProjectDB, $executionRetention);
|
||||
break;
|
||||
@@ -419,7 +416,7 @@ class Deletes extends Action
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void
|
||||
public function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void
|
||||
{
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$teamInternalId = $document->getInternalId();
|
||||
@@ -447,14 +444,21 @@ class Deletes extends Action
|
||||
* @throws Conflict
|
||||
* @throws Restricted
|
||||
* @throws Structure
|
||||
* @throws Exception
|
||||
*/
|
||||
private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, Document $document): void
|
||||
private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, Document $document): void
|
||||
{
|
||||
|
||||
$projects = $dbForConsole->find('projects', [
|
||||
Query::equal('teamInternalId', [$document->getInternalId()])
|
||||
]);
|
||||
|
||||
foreach ($projects as $project) {
|
||||
$deviceForFiles = getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
$deviceForFunctions = getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
$deviceForBuilds = getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
$deviceForCache = getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
|
||||
|
||||
$this->deleteProject($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $project);
|
||||
$dbForConsole->deleteDocument('projects', $project->getId());
|
||||
}
|
||||
|
||||
@@ -13,7 +13,10 @@ class V18 extends Filter
|
||||
case 'account.deleteMfaAuthenticator':
|
||||
unset($content['otp']);
|
||||
break;
|
||||
|
||||
case 'functions.create':
|
||||
$content['templateVersion'] = $content['templateBranch'];
|
||||
unset($content['templateBranch']);
|
||||
break;
|
||||
}
|
||||
|
||||
return $content;
|
||||
|
||||
@@ -98,9 +98,9 @@ class TemplateFunction extends Model
|
||||
'default' => '',
|
||||
'example' => 'appwrite',
|
||||
])
|
||||
->addRule('providerBranch', [
|
||||
->addRule('providerVersion', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'VCS (Version Control System) branch name',
|
||||
'description' => 'VCS (Version Control System) branch version (tag).',
|
||||
'default' => '',
|
||||
'example' => 'main',
|
||||
])
|
||||
|
||||
@@ -250,8 +250,8 @@ class FunctionsCustomClientTest extends Scope
|
||||
break;
|
||||
}
|
||||
|
||||
if (\microtime(true) - $start > 5) {
|
||||
$this->fail('Execution did not complete within 5 seconds of schedule');
|
||||
if (\microtime(true) - $start > 10) {
|
||||
$this->fail('Execution did not complete within 10 seconds of schedule in status ' . $execution['body']['status'] . ': ' . \json_encode($execution));
|
||||
}
|
||||
|
||||
usleep(500000); // 0.5 seconds
|
||||
@@ -848,6 +848,7 @@ class FunctionsCustomClientTest extends Scope
|
||||
$this->assertEquals(200, $templates['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $templates['body']['total']);
|
||||
$this->assertIsArray($templates['body']['templates']);
|
||||
|
||||
$this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
|
||||
$this->assertArrayHasKey('useCases', $templates['body']['templates'][0]);
|
||||
for ($i = 0; $i < 25; $i++) {
|
||||
|
||||
@@ -439,6 +439,135 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals('cli', $functionDetails['body']['type']);
|
||||
}
|
||||
|
||||
public function testCreateDeploymentFromTemplate()
|
||||
{
|
||||
$runtimeName = 'php-8.0';
|
||||
|
||||
// Fetch starter template (used to create function later)
|
||||
$template = $this->client->call(Client::METHOD_GET, '/functions/templates/starter', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
|
||||
$entrypoint = null;
|
||||
$rootDirectory = null;
|
||||
$commands = null;
|
||||
foreach($template['body']['runtimes'] as $runtime) {
|
||||
if($runtime["name"] !== $runtimeName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entrypoint = $runtime["entrypoint"];
|
||||
$rootDirectory = $runtime["providerRootDirectory"];
|
||||
$commands = $runtime["commands"];
|
||||
break;
|
||||
}
|
||||
|
||||
$this->assertNotNull($entrypoint);
|
||||
|
||||
/**
|
||||
* If below test ever starts failing, it means temaplate used in
|
||||
* this test now has some variables. This test currently doesnt test variables.
|
||||
* Remove bellow assertion and update test to crete variable,
|
||||
* and ensure variable works as expected in execution.
|
||||
*/
|
||||
$this->assertEmpty($template['body']['variables']);
|
||||
|
||||
// Create function using settings from template.
|
||||
// Deployment is automatically created from template inside endpoint
|
||||
$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'],
|
||||
], $this->getHeaders()), [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => $template['body']['name'],
|
||||
'runtime' => $runtimeName,
|
||||
'execute' => $template['body']['permissions'],
|
||||
'entrypoint' => $entrypoint,
|
||||
'events' => $template['body']['events'],
|
||||
'schedule' => $template['body']['cron'],
|
||||
'timeout' => $template['body']['timeout'],
|
||||
'commands' => $commands,
|
||||
'scopes' => $template['body']['scopes'],
|
||||
'templateRepository' => $template['body']['providerRepositoryId'],
|
||||
'templateOwner' => $template['body']['providerOwner'],
|
||||
'templateRootDirectory' => $rootDirectory,
|
||||
'templateVersion' => $template['body']['providerVersion'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'];
|
||||
|
||||
// List deployments so we can await deployment build
|
||||
$deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], []);
|
||||
|
||||
$this->assertEquals(200, $deployments['headers']['status-code']);
|
||||
$this->assertEquals(1, $deployments['body']['total']);
|
||||
$this->assertNotEmpty($deployments['body']['deployments'][0]['$id']);
|
||||
|
||||
$deploymentId = $deployments['body']['deployments'][0]['$id'];
|
||||
|
||||
// Wait for deployment build to finish
|
||||
// Deployment is automatically activated
|
||||
$this->awaitDeploymentIsBuilt($function['body']['$id'], $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'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(200, $function['headers']['status-code']);
|
||||
$this->assertEquals($deploymentId, $function['body']['deployment']);
|
||||
|
||||
// Execute function to ensure starter code is used
|
||||
// Also tests if dynamic keys works as expected
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'path' => '/ping'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEquals("completed", $execution['body']['status']);
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
$this->assertEquals("Pong", $execution['body']['responseBody']);
|
||||
$this->assertEmpty($execution['body']['errors']);
|
||||
|
||||
// Get users to ensure execution logged correct total users
|
||||
$users = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(200, $users['headers']['status-code']);
|
||||
$this->assertIsInt($users['body']['total']);
|
||||
|
||||
$totalusers = $users['body']['total'];
|
||||
|
||||
$this->assertStringContainsString("Total users: " . $totalusers, $execution['body']['logs']);
|
||||
|
||||
// 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']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdate
|
||||
*/
|
||||
@@ -468,6 +597,37 @@ class FunctionsCustomServerTest extends Scope
|
||||
|
||||
$this->awaitDeploymentIsBuilt($data['functionId'], $deploymentId);
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
|
||||
'activate' => 'false'
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deploymentIdInactive = $deployment['body']['$id'];
|
||||
|
||||
$this->awaitDeploymentIsBuilt($data['functionId'], $deploymentIdInactive);
|
||||
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $function['headers']['status-code']);
|
||||
$this->assertEquals($deploymentId, $function['body']['deployment']);
|
||||
$this->assertNotEquals($deploymentIdInactive, $function['body']['deployment']);
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentIdInactive, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(204, $deployment['headers']['status-code']);
|
||||
|
||||
return array_merge($data, ['deploymentId' => $deploymentId]);
|
||||
}
|
||||
|
||||
@@ -1268,7 +1428,11 @@ class FunctionsCustomServerTest extends Scope
|
||||
$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->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('trigger', ['http'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($executions['headers']['status-code'], 200);
|
||||
$this->assertEquals($executions['body']['total'], 1);
|
||||
@@ -1284,17 +1448,39 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals($executions['body']['executions'][0]['logs'], '');
|
||||
$this->assertStringContainsString('timed out', $executions['body']['executions'][0]['errors']);
|
||||
|
||||
sleep(75); // Wait for scheduled execution to be created and time out
|
||||
$start = \microtime(true);
|
||||
while (true) {
|
||||
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('trigger', ['schedule'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$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(200, $executions['headers']['status-code']);
|
||||
|
||||
if (\count($executions['body']['executions']) > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 0s would mean instant execution
|
||||
// +60 seconds, maximum possible waiting time before next minute
|
||||
// +10 seconds, maximum update interval time
|
||||
// +60 seconds, possible overlap between update and schedule tick
|
||||
// +10 seconds, maximum execution time including cold-start
|
||||
// Result: We allow maximum
|
||||
if (\microtime(true) - $start > 140) {
|
||||
$this->fail('Execution did not create within 140 seconds of schedule: ' . \json_encode($executions));
|
||||
}
|
||||
|
||||
usleep(1000000); // 1 second
|
||||
}
|
||||
|
||||
$this->assertEquals(200, $executions['headers']['status-code']);
|
||||
$this->assertCount(2, $executions['body']['executions']);
|
||||
$this->assertIsArray($executions['body']['executions']);
|
||||
$this->assertEquals($executions['body']['executions'][1]['trigger'], 'schedule');
|
||||
$this->assertGreaterThanOrEqual(1, \count($executions['body']['executions']));
|
||||
$this->assertEquals($executions['body']['executions'][0]['trigger'], 'schedule');
|
||||
|
||||
// Cleanup : Delete function
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
namespace Tests\E2E\Services\Teams;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
class TeamsCustomServerTest extends Scope
|
||||
{
|
||||
@@ -12,4 +15,96 @@ class TeamsCustomServerTest extends Scope
|
||||
use TeamsBaseServer;
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testMembershipDeletedWhenTeamDeleted(): array
|
||||
{
|
||||
/* 1. Create Team */
|
||||
$response = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Demo'
|
||||
]);
|
||||
|
||||
$teamUid = $response['body']['$id'];
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertEquals('Demo', $response['body']['name']);
|
||||
$this->assertGreaterThan(-1, $response['body']['total']);
|
||||
$this->assertIsInt($response['body']['total']);
|
||||
$this->assertArrayHasKey('prefs', $response['body']);
|
||||
$dateValidator = new DatetimeValidator();
|
||||
$this->assertEquals(true, $dateValidator->isValid($response['body']['$createdAt']));
|
||||
|
||||
/* 2. Create user. */
|
||||
$email = uniqid() . 'friend@localhost.test';
|
||||
$name = 'Friend User';
|
||||
$password = 'password';
|
||||
$userId = ID::unique();
|
||||
|
||||
// Create a user account before we create a invite so we can check if the user has permissions when it shouldn't
|
||||
$user = $this->client->call(Client::METHOD_POST, '/account', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'userId' => $userId,
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
], false);
|
||||
|
||||
$this->assertEquals(201, $user['headers']['status-code']);
|
||||
|
||||
/* 3. Add membership to user. */
|
||||
$response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => $userId,
|
||||
'roles' => ['admin', 'editor'],
|
||||
'url' => 'http://localhost:5000/join-us#title'
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
/* 4. Ensure user is a member. */
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($teamUid, $response['body']['memberships'][0]['teamId']);
|
||||
|
||||
/* 5. Delete Team */
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
$this->assertEmpty($response['body']);
|
||||
|
||||
/* 6. Ensure Team got deleted */
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
|
||||
/* 7. Ensure memberships got removed from the user. */
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEmpty($response['body']['memberships']);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user