From c0f7edc840b3fcdd942a02685b2f4ebc858f4498 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Thu, 18 Jul 2024 23:47:28 +0530
Subject: [PATCH 01/16] Add function templates to config
---
app/config/templates.php | 2299 ++++++++++++++++++++++++++++++++++++++
1 file changed, 2299 insertions(+)
create mode 100644 app/config/templates.php
diff --git a/app/config/templates.php b/app/config/templates.php
new file mode 100644
index 0000000000..5c6e4fbe0f
--- /dev/null
+++ b/app/config/templates.php
@@ -0,0 +1,2299 @@
+ [
+ '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']
+ ],
+ 'DART' => [
+ 'name' => 'dart',
+ 'versions' => ['3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16']
+ ],
+ 'BUN' => [
+ 'name' => 'bun',
+ 'versions' => ['1.0']
+ ]
+];
+
+function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = []) {
+ return array_map(function($version) use ($runtime, $commands, $entrypoint, $providerRootDirectory) {
+ return [
+ 'name' => $runtime['name'] . '-' . $version,
+ 'commands' => $commands,
+ 'entrypoint' => $entrypoint,
+ 'providerRootDirectory' => $providerRootDirectory
+ ];
+ }, array_filter($runtime['versions'], function($version) use ($versionsDenyList) {
+ return !in_array($version, $versionsDenyList);
+ }));
+}
+
+return [
+ [
+ 'icon' => 'icon-lightning-bolt',
+ 'id' => 'starter',
+ 'name' => 'Starter function',
+ 'tagline' =>
+ 'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ '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',
+ 'src/main.py',
+ 'python/starter'
+ ),
+ ...getRuntimes(TEMPLATE_RUNTIMES['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
+ ...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter')
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => false,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-upstash',
+ 'id' => 'query-upstash-vector',
+ 'name' => 'Query Upstash Vector',
+ 'tagline' => 'Vector database that stores text embeddings and context retrieval for LLMs',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/query-upstash-vector'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'UPSTASH_URL',
+ 'description' => 'The endpoint to connect to your Upstash Vector database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'https://resolved-mallard-84564-eu1-vector.upstash.io',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'UPSTASH_TOKEN',
+ 'description' => 'Authentication token to access your Upstash Vector database. Learn more.',
+ 'value' => '',
+ 'placeholder' =>
+ 'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-redis',
+ 'id' => 'query-redis-labs',
+ 'name' => 'Query Redis Labs',
+ 'tagline' => 'Key-value database with advanced caching capabilities.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/query-redis-labs'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'REDIS_HOST',
+ 'description' => 'The endpoint to connect to your Redis database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'redis-13258.c35.eu-central-1-1.ec2.redns.redis-cloud.com',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'REDIS_PASSWORD',
+ 'description' => 'Authentication password to access your Redis database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'efNNehiACfcZiwsTAjcK6xiwPyu6Dpdq',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-neo4j',
+ 'id' => 'query-neo4j-auradb',
+ 'name' => 'Query Neo4j AuraDB',
+ 'tagline' => 'Graph database with focus on relations between data.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/query-neo4j-auradb'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'NEO4J_URI',
+ 'description' => 'The endpoint to connect to your Neo4j database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'neo4j+s://4tg4mddo.databases.neo4j.io',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'NEO4J_USER',
+ 'description' => 'Authentication user to access your Neo4j database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'neo4j',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'NEO4J_PASSWORD',
+ 'description' => 'Authentication password to access your Neo4j database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'mCUc4PbVUQN-_NkTLJLisb6ccnwzQKKhrkF77YMctzx',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-mongodb',
+ 'id' => 'query-mongo-atlas',
+ 'name' => 'Query MongoDB Atlas',
+ 'tagline' =>
+ 'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/query-mongo-atlas'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'MONGO_URI',
+ 'description' => 'The endpoint to connect to your Mongo database. Learn more.',
+ 'value' => '',
+ 'placeholder' =>
+ 'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-neon',
+ 'id' => 'query-neon-postgres',
+ 'name' => 'Query Neon Postgres',
+ 'tagline' =>
+ 'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/query-neon-postgres'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'PGHOST',
+ 'description' => 'The endpoint to connect to your Postgres database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'ep-still-sea-a792sh84.eu-central-1.aws.neon.tech',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'PGDATABASE',
+ 'description' => 'Name of our Postgres database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'main',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'PGUSER',
+ 'description' => 'Name of our Postgres user for authentication. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'main_owner',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'PGPASSWORD',
+ 'description' => 'Password of our Postgres user for authentication. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'iQCfaUaaWB3B',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'ENDPOINT_ID',
+ 'description' => 'Endpoint ID provided for your Postgres database. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'ep-still-sea-a792sh84',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-open-ai',
+ 'id' => 'prompt-chatgpt',
+ 'name' => 'Prompt ChatGPT',
+ 'tagline' => 'Ask questions and let OpenAI GPT-3.5-turbo answer.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/prompt-chatgpt'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt',
+ 'src/main.py',
+ 'python/prompt_chatgpt'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PHP'],
+ 'composer install',
+ 'src/index.php',
+ 'php/prompt-chatgpt'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['DART'],
+ 'dart pub get',
+ 'lib/main.dart',
+ 'dart/prompt_chatgpt'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'OPENAI_API_KEY',
+ 'description' => 'A unique key used to authenticate with the OpenAI API. This is a paid service and you will be charged for each request made to the API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'sk-wzG...vcy',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'OPENAI_MAX_TOKENS',
+ 'description' => 'The maximum number of tokens that the OpenAI response should contain. Be aware that OpenAI models read and write a maximum number of tokens per API call, which varies depending on the model. For GPT-3.5-turbo, the limit is 4096 tokens. Learn more.',
+ 'value' => '512',
+ 'placeholder' => '512',
+ 'required' => false,
+ 'type' => 'number'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-discord',
+ 'id' => 'discord-command-bot',
+ 'name' => 'Discord Command Bot',
+ 'tagline' => 'Simple command using Discord Interactions.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Messaging'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install && npm run setup',
+ 'src/main.js',
+ 'node/discord-command-bot'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt && python src/setup.py',
+ 'src/main.py',
+ 'python/discord_command_bot'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'DISCORD_PUBLIC_KEY',
+ 'description' => 'Public Key of your application in Discord Developer Portal. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'db9...980',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'DISCORD_APPLICATION_ID',
+ 'description' => 'ID of your application in Discord Developer Portal. Learn more.',
+ 'value' => '',
+ 'placeholder' => '427...169',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'DISCORD_TOKEN',
+ 'description' => 'Bot token of your application in Discord Developer Portal. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'NDI...LUfg',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-perspective-api',
+ 'id' => 'analyze-with-perspectiveapi',
+ 'name' => 'Analyze with PerspectiveAPI',
+ 'tagline' => 'Automate moderation by getting toxicity of messages.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/analyze-with-perspectiveapi'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'PERSPECTIVE_API_KEY',
+ 'description' => 'Google Perspective API key. It authenticates your function, allowing it to interact with the API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'AIzaS...fk-fuM',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-pangea',
+ 'id' => 'censor-with-redact',
+ 'name' => 'Censor with Redact',
+ 'tagline' =>
+ 'Censor sensitive information from a provided text string using Redact API by Pangea.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/censor-with-redact'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt',
+ 'src/main.py',
+ 'python/censor_with_redact'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['DART'],
+ 'dart pub get',
+ 'lib/main.dart',
+ 'dart/censor_with_redact'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'PANGEA_REDACT_TOKEN',
+ 'description' => 'Access token for the Pangea Redact API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'pts_7p4...5wl4',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-document',
+ 'id' => 'generate-pdf',
+ 'name' => 'Generate PDF',
+ 'tagline' => 'Document containing sample invoice in PDF format.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => []
+ ],
+ [
+ 'icon' => 'icon-github',
+ 'id' => 'github-issue-bot',
+ 'name' => 'GitHub issue bot',
+ 'tagline' =>
+ 'Automate the process of responding to newly opened issues in a GitHub repository.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Dev Tools'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/github-issue-bot'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'GITHUB_TOKEN',
+ 'description' => 'A personal access token from GitHub with the necessary permissions to post comments on issues. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'ghp_1...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'GITHUB_WEBHOOK_SECRET',
+ 'description' => 'The secret used to verify that the webhook request comes from GitHub. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-bookmark',
+ 'id' => 'url-shortener',
+ 'name' => 'URL shortener',
+ 'tagline' => 'Generate URL with short ID and redirect to the original URL when visited.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/url-shortener'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The API endpoint of the Appwrite. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database to store the short URLs. Learn more.',
+ 'value' => 'urlShortener',
+ 'placeholder' => 'urlShortener',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection to store the short URLs. Learn more.',
+ 'value' => 'urls',
+ 'placeholder' => 'urls',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'SHORT_BASE_URL',
+ 'description' => 'The domain to use for the short URLs. You can use your functions subdomain or a custom domain.',
+ 'value' => '',
+ 'placeholder' => 'https://shortdomain.io',
+ 'required' => true,
+ 'type' => 'url'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-algolia',
+ 'id' => 'sync-with-algolia',
+ 'name' => 'Sync with Algolia',
+ 'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/sync-with-algolia'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt',
+ 'src/main.py',
+ 'python/sync_with_algolia'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PHP'],
+ 'composer install',
+ 'src/index.php',
+ 'php/sync-with-algolia'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the Appwrite database that contains the collection to sync. Learn more.',
+ 'placeholder' => '64a55...7b912',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection in the Appwrite database to sync. Learn more.',
+ 'placeholder' => '7c3e8...2a9f1',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'ALGOLIA_APP_ID',
+ 'description' => 'The ID of the application in Algolia. Learn more.',
+ 'placeholder' => 'OFCNCOG2CU',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'ALGOLIA_ADMIN_API_KEY',
+ 'description' => 'The admin API Key for your Algolia service. Learn more.',
+ 'placeholder' => 'fd0aa...136a8',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'ALGOLIA_INDEX_ID',
+ 'description' => 'The ID of the index in Algolia where the documents are to be synced. Learn more.',
+ 'placeholder' => 'my_index',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'ALGOLIA_SEARCH_API_KEY',
+ 'description' => 'The search API Key for your Algolia service. This key is used for searching the synced index. Learn more.',
+ 'placeholder' => 'bf2f5...df733',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-meilisearch',
+ 'id' => 'sync-with-meilisearch',
+ 'name' => 'Sync with Meilisearch',
+ 'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Databases'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/sync-with-meilisearch'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt',
+ 'src/main.py',
+ 'python/sync-with-meilisearch'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PHP'],
+ 'composer install',
+ 'src/index.php',
+ 'php/sync-with-meilisearch'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['BUN'],
+ 'bun install',
+ 'src/main.ts',
+ 'bun/sync-with-meilisearch'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['RUBY'],
+ 'bundle install',
+ 'lib/main.rb',
+ 'ruby/sync-with-meilisearch'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the Appwrite database that contains the collection to sync. Learn more.',
+ 'placeholder' => '64a55...7b912',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection in the Appwrite database to sync. Learn more.',
+ 'placeholder' => '7c3e8...2a9f1',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'MEILISEARCH_ENDPOINT',
+ 'description' => 'The host URL of the Meilisearch server. Learn more.',
+ 'placeholder' => 'http://127.0.0.1:7700',
+ 'required' => true,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'MEILISEARCH_ADMIN_API_KEY',
+ 'description' => 'The admin API key for Meilisearch. Learn more.',
+ 'placeholder' => 'masterKey1234',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'MEILISEARCH_SEARCH_API_KEY',
+ 'description' => 'API Key for Meilisearch search operations. Learn more.',
+ 'placeholder' => 'searchKey1234',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'MEILISEARCH_INDEX_NAME',
+ 'description' => 'Name of the Meilisearch index to which the documents will be synchronized. Learn more.',
+ 'placeholder' => 'appwrite_index',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-vonage',
+ 'id' => 'whatsapp-with-vonage',
+ 'name' => 'WhatsApp with Vonage',
+ 'tagline' => 'Simple bot to answer WhatsApp messages.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Messaging'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/whatsapp-with-vonage'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt',
+ 'src/main.py',
+ 'python/whatsapp_with_vonage'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PHP'],
+ 'composer install',
+ 'src/index.php',
+ 'php/whatsapp-with-vonage'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['DART'],
+ 'dart pub get',
+ 'lib/main.dart',
+ 'dart/whatsapp-with-vonage'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['RUBY'],
+ 'bundle install',
+ '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 file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'VONAGE_API_KEY',
+ 'description' => 'API Key to use the Vonage API. Learn more.',
+ 'value' => '',
+ 'placeholder' => '62...97',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'VONAGE_API_SECRET',
+ 'description' => 'Secret to use the Vonage API. Learn more.',
+ 'placeholder' => 'Zjc...5PH',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'VONAGE_API_SIGNATURE_SECRET',
+ 'description' => 'Secret to verify the JWT token sent by Vonage. Learn more.',
+ 'placeholder' => 'NXOi3...IBHDa',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'VONAGE_WHATSAPP_NUMBER',
+ 'description' => 'Vonage WhatsApp number to send messages from. Learn more.',
+ 'placeholder' => '+14000000102',
+ 'required' => true,
+ 'type' => 'phone'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-bell',
+ 'id' => 'push-notification-with-fcm',
+ 'name' => 'Push notification with FCM',
+ 'tagline' => 'Send push notifications to your users using Firebase Cloud Messaging (FCM).',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Messaging'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/push-notification-with-fcm'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'FCM_PROJECT_ID',
+ 'description' => 'A unique identifier for your FCM project. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'mywebapp-f6e57',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'FCM_CLIENT_EMAIL',
+ 'description' => 'Your FCM service account email. Learn more.',
+ 'placeholder' => 'fcm-adminsdk-2f0de@test-f7q57.iam.gserviceaccount.com',
+ 'required' => true,
+ 'type' => 'email'
+ ],
+ [
+ 'name' => 'FCM_PRIVATE_KEY',
+ 'description' => 'A unique private key used to authenticate with FCM. Learn more.',
+ 'placeholder' => '0b683...75675',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'FCM_DATABASE_URL',
+ 'description' => 'URL of your FCM database. Learn more.',
+ 'placeholder' => 'https://my-app-f298e.firebaseio.com',
+ 'required' => true,
+ 'type' => 'url'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-mail',
+ 'id' => 'email-contact-form',
+ 'name' => 'Email contact form',
+ 'tagline' => 'Sends an email with the contents of a HTML form.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/email-contact-form'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PYTHON'],
+ 'pip install -r requirements.txt',
+ 'src/main.py',
+ 'python/email_contact_form'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['PHP'],
+ 'composer install',
+ 'src/index.php',
+ 'php/email-contact-form'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'SMTP_HOST',
+ 'description' => 'The address of your SMTP server. Many STMP providers will provide this information in their documentation. Some popular providers include: Mailgun, SendGrid, and Gmail.',
+ 'value' => '',
+ 'placeholder' => 'smtp.mailgun.org',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'SMTP_PORT',
+ 'description' => 'The port of your STMP server. Commnly used ports include 25, 465, and 587.',
+ 'placeholder' => '25',
+ 'required' => true,
+ 'type' => 'number'
+ ],
+ [
+ 'name' => 'SMTP_USERNAME',
+ 'description' => 'The username for your SMTP server. This is commonly your email address.',
+ 'placeholder' => 'no-reply@mywebapp.org',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'SMTP_PASSWORD',
+ 'description' => 'The password for your SMTP server.',
+ 'placeholder' => '5up3r5tr0ngP4ssw0rd',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'SUBMIT_EMAIL',
+ 'description' => 'The email address to send form submissions to.',
+ 'placeholder' => 'me@mywebapp.org',
+ 'required' => true,
+ 'type' => 'email'
+ ],
+ [
+ 'name' => 'ALLOWED_ORIGINS',
+ 'description' => 'An optional comma-separated list of allowed origins for CORS (defaults to *). This is an important security measure to prevent malicious users from abusing your function.',
+ 'value' => '',
+ 'placeholder' => 'https://mywebapp.org,https://mywebapp.com',
+ 'required' => false,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-stripe',
+ 'id' => 'subscriptions-with-stripe',
+ 'name' => 'Subscriptions with Stripe',
+ 'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/subscriptions-with-stripe'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'STRIPE_SECRET_KEY',
+ 'description' => 'Secret for sending requests to the Stripe API. Learn more.',
+ 'placeholder' => 'sk_test_51J...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'STRIPE_WEBHOOK_SECRET',
+ 'description' => 'Secret used to validate the Stripe Webhook signature. Learn more.',
+ 'placeholder' => 'whsec_...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-stripe',
+ 'id' => 'payments-with-stripe',
+ 'name' => 'Payments with Stripe',
+ 'tagline' => 'Receive card payments and store paid orders.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/payments-with-stripe'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'STRIPE_SECRET_KEY',
+ 'description' => 'Secret for sending requests to the Stripe API. Learn more.',
+ 'placeholder' => 'sk_test_51J...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'STRIPE_WEBHOOK_SECRET',
+ 'description' => 'Secret used to validate the Stripe Webhook signature. Learn more.',
+ 'placeholder' => 'whsec_...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database to store paid orders. Learn more.',
+ 'value' => 'orders',
+ 'placeholder' => 'orders',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection to store paid orders. Learn more.',
+ 'value' => 'orders',
+ 'placeholder' => 'orders',
+ 'required' => false,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chat',
+ 'id' => 'text-generation-with-huggingface',
+ 'name' => 'Text generation',
+ 'tagline' => 'Generate text using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 30,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/text-generation-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-translate',
+ 'id' => 'language-translation-with-huggingface',
+ 'name' => 'Language translation',
+ 'tagline' => 'Translate text using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 30,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/language-translation-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-eye',
+ 'id' => 'image-classification-with-huggingface',
+ 'name' => 'Image classification',
+ 'tagline' => 'Classify images using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => ['buckets.*.files.*.create'],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install && npm run setup',
+ 'src/main.js',
+ 'node/image-classification-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the responses are stored. Learn more.',
+ 'value' => 'ai',
+ 'placeholder' => 'ai',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the responses are stored. Learn more.',
+ 'value' => 'image_classification',
+ 'placeholder' => 'image_classification',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where the images are stored. Learn more.',
+ 'value' => 'image_classification',
+ 'placeholder' => 'image_classification',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-eye',
+ 'id' => 'object-detection-with-huggingface',
+ 'name' => 'Object detection',
+ 'tagline' => 'Detect objects in images using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => ['buckets.*.files.*.create'],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install && npm run setup',
+ 'src/main.js',
+ 'node/object-detection-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the responses are stored. Learn more.',
+ 'value' => 'ai',
+ 'placeholder' => 'ai',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the responses are stored. Learn more.',
+ 'value' => 'object_detection',
+ 'placeholder' => 'object_detection',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where the images are stored. Learn more.',
+ 'value' => 'object_detection',
+ 'placeholder' => 'object_detection',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-text',
+ 'id' => 'speech-recognition-with-huggingface',
+ 'name' => 'Speech recognition',
+ 'tagline' => 'Transcribe audio to text using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => ['buckets.*.files.*.create'],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install && npm run setup',
+ 'src/main.js',
+ 'node/speech-recognition-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the responses are stored. Learn more.',
+ 'value' => 'ai',
+ 'placeholder' => 'ai',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the responses are stored. Learn more.',
+ 'value' => 'speech_recognition',
+ 'placeholder' => 'speech_recognition',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where audio is stored. Learn more.',
+ 'value' => 'speech_recognition',
+ 'placeholder' => 'speech_recognition',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chat',
+ 'id' => 'text-to-speech-with-huggingface',
+ 'name' => 'Text to speech',
+ 'tagline' => 'Convert text to speech using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => ['databases.*.collections.*.documents.*.create'],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install && npm run setup',
+ 'src/main.js',
+ 'node/text-to-speech-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the responses are stored. Learn more.',
+ 'value' => 'ai',
+ 'placeholder' => 'ai',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the responses are stored. Learn more.',
+ 'value' => 'speech_recognition',
+ 'placeholder' => 'speech_recognition',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where audio is stored. Learn more.',
+ 'value' => 'speech_recognition',
+ 'placeholder' => 'speech_recognition',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'generate-with-replicate',
+ 'name' => 'Generate with Replicate',
+ 'tagline' => "Generate text, audio and images using Replicate's API.",
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 300,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/generate-with-replicate'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'REPLICATE_API_KEY',
+ 'description' => 'A unique key used to authenticate with the Replicate API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'generate-with-together-ai',
+ 'name' => 'Generate with Together AI',
+ 'tagline' => "Generate text and images using Together AI's API.",
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 300,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/generate-with-together-ai'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'TOGETHER_API_KEY',
+ 'description' => 'A unique key used to authenticate with the Together AI API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where audio is stored. Learn more.',
+ 'placeholder' => 'generated_speech',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'chat-with-perplexity-ai',
+ 'name' => 'Chat with Perplexity AI',
+ 'tagline' => 'Create a chatbot using the Perplexity AI API.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/chat-with-perplexity-ai'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'PERPLEXITY_API_KEY',
+ 'description' => 'A unique key used to authenticate with the Perplexity API. Learn more.',
+ 'placeholder' => 'pplex-68...999',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'PERPLEXITY_MAX_TOKENS',
+ 'description' => 'The maximum number of tokens to generate. Learn more.',
+ 'placeholder' => '512',
+ 'required' => false,
+ 'type' => 'number'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'generate-with-replicate',
+ 'name' => 'Generate with Replicate',
+ 'tagline' => "Generate text, audio and images using Replicate's API.",
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 300,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/generate-with-replicate'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'REPLICATE_API_KEY',
+ 'description' => 'A unique key used to authenticate with the Replicate API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-document-search',
+ 'id' => 'sync-with-pinecone',
+ 'name' => 'Sync with Pinecone',
+ 'tagline' => "Sync your Appwrite database with Pinecone's vector database.",
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 30,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/sync-with-pinecone'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'OPENAI_API_KEY',
+ 'description' => 'A unique key used to authenticate with the OpenAI API. This is a paid service and you will be charged for each request made to the API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'sk-wzG...vcy',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'PINECONE_API_KEY',
+ 'description' => 'A unique key used to authenticate with the Pinecone API. Learn more.',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'PINECONE_INDEX_NAME',
+ 'description' => 'The name of the index in Pinecone. Learn more.',
+ 'placeholder' => 'my-index',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the documents are stored. Learn more.',
+ 'placeholder' => 'my-database',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the documents are stored. Learn more.',
+ 'placeholder' => 'my-collection',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'rag-with-langchain',
+ 'name' => 'RAG with LangChain',
+ 'tagline' => 'Generate text using a LangChain RAG model',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 30,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/rag-with-langchain'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'OPENAI_API_KEY',
+ 'description' => 'A unique key used to authenticate with the OpenAI API. This is a paid service and you will be charged for each request made to the API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'sk-wzG...vcy',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'PINECONE_API_KEY',
+ 'description' => 'A unique key used to authenticate with the Pinecone API. Learn more.',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'PINECONE_INDEX_NAME',
+ 'description' => 'The name of the index in Pinecone. Learn more.',
+ 'placeholder' => 'my-index',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the documents are stored. Learn more.',
+ 'placeholder' => 'my-database',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the documents are stored. Learn more.',
+ 'placeholder' => 'my-collection',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chat',
+ 'id' => 'speak-with-elevenlabs',
+ 'name' => 'Speak with ElevenLabs',
+ 'tagline' => 'Convert text to speech using the ElevenLabs API.',
+ 'permissions' => ['any'],
+ 'cron' => '',
+ 'events' => [],
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/speak-with-elevenlabs'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'ELEVENLABS_API_KEY',
+ 'description' => 'A unique key used to authenticate with the ElevenLabs API. Learn more.',
+ 'placeholder' => 'd03xxxxxxxx26',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database where the responses are stored. Learn more.',
+ 'placeholder' => 'my-database',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection where the responses are stored. Learn more.',
+ 'placeholder' => 'my-collection',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where audio is stored. Learn more.',
+ 'placeholder' => 'generated_speech',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'speak-with-lmnt',
+ 'name' => 'Speak with LMNT',
+ 'tagline' => 'Convert text to speech using the LMNT API.',
+ 'permissions' => ['any'],
+ 'cron' => '',
+ 'events' => [],
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/speak-with-lmnt'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'LMNT_API_KEY',
+ 'description' => 'A unique key used to authenticate with the LMNT API. Learn more.',
+ 'placeholder' => 'd03xxxxxxxx26',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where audio is stored. Learn more.',
+ 'placeholder' => 'generated_speech',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'chat-with-anyscale',
+ 'name' => 'Chat with AnyScale',
+ 'tagline' => 'Create a chatbot using the AnyScale API.',
+ 'permissions' => ['any'],
+ 'cron' => '',
+ 'events' => [],
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/chat-with-anyscale'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'ANYSCALE_API_KEY',
+ 'description' => 'A unique key used to authenticate with the AnyScale API. Learn more.',
+ 'placeholder' => 'd03xxxxxxxx26',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'ANYSCALE_MAX_TOKENS',
+ 'description' => 'The maximum number of tokens that Anyscale responses should contain. Learn more.',
+ 'placeholder' => '',
+ 'required' => false,
+ 'type' => 'number'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-music-note',
+ 'id' => 'music-generation-with-huggingface',
+ 'name' => 'Music generation',
+ 'tagline' => 'Generate music from a text prompt using the Hugging Face inference API.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install && npm run setup',
+ 'src/main.js',
+ 'node/music-generation-with-huggingface'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_BUCKET_ID',
+ 'description' => 'The ID of the bucket where generated music is stored. Learn more.',
+ 'value' => 'generated_music',
+ 'placeholder' => 'generated_music',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'HUGGINGFACE_ACCESS_TOKEN',
+ 'description' => 'Secret for sending requests to the Hugging Face API. Learn more.',
+ 'placeholder' => 'hf_MUvn...',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-chip',
+ 'id' => 'generate-with-fal-ai',
+ 'name' => 'Generate with fal.ai',
+ 'tagline' => "Generate images using fal.ai's API.",
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 300,
+ 'usecases' => ['AI'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/generate-with-fal-ai'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'FAL_API_KEY',
+ 'description' => 'A unique key used to authenticate with the fal.ai API. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-currency-dollar',
+ 'id' => 'subscriptions-with-lemon-squeezy',
+ 'name' => 'Subscriptions with Lemon Squeezy',
+ 'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/subscriptions-with-lemon-squeezy'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_API_KEY',
+ 'description' => 'API key for sending requests to the Lemon Squeezy API. Learn more.',
+ 'placeholder' => 'eyJ0eXAiOiJ...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_WEBHOOK_SECRET',
+ 'description' => 'Secret used to validate the Lemon Squuezy Webhook signature. Learn more.',
+ 'placeholder' => 'abcd...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_STORE_ID',
+ 'description' => 'Store ID required to create a checkout using the Lemon Squeezy API. Learn more.',
+ 'placeholder' => '123456',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_VARIANT_ID',
+ 'description' => 'Variant ID of a product required to create a checkout using the Lemon Squeezy API. Learn more.',
+ 'placeholder' => 'abcd...',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ],
+ [
+ 'icon' => 'icon-currency-dollar',
+ 'id' => 'payments-with-lemon-squeezy',
+ 'name' => 'Payments with Lemon Squeezy',
+ 'tagline' => 'Receive card payments and store paid orders.',
+ 'permissions' => ['any'],
+ 'events' => [],
+ 'cron' => '',
+ 'timeout' => 15,
+ 'usecases' => ['Utilities'],
+ 'runtimes' => [
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['NODE'],
+ 'npm install',
+ 'src/main.js',
+ 'node/payments-with-lemon-squeezy'
+ )
+ ],
+ 'instructions' => 'For documentation and instructions check out file.',
+ 'vcsProvider' => 'github',
+ 'providerRepositoryId' => 'templates',
+ 'providerOwner' => 'appwrite',
+ 'providerBranch' => 'main',
+ 'variables' => [
+ [
+ 'name' => 'APPWRITE_API_KEY',
+ 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
+ 'value' => '',
+ 'placeholder' => 'd1efb...aec35',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'APPWRITE_ENDPOINT',
+ 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
+ 'value' => 'https://cloud.appwrite.io/v1',
+ 'placeholder' => 'https://cloud.appwrite.io/v1',
+ 'required' => false,
+ 'type' => 'url'
+ ],
+ [
+ 'name' => 'APPWRITE_DATABASE_ID',
+ 'description' => 'The ID of the database to store paid orders. Learn more.',
+ 'value' => 'orders',
+ 'placeholder' => 'orders',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'APPWRITE_COLLECTION_ID',
+ 'description' => 'The ID of the collection to store paid orders. Learn more.',
+ 'value' => 'orders',
+ 'placeholder' => 'orders',
+ 'required' => false,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_API_KEY',
+ 'description' => 'API key for sending requests to the Lemon Squeezy API. Learn more.',
+ 'placeholder' => 'eyJ0eXAiOiJ...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_WEBHOOK_SECRET',
+ 'description' => 'Secret used to validate the Lemon Squuezy Webhook signature. Learn more.',
+ 'placeholder' => 'abcd...',
+ 'required' => true,
+ 'type' => 'password'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_STORE_ID',
+ 'description' => 'Store ID required to create a checkout using the Lemon Squeezy API. Learn more.',
+ 'placeholder' => '123456',
+ 'required' => true,
+ 'type' => 'text'
+ ],
+ [
+ 'name' => 'LEMON_SQUEEZY_VARIANT_ID',
+ 'description' => 'Variant ID of a product required to create a checkout using the Lemon Squeezy API. Learn more.',
+ 'placeholder' => 'abcd...',
+ 'required' => true,
+ 'type' => 'text'
+ ]
+ ]
+ ]
+];
\ No newline at end of file
From 6c1cfb83a6ae4283389f6b2ad903a1245aa0499a Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Thu, 18 Jul 2024 23:50:09 +0530
Subject: [PATCH 02/16] Formatted file
---
app/config/templates.php | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/app/config/templates.php b/app/config/templates.php
index 5c6e4fbe0f..fff9735a94 100644
--- a/app/config/templates.php
+++ b/app/config/templates.php
@@ -27,15 +27,16 @@ const TEMPLATE_RUNTIMES = [
]
];
-function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = []) {
- return array_map(function($version) use ($runtime, $commands, $entrypoint, $providerRootDirectory) {
+function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = [])
+{
+ return array_map(function ($version) use ($runtime, $commands, $entrypoint, $providerRootDirectory) {
return [
'name' => $runtime['name'] . '-' . $version,
'commands' => $commands,
'entrypoint' => $entrypoint,
'providerRootDirectory' => $providerRootDirectory
];
- }, array_filter($runtime['versions'], function($version) use ($versionsDenyList) {
+ }, array_filter($runtime['versions'], function ($version) use ($versionsDenyList) {
return !in_array($version, $versionsDenyList);
}));
}
@@ -46,7 +47,7 @@ return [
'id' => 'starter',
'name' => 'Starter function',
'tagline' =>
- 'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
+ 'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -123,7 +124,7 @@ return [
'description' => 'Authentication token to access your Upstash Vector database. Learn more.',
'value' => '',
'placeholder' =>
- 'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
+ 'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
'required' => true,
'type' => 'password'
]
@@ -226,7 +227,7 @@ return [
'id' => 'query-mongo-atlas',
'name' => 'Query MongoDB Atlas',
'tagline' =>
- 'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
+ 'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -251,7 +252,7 @@ return [
'description' => 'The endpoint to connect to your Mongo database. Learn more.',
'value' => '',
'placeholder' =>
- 'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
+ 'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
'required' => true,
'type' => 'password'
]
@@ -262,7 +263,7 @@ return [
'id' => 'query-neon-postgres',
'name' => 'Query Neon Postgres',
'tagline' =>
- 'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
+ 'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -479,7 +480,7 @@ return [
'id' => 'censor-with-redact',
'name' => 'Censor with Redact',
'tagline' =>
- 'Censor sensitive information from a provided text string using Redact API by Pangea.',
+ 'Censor sensitive information from a provided text string using Redact API by Pangea.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -546,7 +547,7 @@ return [
'id' => 'github-issue-bot',
'name' => 'GitHub issue bot',
'tagline' =>
- 'Automate the process of responding to newly opened issues in a GitHub repository.',
+ 'Automate the process of responding to newly opened issues in a GitHub repository.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -2296,4 +2297,4 @@ return [
]
]
]
-];
\ No newline at end of file
+];
From 1b5d815532b9ba460e7a01e601852b296ef1ccf2 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Fri, 19 Jul 2024 17:39:18 +0530
Subject: [PATCH 03/16] Add get function templates endpoint
---
.../{templates.php => functionTemplates.php} | 0
app/controllers/api/functions.php | 20 +++
app/init.php | 1 +
.../functions/get-function-templates.md | 1 +
src/Appwrite/Utopia/Response.php | 15 ++
.../Response/Model/FunctionTemplate.php | 136 ++++++++++++++++++
.../Utopia/Response/Model/RuntimeTemplate.php | 59 ++++++++
.../Response/Model/VariableTemplate.php | 65 +++++++++
8 files changed, 297 insertions(+)
rename app/config/{templates.php => functionTemplates.php} (100%)
create mode 100644 docs/references/functions/get-function-templates.md
create mode 100644 src/Appwrite/Utopia/Response/Model/FunctionTemplate.php
create mode 100644 src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php
create mode 100644 src/Appwrite/Utopia/Response/Model/VariableTemplate.php
diff --git a/app/config/templates.php b/app/config/functionTemplates.php
similarity index 100%
rename from app/config/templates.php
rename to app/config/functionTemplates.php
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index d2a1790d94..1318f2ce3f 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2349,3 +2349,23 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
$response->noContent();
});
+
+App::get('/v1/functions/templates')
+ ->desc('Get function templates')
+ ->groups(['api', 'functions'])
+ ->label('scope', 'functions.read')
+ ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
+ ->label('sdk.namespace', 'functions')
+ ->label('sdk.method', 'getFunctionTemplates')
+ ->label('sdk.description', '/docs/references/functions/get-function-templates.md')
+ ->label('sdk.response.code', Response::STATUS_CODE_OK)
+ ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
+ ->label('sdk.response.model', Response::MODEL_FUNCTION_TEMPLATE_LIST)
+ ->inject('response')
+ ->action(function (Response $response) {
+ $templates = Config::getParam('functionTemplates', []);
+ $response->dynamic(new Document([
+ 'templates' => $templates,
+ 'total' => \count($templates),
+ ]), Response::MODEL_FUNCTION_TEMPLATE_LIST);
+ });
diff --git a/app/init.php b/app/init.php
index a0e71f041b..dfc04ca2ae 100644
--- a/app/init.php
+++ b/app/init.php
@@ -303,6 +303,7 @@ Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
+Config::load('functionTemplates', __DIR__ . '/config/functionTemplates.php'); // List of function templates
/**
* New DB Filters
diff --git a/docs/references/functions/get-function-templates.md b/docs/references/functions/get-function-templates.md
new file mode 100644
index 0000000000..8edc21e996
--- /dev/null
+++ b/docs/references/functions/get-function-templates.md
@@ -0,0 +1 @@
+Get function templates from marketplace.
\ No newline at end of file
diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php
index 128fce62c8..59a654849a 100644
--- a/src/Appwrite/Utopia/Response.php
+++ b/src/Appwrite/Utopia/Response.php
@@ -44,6 +44,7 @@ use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Func;
+use Appwrite\Utopia\Response\Model\FunctionTemplate;
use Appwrite\Utopia\Response\Model\Headers;
use Appwrite\Utopia\Response\Model\HealthAntivirus;
use Appwrite\Utopia\Response\Model\HealthCertificate;
@@ -82,6 +83,7 @@ use Appwrite\Utopia\Response\Model\Provider;
use Appwrite\Utopia\Response\Model\ProviderRepository;
use Appwrite\Utopia\Response\Model\Rule;
use Appwrite\Utopia\Response\Model\Runtime;
+use Appwrite\Utopia\Response\Model\RuntimeTemplate;
use Appwrite\Utopia\Response\Model\Session;
use Appwrite\Utopia\Response\Model\Subscriber;
use Appwrite\Utopia\Response\Model\Target;
@@ -101,6 +103,7 @@ use Appwrite\Utopia\Response\Model\UsageStorage;
use Appwrite\Utopia\Response\Model\UsageUsers;
use Appwrite\Utopia\Response\Model\User;
use Appwrite\Utopia\Response\Model\Variable;
+use Appwrite\Utopia\Response\Model\VariableTemplate;
use Appwrite\Utopia\Response\Model\VcsContent;
use Appwrite\Utopia\Response\Model\Webhook;
use Exception;
@@ -251,6 +254,12 @@ class Response extends SwooleResponse
public const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
public const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
public const MODEL_HEADERS = 'headers';
+ public const MODEL_FUNCTION_TEMPLATE = 'functionTemplate';
+ public const MODEL_FUNCTION_TEMPLATE_LIST = 'functionTemplateList';
+ public const MODEL_RUNTIME_TEMPLATE = 'runtimeTemplate';
+ public const MODEL_VARIABLE_TEMPLATE = 'variableTemplate';
+ public const MODEL_RUNTIME_TEMPLATE_LIST = 'runtimeTemplateList';
+ public const MODEL_VARIABLE_TEMPLATE_LIST = 'variableTemplateList';
// Proxy
public const MODEL_PROXY_RULE = 'proxyRule';
@@ -340,6 +349,9 @@ class Response extends SwooleResponse
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
+ ->setModel(new BaseList('Function Templates List', self::MODEL_FUNCTION_TEMPLATE_LIST, 'templates', self::MODEL_FUNCTION_TEMPLATE))
+ ->setModel(new BaseList('Runtime Templates List', self::MODEL_RUNTIME_TEMPLATE_LIST, 'runtimes', self::MODEL_RUNTIME_TEMPLATE))
+ ->setModel(new BaseList('Variable Templates List', self::MODEL_VARIABLE_TEMPLATE_LIST, 'variables', self::MODEL_VARIABLE_TEMPLATE))
->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION))
->setModel(new BaseList('Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', self::MODEL_PROVIDER_REPOSITORY))
->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH))
@@ -409,6 +421,9 @@ class Response extends SwooleResponse
->setModel(new Team())
->setModel(new Membership())
->setModel(new Func())
+ ->setModel(new FunctionTemplate())
+ ->setModel(new RuntimeTemplate())
+ ->setModel(new VariableTemplate())
->setModel(new Installation())
->setModel(new ProviderRepository())
->setModel(new Detection())
diff --git a/src/Appwrite/Utopia/Response/Model/FunctionTemplate.php b/src/Appwrite/Utopia/Response/Model/FunctionTemplate.php
new file mode 100644
index 0000000000..f67400b095
--- /dev/null
+++ b/src/Appwrite/Utopia/Response/Model/FunctionTemplate.php
@@ -0,0 +1,136 @@
+addRule('icon', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function Template Icon.',
+ 'default' => '',
+ 'example' => 'icon-lightning-bolt',
+ ])
+ ->addRule('$id', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function Template ID.',
+ 'default' => '',
+ 'example' => 'starter',
+ ])
+ ->addRule('name', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function Template Name.',
+ 'default' => '',
+ 'example' => 'Starter function',
+ ])
+ ->addRule('tagline', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function Template Tagline.',
+ 'default' => '',
+ 'example' => 'A simple function to get started.',
+ ])
+ ->addRule('permissions', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Execution permissions.',
+ 'default' => [],
+ 'example' => 'any',
+ 'array' => true,
+ ])
+ ->addRule('events', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function trigger events.',
+ 'default' => [],
+ 'example' => 'account.create',
+ 'array' => true,
+ ])
+ ->addRule('cron', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function execution schedult in CRON format.',
+ 'default' => '',
+ 'example' => '0 0 * * *',
+ ])
+ ->addRule('timeout', [
+ 'type' => self::TYPE_INTEGER,
+ 'description' => 'Function execution timeout in seconds.',
+ 'default' => 15,
+ 'example' => 300,
+ ])
+ ->addRule('usecases', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function usecases.',
+ 'default' => [],
+ 'example' => 'Starter',
+ 'array' => true,
+ ])
+ ->addRule('runtimes', [
+ 'type' => Response::MODEL_RUNTIME_TEMPLATE,
+ 'description' => 'List of runtimes that can be used with this template.',
+ 'default' => [],
+ 'example' => [],
+ 'array' => true
+ ])
+ ->addRule('instructions', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Function Template Instructions.',
+ 'default' => '',
+ 'example' => 'For documentation and instructions check out .',
+ ])
+ ->addRule('vcsProvider', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'VCS (Version Control System) Provider.',
+ 'default' => '',
+ 'example' => 'github',
+ ])
+ ->addRule('providerRepositoryId', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'VCS (Version Control System) Repository ID',
+ 'default' => '',
+ 'example' => 'templates',
+ ])
+ ->addRule('providerOwner', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'VCS (Version Control System) Owner.',
+ 'default' => '',
+ 'example' => 'appwrite',
+ ])
+ ->addRule('providerBranch', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'VCS (Version Control System) branch name',
+ 'default' => '',
+ 'example' => 'main',
+ ])
+ ->addRule('variables', [
+ 'type' => Response::MODEL_VARIABLE_TEMPLATE,
+ 'description' => 'Function variables.',
+ 'default' => [],
+ 'example' => [],
+ 'array' => true
+ ])
+ ;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return 'Function Template';
+ }
+
+ /**
+ * Get Type
+ *
+ * @return string
+ */
+ public function getType(): string
+ {
+ return Response::MODEL_FUNCTION_TEMPLATE;
+ }
+}
diff --git a/src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php b/src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php
new file mode 100644
index 0000000000..de1a94302a
--- /dev/null
+++ b/src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php
@@ -0,0 +1,59 @@
+addRule('name', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Runtime Name.',
+ 'default' => '',
+ 'example' => 'node-19.0',
+ ])
+ ->addRule('commands', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'The build command used to build the deployment.',
+ 'default' => '',
+ 'example' => 'npm install',
+ ])
+ ->addRule('entrypoint', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'The entrypoint file used to execute the deployment.',
+ 'default' => '',
+ 'example' => 'index.js',
+ ])
+ ->addRule('providerRootDirectory', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Path to function in VCS (Version Control System) repository',
+ 'default' => '',
+ 'example' => 'node/starter',
+ ])
+ ;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return 'Runtime Template';
+ }
+
+ /**
+ * Get Type
+ *
+ * @return string
+ */
+ public function getType(): string
+ {
+ return Response::MODEL_RUNTIME_TEMPLATE;
+ }
+}
diff --git a/src/Appwrite/Utopia/Response/Model/VariableTemplate.php b/src/Appwrite/Utopia/Response/Model/VariableTemplate.php
new file mode 100644
index 0000000000..fdb464681d
--- /dev/null
+++ b/src/Appwrite/Utopia/Response/Model/VariableTemplate.php
@@ -0,0 +1,65 @@
+addRule('name', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Variable Name.',
+ 'default' => '',
+ 'example' => 'APPWRITE_DATABASE_ID',
+ ])
+ ->addRule('description', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Variable Description.',
+ 'default' => '',
+ 'example' => 'The ID of the Appwrite database that contains the collection to sync.',
+ ])
+ ->addRule('placeholder', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Variable Placeholder.',
+ 'default' => '',
+ 'example' => '64a55...7b912',
+ ])
+ ->addRule('required', [
+ 'type' => self::TYPE_BOOLEAN,
+ 'description' => 'Is the variable required?',
+ 'default' => false,
+ 'example' => false,
+ ])
+ ->addRule('type', [
+ 'type' => self::TYPE_STRING,
+ 'description' => 'Variable Type.',
+ 'default' => '',
+ 'example' => 'password',
+ ])
+ ;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return 'Variable Template';
+ }
+
+ /**
+ * Get Type
+ *
+ * @return string
+ */
+ public function getType(): string
+ {
+ return Response::MODEL_VARIABLE_TEMPLATE;
+ }
+}
From 6af886687e522e736616ce5969423cfbc2d3a924 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Fri, 19 Jul 2024 18:24:20 +0530
Subject: [PATCH 04/16] Add Go templates
---
app/config/functionTemplates.php | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/app/config/functionTemplates.php b/app/config/functionTemplates.php
index fff9735a94..d3ac7a2df7 100644
--- a/app/config/functionTemplates.php
+++ b/app/config/functionTemplates.php
@@ -24,6 +24,10 @@ const TEMPLATE_RUNTIMES = [
'BUN' => [
'name' => 'bun',
'versions' => ['1.0']
+ ],
+ 'GO' => [
+ 'name' => 'go',
+ 'versions' => ['1.22']
]
];
@@ -69,7 +73,8 @@ return [
'python/starter'
),
...getRuntimes(TEMPLATE_RUNTIMES['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
- ...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter')
+ ...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
+ ...getRuntimes(TEMPLATE_RUNTIMES['GO'], 'go get', 'main.go', 'go/starter')
],
'instructions' => 'For documentation and instructions check out file.',
'vcsProvider' => 'github',
@@ -407,6 +412,12 @@ return [
'pip install -r requirements.txt && python src/setup.py',
'src/main.py',
'python/discord_command_bot'
+ ),
+ ...getRuntimes(
+ TEMPLATE_RUNTIMES['GO'],
+ 'go get',
+ 'main.go',
+ 'go/discord-command-bot'
)
],
'instructions' => 'For documentation and instructions check out file.',
From 14589a11c0c6493ef501d21e7f52d23d8b2eafbe Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Fri, 19 Jul 2024 18:24:59 +0530
Subject: [PATCH 05/16] Add Go templates
---
app/config/functionTemplates.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/config/functionTemplates.php b/app/config/functionTemplates.php
index d3ac7a2df7..e00db3e44e 100644
--- a/app/config/functionTemplates.php
+++ b/app/config/functionTemplates.php
@@ -74,7 +74,7 @@ return [
),
...getRuntimes(TEMPLATE_RUNTIMES['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
- ...getRuntimes(TEMPLATE_RUNTIMES['GO'], 'go get', 'main.go', 'go/starter')
+ ...getRuntimes(TEMPLATE_RUNTIMES['GO'], '', 'main.go', 'go/starter')
],
'instructions' => 'For documentation and instructions check out file.',
'vcsProvider' => 'github',
@@ -415,7 +415,7 @@ return [
),
...getRuntimes(
TEMPLATE_RUNTIMES['GO'],
- 'go get',
+ '',
'main.go',
'go/discord-command-bot'
)
From df5c495e8ffa8711873300146a8e9a18c16c8ea1 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Fri, 26 Jul 2024 16:06:40 +0530
Subject: [PATCH 06/16] Remove APPWRITE_API_KEY and add scopes
---
app/config/functionTemplates.php | 335 ++++---------------------------
1 file changed, 36 insertions(+), 299 deletions(-)
diff --git a/app/config/functionTemplates.php b/app/config/functionTemplates.php
index e00db3e44e..940229b83c 100644
--- a/app/config/functionTemplates.php
+++ b/app/config/functionTemplates.php
@@ -81,16 +81,8 @@ return [
'providerRepositoryId' => 'templates',
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
- 'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => false,
- 'type' => 'password'
- ]
- ]
+ 'variables' => [],
+ 'scopes' => ["users.read"]
],
[
'icon' => 'icon-upstash',
@@ -620,22 +612,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The API endpoint of the Appwrite. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database to store the short URLs. Learn more.',
@@ -660,7 +636,8 @@ return [
'required' => true,
'type' => 'url'
]
- ]
+ ],
+ 'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"]
],
[
'icon' => 'icon-algolia',
@@ -698,14 +675,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the Appwrite database that contains the collection to sync. Learn more.',
@@ -748,15 +717,8 @@ return [
'required' => true,
'type' => 'password'
],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ]
- ]
+ ],
+ 'scopes' => ["databases.read", "collections.read", "documents.read"]
],
[
'icon' => 'icon-meilisearch',
@@ -806,14 +768,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the Appwrite database that contains the collection to sync. Learn more.',
@@ -856,15 +810,8 @@ return [
'required' => true,
'type' => 'text'
],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ]
- ]
+ ],
+ 'scopes' => ["databases.read", "collections.read", "documents.read"]
],
[
'icon' => 'icon-vonage',
@@ -1112,22 +1059,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'STRIPE_SECRET_KEY',
'description' => 'Secret for sending requests to the Stripe API. Learn more.',
@@ -1142,7 +1073,8 @@ return [
'required' => true,
'type' => 'password'
]
- ]
+ ],
+ 'scopes' => ["users.read", "sessions.write", "users.write"]
],
[
'icon' => 'icon-stripe',
@@ -1168,22 +1100,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'STRIPE_SECRET_KEY',
'description' => 'Secret for sending requests to the Stripe API. Learn more.',
@@ -1214,7 +1130,8 @@ return [
'required' => false,
'type' => 'text'
]
- ]
+ ],
+ 'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"]
],
[
'icon' => 'icon-chat',
@@ -1306,22 +1223,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the responses are stored. Learn more.',
@@ -1353,7 +1254,8 @@ return [
'required' => true,
'type' => 'password'
]
- ]
+ ],
+ 'scopes' => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
],
[
'icon' => 'icon-eye',
@@ -1379,22 +1281,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the responses are stored. Learn more.',
@@ -1426,7 +1312,8 @@ return [
'required' => true,
'type' => 'password'
]
- ]
+ ],
+ "scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
],
[
'icon' => 'icon-text',
@@ -1452,22 +1339,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the responses are stored. Learn more.',
@@ -1499,7 +1370,8 @@ return [
'required' => true,
'type' => 'password'
]
- ]
+ ],
+ "scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"]
],
[
'icon' => 'icon-chat',
@@ -1525,22 +1397,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the responses are stored. Learn more.',
@@ -1572,7 +1428,8 @@ return [
'required' => true,
'type' => 'password'
]
- ]
+ ],
+ "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
],
[
'icon' => 'icon-chip',
@@ -1640,22 +1497,6 @@ return [
'required' => true,
'type' => 'password'
],
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_BUCKET_ID',
'description' => 'The ID of the bucket where audio is stored. Learn more.',
@@ -1663,7 +1504,8 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["buckets.write", "files.read", "files.write"]
],
[
'icon' => 'icon-chip',
@@ -1785,22 +1627,6 @@ return [
'required' => true,
'type' => 'text'
],
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the documents are stored. Learn more.',
@@ -1815,7 +1641,8 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["databases.read", "collections.read", "documents.read"]
],
[
'icon' => 'icon-chip',
@@ -1863,22 +1690,6 @@ return [
'required' => true,
'type' => 'text'
],
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the documents are stored. Learn more.',
@@ -1893,7 +1704,8 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["databases.read", "collections.read", "documents.read"]
],
[
'icon' => 'icon-chat',
@@ -1926,22 +1738,6 @@ return [
'required' => true,
'type' => 'password'
],
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database where the responses are stored. Learn more.',
@@ -1963,7 +1759,8 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
],
[
'icon' => 'icon-chip',
@@ -1996,22 +1793,6 @@ return [
'required' => true,
'type' => 'password'
],
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_BUCKET_ID',
'description' => 'The ID of the bucket where audio is stored. Learn more.',
@@ -2019,7 +1800,8 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
],
[
'icon' => 'icon-chip',
@@ -2085,22 +1867,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_BUCKET_ID',
'description' => 'The ID of the bucket where generated music is stored. Learn more.',
@@ -2116,7 +1882,8 @@ return [
'required' => true,
'type' => 'password'
]
- ]
+ ],
+ "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"]
],
[
'icon' => 'icon-chip',
@@ -2176,22 +1943,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'LEMON_SQUEEZY_API_KEY',
'description' => 'API key for sending requests to the Lemon Squeezy API. Learn more.',
@@ -2220,7 +1971,8 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["users.read", "users.write"]
],
[
'icon' => 'icon-currency-dollar',
@@ -2246,22 +1998,6 @@ return [
'providerOwner' => 'appwrite',
'providerBranch' => 'main',
'variables' => [
- [
- 'name' => 'APPWRITE_API_KEY',
- 'description' => 'The API Key to authenticate against Appwrite\'s Server APIs. Learn more.',
- 'value' => '',
- 'placeholder' => 'd1efb...aec35',
- 'required' => true,
- 'type' => 'password'
- ],
- [
- 'name' => 'APPWRITE_ENDPOINT',
- 'description' => 'The URL endpoint of the Appwrite server. Learn more.',
- 'value' => 'https://cloud.appwrite.io/v1',
- 'placeholder' => 'https://cloud.appwrite.io/v1',
- 'required' => false,
- 'type' => 'url'
- ],
[
'name' => 'APPWRITE_DATABASE_ID',
'description' => 'The ID of the database to store paid orders. Learn more.',
@@ -2306,6 +2042,7 @@ return [
'required' => true,
'type' => 'text'
]
- ]
+ ],
+ "scopes" => ["users.read", "users.write"]
]
];
From 03de5782932c6d35236f06b6b2b1366c98681802 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Fri, 26 Jul 2024 17:47:03 +0530
Subject: [PATCH 07/16] Rename as per convensions
---
...onTemplates.php => function-templates.php} | 0
app/controllers/api/functions.php | 6 ++---
app/init.php | 2 +-
src/Appwrite/Utopia/Response.php | 26 ++++++++-----------
...ctionTemplate.php => TemplateFunction.php} | 10 +++----
...untimeTemplate.php => TemplateRuntime.php} | 6 ++---
...iableTemplate.php => TemplateVariable.php} | 6 ++---
7 files changed, 26 insertions(+), 30 deletions(-)
rename app/config/{functionTemplates.php => function-templates.php} (100%)
rename src/Appwrite/Utopia/Response/Model/{FunctionTemplate.php => TemplateFunction.php} (94%)
rename src/Appwrite/Utopia/Response/Model/{RuntimeTemplate.php => TemplateRuntime.php} (92%)
rename src/Appwrite/Utopia/Response/Model/{VariableTemplate.php => TemplateVariable.php} (92%)
diff --git a/app/config/functionTemplates.php b/app/config/function-templates.php
similarity index 100%
rename from app/config/functionTemplates.php
rename to app/config/function-templates.php
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index 1318f2ce3f..7a9a217ad4 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2360,12 +2360,12 @@ App::get('/v1/functions/templates')
->label('sdk.description', '/docs/references/functions/get-function-templates.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
- ->label('sdk.response.model', Response::MODEL_FUNCTION_TEMPLATE_LIST)
+ ->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
->inject('response')
->action(function (Response $response) {
- $templates = Config::getParam('functionTemplates', []);
+ $templates = Config::getParam('function-templates', []);
$response->dynamic(new Document([
'templates' => $templates,
'total' => \count($templates),
- ]), Response::MODEL_FUNCTION_TEMPLATE_LIST);
+ ]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
});
diff --git a/app/init.php b/app/init.php
index 81688fcbd3..e7a07a40d0 100644
--- a/app/init.php
+++ b/app/init.php
@@ -303,7 +303,7 @@ Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
-Config::load('functionTemplates', __DIR__ . '/config/functionTemplates.php'); // List of function templates
+Config::load('function-templates', __DIR__ . '/config/function-templates.php'); // List of function templates
/**
* New DB Filters
diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php
index 59a654849a..d2e78bb310 100644
--- a/src/Appwrite/Utopia/Response.php
+++ b/src/Appwrite/Utopia/Response.php
@@ -44,7 +44,6 @@ use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Func;
-use Appwrite\Utopia\Response\Model\FunctionTemplate;
use Appwrite\Utopia\Response\Model\Headers;
use Appwrite\Utopia\Response\Model\HealthAntivirus;
use Appwrite\Utopia\Response\Model\HealthCertificate;
@@ -83,13 +82,15 @@ use Appwrite\Utopia\Response\Model\Provider;
use Appwrite\Utopia\Response\Model\ProviderRepository;
use Appwrite\Utopia\Response\Model\Rule;
use Appwrite\Utopia\Response\Model\Runtime;
-use Appwrite\Utopia\Response\Model\RuntimeTemplate;
use Appwrite\Utopia\Response\Model\Session;
use Appwrite\Utopia\Response\Model\Subscriber;
use Appwrite\Utopia\Response\Model\Target;
use Appwrite\Utopia\Response\Model\Team;
use Appwrite\Utopia\Response\Model\TemplateEmail;
+use Appwrite\Utopia\Response\Model\TemplateFunction;
+use Appwrite\Utopia\Response\Model\TemplateRuntime;
use Appwrite\Utopia\Response\Model\TemplateSMS;
+use Appwrite\Utopia\Response\Model\TemplateVariable;
use Appwrite\Utopia\Response\Model\Token;
use Appwrite\Utopia\Response\Model\Topic;
use Appwrite\Utopia\Response\Model\UsageBuckets;
@@ -103,7 +104,6 @@ use Appwrite\Utopia\Response\Model\UsageStorage;
use Appwrite\Utopia\Response\Model\UsageUsers;
use Appwrite\Utopia\Response\Model\User;
use Appwrite\Utopia\Response\Model\Variable;
-use Appwrite\Utopia\Response\Model\VariableTemplate;
use Appwrite\Utopia\Response\Model\VcsContent;
use Appwrite\Utopia\Response\Model\Webhook;
use Exception;
@@ -254,12 +254,10 @@ class Response extends SwooleResponse
public const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
public const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
public const MODEL_HEADERS = 'headers';
- public const MODEL_FUNCTION_TEMPLATE = 'functionTemplate';
- public const MODEL_FUNCTION_TEMPLATE_LIST = 'functionTemplateList';
- public const MODEL_RUNTIME_TEMPLATE = 'runtimeTemplate';
- public const MODEL_VARIABLE_TEMPLATE = 'variableTemplate';
- public const MODEL_RUNTIME_TEMPLATE_LIST = 'runtimeTemplateList';
- public const MODEL_VARIABLE_TEMPLATE_LIST = 'variableTemplateList';
+ public const MODEL_TEMPLATE_FUNCTION = 'templateFunction';
+ public const MODEL_TEMPLATE_FUNCTION_LIST = 'templateFunctionList';
+ public const MODEL_TEMPLATE_RUNTIME = 'templateRuntime';
+ public const MODEL_TEMPLATE_VARIABLE = 'templateVariable';
// Proxy
public const MODEL_PROXY_RULE = 'proxyRule';
@@ -349,9 +347,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
- ->setModel(new BaseList('Function Templates List', self::MODEL_FUNCTION_TEMPLATE_LIST, 'templates', self::MODEL_FUNCTION_TEMPLATE))
- ->setModel(new BaseList('Runtime Templates List', self::MODEL_RUNTIME_TEMPLATE_LIST, 'runtimes', self::MODEL_RUNTIME_TEMPLATE))
- ->setModel(new BaseList('Variable Templates List', self::MODEL_VARIABLE_TEMPLATE_LIST, 'variables', self::MODEL_VARIABLE_TEMPLATE))
+ ->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION))
->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION))
->setModel(new BaseList('Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', self::MODEL_PROVIDER_REPOSITORY))
->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH))
@@ -421,9 +417,9 @@ class Response extends SwooleResponse
->setModel(new Team())
->setModel(new Membership())
->setModel(new Func())
- ->setModel(new FunctionTemplate())
- ->setModel(new RuntimeTemplate())
- ->setModel(new VariableTemplate())
+ ->setModel(new TemplateFunction())
+ ->setModel(new TemplateRuntime())
+ ->setModel(new TemplateVariable())
->setModel(new Installation())
->setModel(new ProviderRepository())
->setModel(new Detection())
diff --git a/src/Appwrite/Utopia/Response/Model/FunctionTemplate.php b/src/Appwrite/Utopia/Response/Model/TemplateFunction.php
similarity index 94%
rename from src/Appwrite/Utopia/Response/Model/FunctionTemplate.php
rename to src/Appwrite/Utopia/Response/Model/TemplateFunction.php
index f67400b095..5fdbd95a35 100644
--- a/src/Appwrite/Utopia/Response/Model/FunctionTemplate.php
+++ b/src/Appwrite/Utopia/Response/Model/TemplateFunction.php
@@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
-class FunctionTemplate extends Model
+class TemplateFunction extends Model
{
public function __construct()
{
@@ -68,7 +68,7 @@ class FunctionTemplate extends Model
'array' => true,
])
->addRule('runtimes', [
- 'type' => Response::MODEL_RUNTIME_TEMPLATE,
+ 'type' => Response::MODEL_TEMPLATE_RUNTIME,
'description' => 'List of runtimes that can be used with this template.',
'default' => [],
'example' => [],
@@ -105,7 +105,7 @@ class FunctionTemplate extends Model
'example' => 'main',
])
->addRule('variables', [
- 'type' => Response::MODEL_VARIABLE_TEMPLATE,
+ 'type' => Response::MODEL_TEMPLATE_VARIABLE,
'description' => 'Function variables.',
'default' => [],
'example' => [],
@@ -121,7 +121,7 @@ class FunctionTemplate extends Model
*/
public function getName(): string
{
- return 'Function Template';
+ return 'Template Function';
}
/**
@@ -131,6 +131,6 @@ class FunctionTemplate extends Model
*/
public function getType(): string
{
- return Response::MODEL_FUNCTION_TEMPLATE;
+ return Response::MODEL_TEMPLATE_FUNCTION;
}
}
diff --git a/src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php b/src/Appwrite/Utopia/Response/Model/TemplateRuntime.php
similarity index 92%
rename from src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php
rename to src/Appwrite/Utopia/Response/Model/TemplateRuntime.php
index de1a94302a..c08ea9b32a 100644
--- a/src/Appwrite/Utopia/Response/Model/RuntimeTemplate.php
+++ b/src/Appwrite/Utopia/Response/Model/TemplateRuntime.php
@@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
-class RuntimeTemplate extends Model
+class TemplateRuntime extends Model
{
public function __construct()
{
@@ -44,7 +44,7 @@ class RuntimeTemplate extends Model
*/
public function getName(): string
{
- return 'Runtime Template';
+ return 'Template Runtime';
}
/**
@@ -54,6 +54,6 @@ class RuntimeTemplate extends Model
*/
public function getType(): string
{
- return Response::MODEL_RUNTIME_TEMPLATE;
+ return Response::MODEL_TEMPLATE_RUNTIME;
}
}
diff --git a/src/Appwrite/Utopia/Response/Model/VariableTemplate.php b/src/Appwrite/Utopia/Response/Model/TemplateVariable.php
similarity index 92%
rename from src/Appwrite/Utopia/Response/Model/VariableTemplate.php
rename to src/Appwrite/Utopia/Response/Model/TemplateVariable.php
index fdb464681d..b0fd919dbf 100644
--- a/src/Appwrite/Utopia/Response/Model/VariableTemplate.php
+++ b/src/Appwrite/Utopia/Response/Model/TemplateVariable.php
@@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
-class VariableTemplate extends Model
+class TemplateVariable extends Model
{
public function __construct()
{
@@ -50,7 +50,7 @@ class VariableTemplate extends Model
*/
public function getName(): string
{
- return 'Variable Template';
+ return 'Template Variable';
}
/**
@@ -60,6 +60,6 @@ class VariableTemplate extends Model
*/
public function getType(): string
{
- return Response::MODEL_VARIABLE_TEMPLATE;
+ return Response::MODEL_TEMPLATE_VARIABLE;
}
}
From af94fc7882d623505b3def58a15f537f9a1edbf8 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Fri, 26 Jul 2024 17:59:18 +0530
Subject: [PATCH 08/16] Add test for endpoint
---
.../Functions/FunctionsCustomServerTest.php | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
index c9f9e4443f..bed86a1d72 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
@@ -2097,4 +2097,18 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
}
+
+ public function testGetFunctionTemplates()
+ {
+ $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], $this->getHeaders()));
+
+ $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]);
+ }
}
From 7dca530f7078e7ec37dce0ee5d6fd9adf85e307b Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Mon, 29 Jul 2024 16:02:49 +0530
Subject: [PATCH 09/16] Add pagination to templates
---
app/controllers/api/functions.php | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index d294d5e4ee..995954397a 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2368,9 +2368,11 @@ App::get('/v1/functions/templates')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
+ ->param('limit', 10, new Range(0, 200), 'Limit the number of templates returned in the response. Max limit: 100.', true)
+ ->param('offset', 0, new Range(0, 200), 'Offset the list of returned templates. Max offset: 100.', true)
->inject('response')
- ->action(function (Response $response) {
- $templates = Config::getParam('function-templates', []);
+ ->action(function (int $limit, int $offset, Response $response) {
+ $templates = \array_slice(Config::getParam('function-templates', []), $offset, $limit);
$response->dynamic(new Document([
'templates' => $templates,
'total' => \count($templates),
From 9f0f5657cb8ef0667bc39218665a4d13c50512c9 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Mon, 29 Jul 2024 17:55:58 +0530
Subject: [PATCH 10/16] Add filtering using usecase
---
app/config/function-templates.php | 78 +++++++++++++++----------------
app/config/template-usecases.php | 10 ++++
app/controllers/api/functions.php | 23 ++++++---
app/init.php | 1 +
4 files changed, 66 insertions(+), 46 deletions(-)
create mode 100644 app/config/template-usecases.php
diff --git a/app/config/function-templates.php b/app/config/function-templates.php
index 940229b83c..c8562648be 100644
--- a/app/config/function-templates.php
+++ b/app/config/function-templates.php
@@ -56,7 +56,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Starter'],
+ 'usecases' => ['starter'],
'runtimes' => [
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/starter'),
...getRuntimes(
@@ -93,7 +93,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -136,7 +136,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -178,7 +178,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -229,7 +229,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -265,7 +265,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -331,7 +331,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -391,7 +391,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Messaging'],
+ 'usecases' => ['messaging'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -453,7 +453,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -488,7 +488,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -534,7 +534,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
],
@@ -555,7 +555,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Dev Tools'],
+ 'usecases' => ['dev-tools'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -597,7 +597,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -648,7 +648,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -729,7 +729,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Databases'],
+ 'usecases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -822,7 +822,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Messaging'],
+ 'usecases' => ['messaging'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -907,7 +907,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Messaging'],
+ 'usecases' => ['messaging'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -962,7 +962,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1044,7 +1044,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1085,7 +1085,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1142,7 +1142,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1175,7 +1175,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1208,7 +1208,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1266,7 +1266,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1324,7 +1324,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1382,7 +1382,7 @@ return [
'events' => ['databases.*.collections.*.documents.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1440,7 +1440,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1474,7 +1474,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1516,7 +1516,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1556,7 +1556,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1590,7 +1590,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1653,7 +1653,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1716,7 +1716,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1771,7 +1771,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1812,7 +1812,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1852,7 +1852,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1894,7 +1894,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['AI'],
+ 'usecases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1928,7 +1928,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1983,7 +1983,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['Utilities'],
+ 'usecases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
diff --git a/app/config/template-usecases.php b/app/config/template-usecases.php
new file mode 100644
index 0000000000..bc918e8691
--- /dev/null
+++ b/app/config/template-usecases.php
@@ -0,0 +1,10 @@
+desc('Get function templates')
->groups(['api', 'functions'])
- ->label('scope', 'functions.read')
+ ->label('scope', 'public')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getFunctionTemplates')
@@ -2368,13 +2368,22 @@ App::get('/v1/functions/templates')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
- ->param('limit', 10, new Range(0, 200), 'Limit the number of templates returned in the response. Max limit: 100.', true)
- ->param('offset', 0, new Range(0, 200), 'Offset the list of returned templates. Max offset: 100.', true)
+ ->param('usecases', [], new ArrayList(new WhiteList(Config::getParam('template-usecases')), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
+ ->param('limit', 100, new Range(0, 200), 'Limit the number of templates returned in the response. Max limit: 200.', true)
+ ->param('offset', 0, new Range(0, 200), 'Offset the list of returned templates. Max offset: 200.', true)
->inject('response')
- ->action(function (int $limit, int $offset, Response $response) {
- $templates = \array_slice(Config::getParam('function-templates', []), $offset, $limit);
+ ->action(function (array $usecases, int $limit, int $offset, Response $response) {
+ $templates = Config::getParam('function-templates', []);
+
+ if (!empty($usecases)) {
+ $templates = \array_filter($templates, function ($template) use ($usecases) {
+ return \count(\array_intersect($usecases, $template['usecases'])) > 0;
+ });
+ }
+
+ $responseTemplates = \array_slice($templates, $offset, $limit);
$response->dynamic(new Document([
- 'templates' => $templates,
- 'total' => \count($templates),
+ 'templates' => $responseTemplates,
+ 'total' => \count($responseTemplates),
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
});
diff --git a/app/init.php b/app/init.php
index e7a07a40d0..acf30ac452 100644
--- a/app/init.php
+++ b/app/init.php
@@ -304,6 +304,7 @@ Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
Config::load('function-templates', __DIR__ . '/config/function-templates.php'); // List of function templates
+Config::load('template-usecases', __DIR__ . '/config/template-usecases.php');
/**
* New DB Filters
From 0369e8913fdbdaf6e82dbc4a7cd57a1c94699761 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Mon, 29 Jul 2024 18:59:16 +0530
Subject: [PATCH 11/16] Add filter by runtimes
---
app/controllers/api/functions.php | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index 7e3f6e1475..b5b7a2a4f4 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2368,13 +2368,20 @@ App::get('/v1/functions/templates')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
+ ->param('runtimes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('runtimes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.', true)
->param('usecases', [], new ArrayList(new WhiteList(Config::getParam('template-usecases')), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
->param('limit', 100, new Range(0, 200), 'Limit the number of templates returned in the response. Max limit: 200.', true)
->param('offset', 0, new Range(0, 200), 'Offset the list of returned templates. Max offset: 200.', true)
->inject('response')
- ->action(function (array $usecases, int $limit, int $offset, Response $response) {
+ ->action(function (array $runtimes, array $usecases, int $limit, int $offset, Response $response) {
$templates = Config::getParam('function-templates', []);
+ if (!empty($runtimes)) {
+ $templates = \array_filter($templates, function ($template) use ($runtimes) {
+ return \count(\array_intersect($runtimes, \array_column($template['runtimes'], 'name'))) > 0;
+ });
+ }
+
if (!empty($usecases)) {
$templates = \array_filter($templates, function ($template) use ($usecases) {
return \count(\array_intersect($usecases, $template['usecases'])) > 0;
From df9f4ffb040cff4122de358a0bf72a2cee7ab675 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Mon, 29 Jul 2024 19:09:48 +0530
Subject: [PATCH 12/16] Add tests for filtering and pagination
---
.../Functions/FunctionsCustomClientTest.php | 39 +++++++++++++++++++
.../Functions/FunctionsCustomServerTest.php | 14 -------
2 files changed, 39 insertions(+), 14 deletions(-)
diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
index 4e501692fa..4be2d1d893 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
@@ -963,4 +963,43 @@ class FunctionsCustomClientTest extends Scope
return [];
}
+
+ public function testGetFunctionTemplates()
+ {
+ $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $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]);
+
+ $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'usecases' => ['starter', 'ai'],
+ 'runtimes' => ['bun-1.0', 'dart-2.16']
+ ]);
+
+ $this->assertEquals(200, $templates['headers']['status-code']);
+ $this->assertEquals(3, $templates['body']['total']);
+ $this->assertIsArray($templates['body']['templates']);
+ $this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
+
+ $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'limit' => 10,
+ 'offset' => 2,
+ ]);
+
+ $this->assertEquals(200, $templates['headers']['status-code']);
+ $this->assertEquals(10, $templates['body']['total']);
+ $this->assertIsArray($templates['body']['templates']);
+ $this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
+ }
}
diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
index bed86a1d72..c9f9e4443f 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
@@ -2097,18 +2097,4 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
}
-
- public function testGetFunctionTemplates()
- {
- $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-key' => $this->getProject()['apiKey'],
- ], $this->getHeaders()));
-
- $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]);
- }
}
From 198b460e227c195986d42c8ccd2351a128f9bd62 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 02:25:18 +0530
Subject: [PATCH 13/16] Improve tests
---
app/config/template-usecases.php | 10 ----------
app/controllers/api/functions.php | 14 ++++++--------
app/init.php | 3 +--
.../references/functions/get-function-templates.md | 1 -
.../functions/list-function-templates.md | 1 +
.../Functions/FunctionsCustomClientTest.php | 13 +++++++++++--
6 files changed, 19 insertions(+), 23 deletions(-)
delete mode 100644 app/config/template-usecases.php
delete mode 100644 docs/references/functions/get-function-templates.md
create mode 100644 docs/references/functions/list-function-templates.md
diff --git a/app/config/template-usecases.php b/app/config/template-usecases.php
deleted file mode 100644
index bc918e8691..0000000000
--- a/app/config/template-usecases.php
+++ /dev/null
@@ -1,10 +0,0 @@
-desc('Get function templates')
- ->groups(['api', 'functions'])
+ ->desc('List function templates')
->label('scope', 'public')
- ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
- ->label('sdk.method', 'getFunctionTemplates')
- ->label('sdk.description', '/docs/references/functions/get-function-templates.md')
+ ->label('sdk.method', 'listFunctionTemplates')
+ ->label('sdk.description', '/docs/references/functions/list-function-templates.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
->param('runtimes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('runtimes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.', true)
- ->param('usecases', [], new ArrayList(new WhiteList(Config::getParam('template-usecases')), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
- ->param('limit', 100, new Range(0, 200), 'Limit the number of templates returned in the response. Max limit: 200.', true)
- ->param('offset', 0, new Range(0, 200), 'Offset the list of returned templates. Max offset: 200.', true)
+ ->param('usecases', [], new ArrayList(new WhiteList(['dev-tools','starter','databases','ai','messaging','utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
+ ->param('limit', 100, new Range(0, 100), 'Limit the number of templates returned in the response. Max limit: 100.', true)
+ ->param('offset', 0, new Range(0, 100), 'Offset the list of returned templates. Max offset: 100.', true)
->inject('response')
->action(function (array $runtimes, array $usecases, int $limit, int $offset, Response $response) {
$templates = Config::getParam('function-templates', []);
diff --git a/app/init.php b/app/init.php
index acf30ac452..5ecd78cf00 100644
--- a/app/init.php
+++ b/app/init.php
@@ -303,8 +303,7 @@ Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
-Config::load('function-templates', __DIR__ . '/config/function-templates.php'); // List of function templates
-Config::load('template-usecases', __DIR__ . '/config/template-usecases.php');
+Config::load('function-templates', __DIR__ . '/config/function-templates.php');
/**
* New DB Filters
diff --git a/docs/references/functions/get-function-templates.md b/docs/references/functions/get-function-templates.md
deleted file mode 100644
index 8edc21e996..0000000000
--- a/docs/references/functions/get-function-templates.md
+++ /dev/null
@@ -1 +0,0 @@
-Get function templates from marketplace.
\ No newline at end of file
diff --git a/docs/references/functions/list-function-templates.md b/docs/references/functions/list-function-templates.md
new file mode 100644
index 0000000000..b6195b43c3
--- /dev/null
+++ b/docs/references/functions/list-function-templates.md
@@ -0,0 +1 @@
+List function templates from marketplace.
\ No newline at end of file
diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
index 4be2d1d893..87638f1b11 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
@@ -988,18 +988,27 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(3, $templates['body']['total']);
$this->assertIsArray($templates['body']['templates']);
$this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
+ $this->assertEquals('starter', $templates['body']['templates'][0]['usecases'][0]);
+ $this->assertEquals('ai', $templates['body']['templates'][1]['usecases'][0]);
+ $this->assertEquals('ai', $templates['body']['templates'][2]['usecases'][0]);
+ $this->assertContains('bun-1.0', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
+ $this->assertContains('dart-2.16', array_column($templates['body']['templates'][1]['runtimes'], 'name'));
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
- 'limit' => 10,
+ 'limit' => 5,
'offset' => 2,
+ 'usecases' => ['databases'],
+ 'runtimes' => ['node-16.0']
]);
$this->assertEquals(200, $templates['headers']['status-code']);
- $this->assertEquals(10, $templates['body']['total']);
+ $this->assertEquals(5, $templates['body']['total']);
$this->assertIsArray($templates['body']['templates']);
$this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
+ $this->assertEquals('databases', $templates['body']['templates'][0]['usecases'][0]);
+ $this->assertContains('node-16.0', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
}
}
From c9f8671af16add468bcacfb4978a65b55ba5583e Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:13:38 +0530
Subject: [PATCH 14/16] Small fixes
---
app/controllers/api/functions.php | 10 +++++-----
docs/references/functions/list-function-templates.md | 1 -
docs/references/functions/list-templates.md | 1 +
.../Utopia/Response/Model/TemplateFunction.php | 6 +++---
4 files changed, 9 insertions(+), 9 deletions(-)
delete mode 100644 docs/references/functions/list-function-templates.md
create mode 100644 docs/references/functions/list-templates.md
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index 79134eebbb..88e919730e 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2361,15 +2361,15 @@ App::get('/v1/functions/templates')
->desc('List function templates')
->label('scope', 'public')
->label('sdk.namespace', 'functions')
- ->label('sdk.method', 'listFunctionTemplates')
- ->label('sdk.description', '/docs/references/functions/list-function-templates.md')
+ ->label('sdk.method', 'listTemplates')
+ ->label('sdk.description', '/docs/references/functions/list-templates.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
->param('runtimes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('runtimes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.', true)
- ->param('usecases', [], new ArrayList(new WhiteList(['dev-tools','starter','databases','ai','messaging','utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
- ->param('limit', 100, new Range(0, 100), 'Limit the number of templates returned in the response. Max limit: 100.', true)
- ->param('offset', 0, new Range(0, 100), 'Offset the list of returned templates. Max offset: 100.', true)
+ ->param('useCases', [], new ArrayList(new WhiteList(['dev-tools','starter','databases','ai','messaging','utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
+ ->param('limit', 25, new Range(1, 5000), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.', true)
+ ->param('offset', 0, new Range(0, 5000), 'Offset the list of returned templates. Maximum offset is 5000.', true)
->inject('response')
->action(function (array $runtimes, array $usecases, int $limit, int $offset, Response $response) {
$templates = Config::getParam('function-templates', []);
diff --git a/docs/references/functions/list-function-templates.md b/docs/references/functions/list-function-templates.md
deleted file mode 100644
index b6195b43c3..0000000000
--- a/docs/references/functions/list-function-templates.md
+++ /dev/null
@@ -1 +0,0 @@
-List function templates from marketplace.
\ No newline at end of file
diff --git a/docs/references/functions/list-templates.md b/docs/references/functions/list-templates.md
new file mode 100644
index 0000000000..ed43b9cbf4
--- /dev/null
+++ b/docs/references/functions/list-templates.md
@@ -0,0 +1 @@
+List available function templates. You can use template details in [createFunction](/docs/references/cloud/server-nodejs/functions#create) method.
\ No newline at end of file
diff --git a/src/Appwrite/Utopia/Response/Model/TemplateFunction.php b/src/Appwrite/Utopia/Response/Model/TemplateFunction.php
index 5fdbd95a35..7a3a4cfbd5 100644
--- a/src/Appwrite/Utopia/Response/Model/TemplateFunction.php
+++ b/src/Appwrite/Utopia/Response/Model/TemplateFunction.php
@@ -16,7 +16,7 @@ class TemplateFunction extends Model
'default' => '',
'example' => 'icon-lightning-bolt',
])
- ->addRule('$id', [
+ ->addRule('id', [
'type' => self::TYPE_STRING,
'description' => 'Function Template ID.',
'default' => '',
@@ -60,9 +60,9 @@ class TemplateFunction extends Model
'default' => 15,
'example' => 300,
])
- ->addRule('usecases', [
+ ->addRule('useCases', [
'type' => self::TYPE_STRING,
- 'description' => 'Function usecases.',
+ 'description' => 'Function use cases.',
'default' => [],
'example' => 'Starter',
'array' => true,
From 939adfd09bff8992e46ff80b900792eb761dcacb Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:11:04 +0530
Subject: [PATCH 15/16] Improve tests
---
app/config/function-templates.php | 78 +++++++++----------
app/controllers/api/functions.php | 4 +-
.../Functions/FunctionsCustomClientTest.php | 71 +++++++++++++++--
3 files changed, 104 insertions(+), 49 deletions(-)
diff --git a/app/config/function-templates.php b/app/config/function-templates.php
index c8562648be..dddd8596f4 100644
--- a/app/config/function-templates.php
+++ b/app/config/function-templates.php
@@ -56,7 +56,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['starter'],
+ 'useCases' => ['starter'],
'runtimes' => [
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/starter'),
...getRuntimes(
@@ -93,7 +93,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -136,7 +136,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -178,7 +178,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -229,7 +229,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -265,7 +265,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -331,7 +331,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -391,7 +391,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['messaging'],
+ 'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -453,7 +453,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -488,7 +488,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -534,7 +534,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
],
@@ -555,7 +555,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['dev-tools'],
+ 'useCases' => ['dev-tools'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -597,7 +597,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -648,7 +648,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -729,7 +729,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -822,7 +822,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['messaging'],
+ 'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -907,7 +907,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['messaging'],
+ 'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -962,7 +962,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1044,7 +1044,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1085,7 +1085,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1142,7 +1142,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1175,7 +1175,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1208,7 +1208,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1266,7 +1266,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1324,7 +1324,7 @@ return [
'events' => ['buckets.*.files.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1382,7 +1382,7 @@ return [
'events' => ['databases.*.collections.*.documents.*.create'],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1440,7 +1440,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1474,7 +1474,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1516,7 +1516,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1556,7 +1556,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1590,7 +1590,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1653,7 +1653,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 30,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1716,7 +1716,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1771,7 +1771,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1812,7 +1812,7 @@ return [
'cron' => '',
'events' => [],
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1852,7 +1852,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1894,7 +1894,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 300,
- 'usecases' => ['ai'],
+ 'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1928,7 +1928,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
@@ -1983,7 +1983,7 @@ return [
'events' => [],
'cron' => '',
'timeout' => 15,
- 'usecases' => ['utilities'],
+ 'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
TEMPLATE_RUNTIMES['NODE'],
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index 88e919730e..558b123f65 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2367,7 +2367,7 @@ App::get('/v1/functions/templates')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
->param('runtimes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('runtimes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.', true)
- ->param('useCases', [], new ArrayList(new WhiteList(['dev-tools','starter','databases','ai','messaging','utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of usecases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' usecases are allowed.', true)
+ ->param('useCases', [], new ArrayList(new WhiteList(['dev-tools','starter','databases','ai','messaging','utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of use cases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' use cases are allowed.', true)
->param('limit', 25, new Range(1, 5000), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.', true)
->param('offset', 0, new Range(0, 5000), 'Offset the list of returned templates. Maximum offset is 5000.', true)
->inject('response')
@@ -2382,7 +2382,7 @@ App::get('/v1/functions/templates')
if (!empty($usecases)) {
$templates = \array_filter($templates, function ($template) use ($usecases) {
- return \count(\array_intersect($usecases, $template['usecases'])) > 0;
+ return \count(\array_intersect($usecases, $template['useCases'])) > 0;
});
}
diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
index 87638f1b11..61c8afd9bd 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
@@ -8,6 +8,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
+use Utopia\Config\Config;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -966,6 +967,10 @@ class FunctionsCustomClientTest extends Scope
public function testGetFunctionTemplates()
{
+ /**
+ * Test for SUCCESS
+ */
+ $expectedTemplates = array_slice(Config::getParam('function-templates', []), 0, 25);
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -975,24 +980,47 @@ class FunctionsCustomClientTest extends Scope
$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++) {
+ $this->assertEquals($expectedTemplates[$i]['name'], $templates['body']['templates'][$i]['name']);
+ $this->assertEquals($expectedTemplates[$i]['id'], $templates['body']['templates'][$i]['id']);
+ $this->assertEquals($expectedTemplates[$i]['icon'], $templates['body']['templates'][$i]['icon']);
+ $this->assertEquals($expectedTemplates[$i]['tagline'], $templates['body']['templates'][$i]['tagline']);
+ $this->assertEquals($expectedTemplates[$i]['useCases'], $templates['body']['templates'][$i]['useCases']);
+ $this->assertEquals($expectedTemplates[$i]['vcsProvider'], $templates['body']['templates'][$i]['vcsProvider']);
+ $this->assertEquals($expectedTemplates[$i]['runtimes'], $templates['body']['templates'][$i]['runtimes']);
+ $this->assertEquals($expectedTemplates[$i]['variables'], $templates['body']['templates'][$i]['variables']);
+ }
+
+ $templates_offset = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'limit' => 1,
+ 'offset' => 2
+ ]);
+
+ $this->assertEquals(200, $templates_offset['headers']['status-code']);
+ $this->assertEquals(1, $templates_offset['body']['total']);
+ // assert that offset works as expected
+ $this->assertEquals($templates['body']['templates'][2]['id'], $templates_offset['body']['templates'][0]['id']);
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
- 'usecases' => ['starter', 'ai'],
+ 'useCases' => ['starter', 'ai'],
'runtimes' => ['bun-1.0', 'dart-2.16']
]);
$this->assertEquals(200, $templates['headers']['status-code']);
- $this->assertEquals(3, $templates['body']['total']);
+ $this->assertGreaterThanOrEqual(3, $templates['body']['total']);
$this->assertIsArray($templates['body']['templates']);
+ foreach ($templates['body']['templates'] as $template) {
+ $this->assertContains($template['useCases'][0], ['starter', 'ai']);
+ }
$this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
- $this->assertEquals('starter', $templates['body']['templates'][0]['usecases'][0]);
- $this->assertEquals('ai', $templates['body']['templates'][1]['usecases'][0]);
- $this->assertEquals('ai', $templates['body']['templates'][2]['usecases'][0]);
$this->assertContains('bun-1.0', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
- $this->assertContains('dart-2.16', array_column($templates['body']['templates'][1]['runtimes'], 'name'));
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
@@ -1000,7 +1028,7 @@ class FunctionsCustomClientTest extends Scope
], $this->getHeaders()), [
'limit' => 5,
'offset' => 2,
- 'usecases' => ['databases'],
+ 'useCases' => ['databases'],
'runtimes' => ['node-16.0']
]);
@@ -1008,7 +1036,34 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(5, $templates['body']['total']);
$this->assertIsArray($templates['body']['templates']);
$this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
- $this->assertEquals('databases', $templates['body']['templates'][0]['usecases'][0]);
+ foreach ($templates['body']['templates'] as $template) {
+ $this->assertContains($template['useCases'][0], ['databases']);
+ }
$this->assertContains('node-16.0', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
+
+ /**
+ * Test for FAILURE
+ */
+ $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'limit' => 5001,
+ 'offset' => 10,
+ ]);
+
+ $this->assertEquals(400, $templates['headers']['status-code']);
+ $this->assertEquals('Invalid `limit` param: Value must be a valid range between 1 and 5,000', $templates['body']['message']);
+
+ $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'limit' => 5,
+ 'offset' => 5001,
+ ]);
+
+ $this->assertEquals(400, $templates['headers']['status-code']);
+ $this->assertEquals('Invalid `offset` param: Value must be a valid range between 0 and 5,000', $templates['body']['message']);
}
}
From 395f43171890675ceed593738d9d3177d0819028 Mon Sep 17 00:00:00 2001
From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:16:45 +0530
Subject: [PATCH 16/16] Add endpoint to get template by ID
---
app/config/errors.php | 5 +++
app/controllers/api/functions.php | 25 +++++++++++++
docs/references/functions/get-template.md | 1 +
src/Appwrite/Extend/Exception.php | 1 +
.../Functions/FunctionsCustomClientTest.php | 35 ++++++++++++++++---
5 files changed, 62 insertions(+), 5 deletions(-)
create mode 100644 docs/references/functions/get-template.md
diff --git a/app/config/errors.php b/app/config/errors.php
index 6337c3205a..dc6dcd5daf 100644
--- a/app/config/errors.php
+++ b/app/config/errors.php
@@ -529,6 +529,11 @@ return [
'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.',
'code' => 408,
],
+ Exception::FUNCTION_TEMPLATE_NOT_FOUND => [
+ 'name' => Exception::FUNCTION_TEMPLATE_NOT_FOUND,
+ 'description' => 'Function Template with the requested ID could not be found.',
+ 'code' => 404,
+ ],
/** Builds */
Exception::BUILD_NOT_FOUND => [
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index 558b123f65..a61db99bdf 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -2392,3 +2392,28 @@ App::get('/v1/functions/templates')
'total' => \count($responseTemplates),
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
});
+
+App::get('/v1/functions/templates/:templateId')
+ ->desc('Get function template')
+ ->label('scope', 'public')
+ ->label('sdk.namespace', 'functions')
+ ->label('sdk.method', 'getTemplate')
+ ->label('sdk.description', '/docs/references/functions/get-template.md')
+ ->label('sdk.response.code', Response::STATUS_CODE_OK)
+ ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
+ ->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION)
+ ->param('templateId', '', new Text(128), 'Template ID.')
+ ->inject('response')
+ ->action(function (string $templateId, Response $response) {
+ $templates = Config::getParam('function-templates', []);
+
+ $template = array_shift(\array_filter($templates, function ($template) use ($templateId) {
+ return $template['id'] === $templateId;
+ }));
+
+ if (empty($template)) {
+ throw new Exception(Exception::FUNCTION_TEMPLATE_NOT_FOUND);
+ }
+
+ $response->dynamic(new Document($template), Response::MODEL_TEMPLATE_FUNCTION);
+ });
diff --git a/docs/references/functions/get-template.md b/docs/references/functions/get-template.md
new file mode 100644
index 0000000000..ccdcce7352
--- /dev/null
+++ b/docs/references/functions/get-template.md
@@ -0,0 +1 @@
+Get a function template using ID. You can use template details in [createFunction](/docs/references/cloud/server-nodejs/functions#create) method.
\ No newline at end of file
diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php
index dbc7d9425e..884296ff67 100644
--- a/src/Appwrite/Extend/Exception.php
+++ b/src/Appwrite/Extend/Exception.php
@@ -156,6 +156,7 @@ class Exception extends \Exception
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing';
public const FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout';
+ public const FUNCTION_TEMPLATE_NOT_FOUND = 'function_template_not_found';
/** Deployments */
public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
index 61c8afd9bd..f94cb3744a 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
@@ -965,7 +965,7 @@ class FunctionsCustomClientTest extends Scope
return [];
}
- public function testGetFunctionTemplates()
+ public function testListTemplates()
{
/**
* Test for SUCCESS
@@ -973,7 +973,6 @@ class FunctionsCustomClientTest extends Scope
$expectedTemplates = array_slice(Config::getParam('function-templates', []), 0, 25);
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $templates['headers']['status-code']);
@@ -994,7 +993,6 @@ class FunctionsCustomClientTest extends Scope
$templates_offset = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'offset' => 2
@@ -1007,7 +1005,6 @@ class FunctionsCustomClientTest extends Scope
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'useCases' => ['starter', 'ai'],
'runtimes' => ['bun-1.0', 'dart-2.16']
@@ -1046,7 +1043,6 @@ class FunctionsCustomClientTest extends Scope
*/
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 5001,
'offset' => 10,
@@ -1066,4 +1062,33 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(400, $templates['headers']['status-code']);
$this->assertEquals('Invalid `offset` param: Value must be a valid range between 0 and 5,000', $templates['body']['message']);
}
+
+ public function testGetTemplate()
+ {
+ /**
+ * Test for SUCCESS
+ */
+ $template = $this->client->call(Client::METHOD_GET, '/functions/templates/query-neo4j-auradb', array_merge([
+ 'content-type' => 'application/json',
+ ], $this->getHeaders()), []);
+
+ $this->assertEquals(200, $template['headers']['status-code']);
+ $this->assertIsArray($template['body']);
+ $this->assertEquals('query-neo4j-auradb', $template['body']['id']);
+ $this->assertEquals('Query Neo4j AuraDB', $template['body']['name']);
+ $this->assertEquals('icon-neo4j', $template['body']['icon']);
+ $this->assertEquals('Graph database with focus on relations between data.', $template['body']['tagline']);
+ $this->assertEquals(['databases'], $template['body']['useCases']);
+ $this->assertEquals('github', $template['body']['vcsProvider']);
+
+ /**
+ * Test for FAILURE
+ */
+ $template = $this->client->call(Client::METHOD_GET, '/functions/templates/invalid-template-id', array_merge([
+ 'content-type' => 'application/json',
+ ], $this->getHeaders()), []);
+
+ $this->assertEquals(404, $template['headers']['status-code']);
+ $this->assertEquals('Function Template with the requested ID could not be found.', $template['body']['message']);
+ }
}