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']); + } }