From a0ebf266a8ef920bb9e3555c99fe3c2e549803c1 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 1 Jun 2023 19:42:24 +0000 Subject: [PATCH 01/80] prepare index for Function G4 --- app/views/docs/index.phtml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 63f2d6166..63da00806 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -65,7 +65,17 @@ $cols = [
  • Teams
  • Databases
  • Storage
  • -
  • Functions
  • +
  • + Functions + + +
  • Localization
  • Avatars
  • Health
  • From 3ee6a1f20b7f40bc5e63195915a9a96fa0af0e15 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 1 Jun 2023 19:56:41 +0000 Subject: [PATCH 02/80] Fix reference index, change guide index --- app/views/docs/index.phtml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 63da00806..67be0c3ad 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -65,17 +65,7 @@ $cols = [
  • Teams
  • Databases
  • Storage
  • -
  • - Functions - - -
  • +
  • Functions
  • Localization
  • Avatars
  • Health
  • @@ -102,7 +92,17 @@ $cols = [
  •   Security
  • -
  • Functions
  • +
  • + +
  •    Deploy
  • +
  •    Execute
  • +
  •    Syntax
  • +
  •    Runtimes
  • +
  •    Logging and Debugging
  • + +
    From d491591268e57ac6b8df384f237e4ff2d470c251 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 1 Jun 2023 20:00:05 +0000 Subject: [PATCH 03/80] Change debugging and logging to just debugging --- app/views/docs/index.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 67be0c3ad..7d9bb0b45 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -93,14 +93,14 @@ $cols = [
  • - Functions
  • From 4537a2b71a802db213009df5b741a45d2f374aed Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 17 Jul 2023 21:26:44 +0000 Subject: [PATCH 04/80] Init overview page --- app/views/docs/functions.phtml | 988 +++------------------------------ 1 file changed, 71 insertions(+), 917 deletions(-) diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 43fdedb12..0bdcb3d7e 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -7,920 +7,74 @@ $runtimes = $this->getParam('runtimes', []); ?> -

    Appwrite Functions allow you to extend and customize your Appwrite server functionality by executing your custom code. Appwrite can execute your custom code in response to any Appwrite system event like account creation, user login, or document update. You can also schedule your functions or start them manually by triggering your function from an HTTP endpoint using the Appwrite client or server APIs.

    - -

    Appwrite Functions run in a secure, isolated Docker container. By default, Appwrite supports multiple runtimes for different languages that you can use to run your code.

    - -

    Getting Started

    - -

    The quickest way to get started with Appwrite Functions is using the Appwrite CLI. The CLI comes with starter code and some simple commands to quickly create and deploy your functions. Once you have the CLI installed and setup with an Appwrite project, create your first function using:

    - -
    -
    appwrite init function
    -
    - -

    Give your function a name and choose your runtime. This will create a new starter function in the current directory and also add it to your appwrite.json file. Go ahead and deploy your function using :

    - -
    -
    appwrite deploy function
    -
    - -

    You can now head over to your Appwrite Dashboard, navigate to the Function settings and execute your function. You can find the status of your execution under the Logs tab.

    - -

    Feel free to modify and play around with the starter code and use the appwrite deploy command to instantly deploy your changes to the Appwrite server.

    - -

    The following sections will dive deeper into some more terminology and advanced concepts which can be useful when writing your function from scratch.

    - -

    Writing your own Function

    - -

    When writing your own Appwrite Function, you must export the code in certain ways depending on the language. This varies between languages so refer to the examples below.

    - -
      -
    • -

      Node.js

      -
      -
      -
      module.exports = async (req, res) => {
      -  const payload =
      -    req.payload ||
      -    'No payload provided. Add custom data when executing function.';
      -
      -  const secretKey =
      -    req.variables.SECRET_KEY ||
      -    'SECRET_KEY variable not found. You can set it in Function settings.';
      -
      -  const randomNumber = Math.random();
      -
      -  const trigger = req.variables.APPWRITE_FUNCTION_TRIGGER;
      -
      -  res.json({
      -    message: 'Hello from Appwrite!',
      -    payload,
      -    secretKey,
      -    randomNumber,
      -    trigger,
      -  });
      -};
      -
      -

      Installing Dependencies

      -

      Include a package.json file along with your function, and Appwrite handles the rest! The best practice is to make sure that the node_modules folder is not a part of your tarball.

      -
      -
    • -
    • -

      PHP

      -
      -
      -
      <?php
      -
      -return function ($req, $res) {
      -  $payload =
      -    $req['payload'] ?:
      -    'No payload provided. Add custom data when executing function.';
      -
      -  $secretKey =
      -    $req['variables']['SECRET_KEY'] ?:
      -    'SECRET_KEY variable not found. You can set it in Function settings.';
      -
      -  $randomNumber = \mt_rand() / \mt_getrandmax();
      -
      -  $trigger = $req['variables']['APPWRITE_FUNCTION_TRIGGER'];
      -
      -  $res->json([
      -    'message' => 'Hello from Appwrite!',
      -    'payload' => $payload,
      -    'secretKey' => $secretKey,
      -    'randomNumber' => $randomNumber,
      -    'trigger' => $trigger,
      -  ]);
      -};
      -
      -

      Installing Dependencies

      -

      Include a composer.json file along with your function, make sure to require autoload.php from vendor folder, and Appwrite handles the rest!. The best practice is to make sure that the vendor directory is not a part of your tarball.

      -
      -
    • -
    • -

      Python

      -
      -
      -
      import random
      -
      -def main(req, res):
      -  payload = req.payload or 'No payload provided. Add custom data when executing function.'
      -
      -  secretKey = req.variables.get(
      -    'SECRET_KEY',
      -    'SECRET_KEY variable not found. You can set it in Function settings.'
      -  )
      -
      -  randomNumber = random.random()
      -
      -  trigger = req.variables['APPWRITE_FUNCTION_TRIGGER']
      -
      -  return res.json({
      -    'message': 'Hello from Appwrite!',
      -    'payload': payload,
      -    'secretKey': secretKey,
      -    'randomNumber': randomNumber,
      -    'trigger': trigger,
      -  })
      -
      -

      Installing Dependencies

      -

      Include a requirements.txt file with your function, Appwrite handles the rest!

      -
      -
    • -
    • -

      Ruby

      -
      -
      -
      def main(req, res)
      -  payload =
      -    !req.payload.empty? ? req.payload :
      -    'No payload provided. Add custom data when executing function.'
      -
      -  secretKey =
      -    req.variables['SECRET_KEY'] ||
      -    'SECRET_KEY variable not found. You can set it in Function settings.'
      -
      -  randomNumber = rand()
      -
      -  trigger = req.variables['APPWRITE_FUNCTION_TRIGGER']
      -
      -  return res.json({
      -    :message => 'Hello from Appwrite!',
      -    :payload => payload,
      -    :secretKey => secretKey,
      -    :randomNumber => randomNumber,
      -    :trigger => trigger,
      -  })
      -end
      -
      -

      Installing Dependencies

      -

      Include a Gemfile with your function, Appwrite handles the rest!

      -
      -
    • -
    • -

      Deno

      -
      -
      -
      export default async function (req: any, res: any) {
      -  const payload =
      -    req.payload ||
      -    'No payload provided. Add custom data when executing function.';
      -
      -  const secretKey =
      -    req.variables.SECRET_KEY ||
      -    'SECRET_KEY variable not found. You can set it in Function settings.';
      -
      -  const randomNumber = Math.random();
      -
      -  const trigger = req.variables.APPWRITE_FUNCTION_TRIGGER;
      -
      -  res.json({
      -    message: 'Hello from Appwrite!',
      -    payload,
      -    secretKey,
      -    randomNumber,
      -    trigger,
      -  });
      -};
      -
      -

      Installing Dependencies

      -

      No special steps are required for Deno, Appwrite handles everything!

      -
      -
    • -
    • -

      Dart

      -
      -
      -
      import 'dart:math';
      -import 'dart:async';
      -
      -Future <void> start(final req, final res) async {
      -  final payload =
      -    !req.payload?.isEmpty ? req.payload :
      -    'No payload provided. Add custom data when executing function.';
      -
      -  final secretKey =
      -    req.variables['SECRET_KEY'] ??
      -    'SECRET_KEY variable not found. You can set it in Function settings.';
      -
      -  final randomNumber = new Random().nextDouble();
      -
      -  final trigger = req.variables['APPWRITE_FUNCTION_TRIGGER'];
      -
      -  res.json({
      -    'message': 'Hello from Appwrite!',
      -    'payload': payload,
      -    'secretKey': secretKey,
      -    'randomNumber': randomNumber,
      -    'trigger': trigger,
      -  });
      -}
      -
      -

      Installing Dependencies

      -

      Include a pubspec.yaml file with your function- Appwrite handles the rest!

      -
      -
    • -
    • -

      Swift

      -
      -
      -
      func main(req: RequestValue, res: RequestResponse) throws -> RequestResponse {
      -    let payload = req.payload.isEmpty 
      -        ? "No payload provided. Add custom data when executing function" 
      -        : req.payload
      -    
      -    let secretKey = req.variables["SECRET_KEY"] 
      -        ?? "SECRET_KEY variable not found. You can set it in Function settings."
      -
      -    let randomNumber = Double.random(in: 0...1)
      -
      -    let trigger = req.variables["APPWRITE_FUNCTION_TRIGGER"]
      -
      -    return res.json(data: [
      -        "message": "Hello from Appwrite!",
      -        "payload": payload,
      -        "secretKey": secretKey,
      -        "randomNumber": randomNumber,
      -        "trigger": trigger,
      -    ])
      -}
      -
      -

      With Swift, your entrypoint can be empty since Appwrite automatically infers it from the location of your main() function. Just ensure that your cloud function has a single declaration of main() across your source files.

      -

      Installing Dependencies

      -

      Include a Package.swift file with your function, Appwrite handles the rest!

      -
      -
    • -
    • -

      .NET

      -
      -
      -
      public async Task Main(RuntimeRequest req, RuntimeResponse res)
      -{
      -  var payload = (string.IsNullOrEmpty(req.Payload))
      -                ? "No payload provided. Add custom data when executing function."
      -                : req.Payload; 
      -
      -  var secretKey = req.Variables.ContainsKey("SECRET_KEY")
      -                  ? req.Variables["SECRET_KEY"]
      -                  : "SECRET_KEY variable not found. You can set it in Function settings.";
      -
      -  var randomNumber = new Random().NextDouble();
      -
      -  var trigger = req.Variables["APPWRITE_FUNCTION_TRIGGER"];
      -
      -  return res.Json(new() 
      -  {
      -    { "message", "Hello from Appwrite!" },
      -    { "payload", payload },
      -    { "secretKey", secretKey },
      -    { "randomNumber", randomNumber },
      -    { "trigger", trigger },
      -  });
      -}
      -
      -

      Installing Dependencies

      -

      Include a Function.csproj file with your function, Appwrite handles the rest!

      -
      -
    • -
    • -

      Kotlin

      -
      -
      -
      import kotlin.random.Random
      -
      -@Throws(Exception::class)
      -fun main(req: RuntimeRequest, res: RuntimeResponse): RuntimeResponse {
      -
      -    val payload = if (req.payload.isEmpty()) "No payload provided. Add custom data when executing function." else req.payload
      -
      -    val secretKey = req.variables["SECRET_KEY"] ?: "SECRET_KEY variable not found. You can set it in Function settings."
      -    
      -    val randomNumber = Random.nextDouble(0.0, 1.0)
      -
      -    val trigger = req.variables["APPWRITE_FUNCTION_TRIGGER"]    
      -
      -    return res.json(mapOf(
      -        "message" to "Hello from Appwrite!",
      -        "payload" to payload,
      -        "secretKey" to secretKey,
      -        "randomNumber" to randomNumber,
      -        "trigger" to trigger
      -    ))
      -}
      -
      -

      Installing Dependencies

      -

      Include a deps.gradle file with your function, Appwrite handles the rest!

      -
      -
    • -
    • -

      Java

      -
      -
      -
      import java.util.Map;
      -import java.util.HashMap;
      -
      -public RuntimeResponse main(RuntimeRequest req, RuntimeResponse res) throws Exception {
      -
      -    String payload = (req.getPayload().isEmpty())
      -                     ? "No payload provided. Add custom data when executing function."
      -                     : req.getPayload();
      -
      -    Map variables = req.getVariables();
      -
      -    String secretKey = variables.containsKey("SECRET_KEY")
      -                       ? variables.get("SECRET_KEY")
      -                       : "SECRET_KEY variable not found. You can set it in Function settings.";
      -
      -    double randomNumber = Math.random();
      -
      -    String trigger = variables.get("APPWRITE_FUNCTION_TRIGGER");
      -
      -    Map response = new HashMap();
      -        response.put("message", "Hello from Appwrite!");
      -        response.put("payload", payload);
      -        response.put("secretKey", secretKey);
      -        response.put("randomNumber", randomNumber);
      -        response.put("trigger", trigger);
      -
      -    return res.json(response);
      -}
      -
      -

      Installing Dependencies

      -

      Include a deps.gradle file with your function, Appwrite handles the rest!

      -
      -
    • -
    • -

      C++

      -
      -
      -
      #include <iostream>
      -#include <string>
      -
      -static RuntimeResponse &main(const RuntimeRequest &req, RuntimeResponse &res) {
      -
      -    std::string payload = req.payload.empty() ? 
      -                          "No payload provided. Add custom data when executing function." : 
      -                          req.payload;
      -
      -    std::string secretKey = req.variables.get("SECRET_KEY", "SECRET_KEY variable not found. You can set it in Function settings.").asString();
      -
      -    double randomNumber = ((double) rand() / (RAND_MAX));
      -
      -    std::string trigger = req.variables["APPWRITE_FUNCTION_TRIGGER"].asString();
      -
      -    Json::Value response;
      -    response["message"] = "Hello from Appwrite!";
      -    response["payload"] = payload;
      -    response["secretKey"] = secretKey;
      -    response["randomNumber"] = randomNumber;
      -    response["trigger"] = trigger;
      -    
      -    return res.json(response);
      -}
      -
      -

      Installing Dependencies

      -

      Include a CMakeLists.txt file with your function, Appwrite handles the rest!

      -
      -
    • -
    - - -

    When your function is called, you receive two parameters, a request and a response object. The request object contains all data that was sent to the function including function variables. A schema of the request object can be found below and is the same for all runtimes.

    - - - - - - - - - - - - - - - - - - - - - - -
    PropertyDescription
    headersAn object containing all the request headers.
    payloadA JSON string containing the data when you created the execution.
    variablesAn object containing all the function variables. This includes variables automatically added by Appwrite.
    - -

    The response object has two functions, send() and json() that can be used to send data back to the client. The types and implementation of these functions vary depending on runtime due to all languages being slightly different. You can check out implementation in the specific languages to learn more about them. The schema of the response object can be found below:

    - - - - - - - - - - - - - - - - - - -
    FunctionDescription
    send(text, status)Function to return a text response. Status code defaults to 200
    json(obj, status)Function to return a JSON response. Status code defaults to 200
    - -

    Create your Function

    - -

    Before you can deploy your function, you will need to create a new function from your Appwrite project's dashboard. Access the Function settings from your project's left navigation panel. Click the 'Add Function' button and choose a function name and runtime. In your Functions settings page, you can also set your function event triggers, CRON schedule, and set secure function variables for your function.

    - -

    Deploy Your Function

    - -

    Once you've written your function, you can now deploy it using the Appwrite CLI, the Appwrite Server API or manually from the Appwrite console.

    - -
      -
    • -

      Unix

      - -
      -
      appwrite functions createDeployment \
      -    --functionId=6012cc93d5a7b \
      -    --activate=true \
      -    --entrypoint="index.js" \
      -    --code="."
      -
      -
    • -
    • -

      CMD

      - -
      -
      appwrite functions createDeployment ^
      -    --functionId=6012cc93d5a7b ^
      -    --activate=true ^
      -    --entrypoint="index.js" ^
      -    --code="."
      -
      -
    • -
    • -

      PowerShell

      - -
      -
      appwrite functions createDeployment `
      -    --functionId=6012cc93d5a7b `
      -    --activate=true `
      -    --entrypoint="index.js" `
      -    --code="."
      -
      -
    • -
    - -

    The command above accepts three parameters:

    - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    functionIdThe ID of the Function you created in the previous step. You can find your function ID on your function page in your project dashboard.
    entrypointThe file name of your custom code that is executed when the function is triggered.
    codePath to your function tarball. When used with the Appwrite CLI, simply pass the path to your code directory, and the CLI will automatically package your code.
    - -

    You can also create new code deployments using the Appwrite server API

    - -

    Manual Deployment

    -

    You can also upload your functions to be deployed using the Appwrite console. The example below shows a simple Node.JS function, but the same idea applies to any other language.

    - -
    -
    .
    -├── package.json
    -└── index.js
    -
    -
    - -

    First, navigate inside the folder that contains your dependency file. Package your code files into the .tar.gz format with this tar command:

    - -
      -
    • -

      Unix

      - -
      -
      tar -czf code.tar.gz --exclude code.tar.gz .
      -
      -
    • -
    • -

      CMD

      - -
      -
      tar -czf code.tar.gz --exclude code.tar.gz .
      -
      -
    • -
    • -

      PowerShell

      - -
      -
      tar -czf code.tar.gz --exclude code.tar.gz .
      -
      -
    • -
    - -

    Next, navigate to your Appwrite console and upload the function.

    - -
      -
    1. Navigate to the function you want to deploy.
    2. -
    3. Click Create deployment.
    4. -
    5. Select the Manual tab.
    6. -
    7. Input the entry point of your function under Entrypoint. For the example above, it would be index.js.
    8. -
    9. Upload code.tar.gz.
    10. -
    11. Select Activate deployment after build to use your new function.
    12. -
    13. Click Create to deploy your function.
    14. -
    - - -

    Builds

    -

    Deployments needs to be built before they can be activated. This is automatically done after creating a deployment and the time taken can vary depending on the runtime.

    - -

    If a build fails for any reason, the deployment's status is set to failed and you won't be able to activate it. You can however retry a build if you think it was caused by an external factor using the Retry Button on the Appwrite Dashboard or Retry Build endpoint with the buildId from the deployment.

    - -

    To find more details about a deployment and reasons for its failure, you can use the Get Deployment endpoint using the deploymentId.

    - -

    Deployments that have been built successfully are marked as ready and can be activated and executed.

    - -
    -

    Build Times

    -

    Compiled runtimes such as Rust and Swift take much longer to build however yield better performance over their interpreted counterparts such as Node.

    -
    - -

    Execute

    - -

    Besides setting a schedule or allowing your function to listen to Appwrite's system events, you can also manually execute your cloud functions from your Appwrite console or API.

    - -setParam('srcLight', '/images-ee/docs/functions-light.png') - ->setParam('srcDark', '/images-ee/docs/functions-dark.png') - ->setParam('alt', 'Function settings page.') - ->setParam('description', 'Function settings page.') - ->render(); -?> - -

    To execute a function from the Appwrite console, click the Execute Now button on your function's overview page. To execute a function from the API, send a POST request to the function execution endpoint.

    - -

    The function execution endpoint is available from both Appwrite client and server APIs. To execute your function from the server API, you need an API key with 'execution.write' scope.

    - -

    Executing the function from the client API requires the current user to have execution permission for the function. You can change the execution permission from the function's settings page in the Appwrite console, by default no user, team, or role has this permission.

    - -
      -
    • -

      Web

      -
      -
      import { Client, Functions } from "appwrite";
      -
      -const client = new Client()
      -    .setEndpoint('https://cloud.appwrite.io/v1')
      -    .setProject('[PROJECT_ID]');
      -
      -const functions = new Functions(client);
      -
      -let promise = functions.createExecution('[FUNCTION_ID]');
      -
      -promise.then(function (response) {
      -    console.log(response); // Success
      -}, function (error) {
      -    console.log(error); // Failure
      -});
      -
      -
    • -
    • -

      Flutter

      - -
      -
      import 'package:appwrite/appwrite.dart';
      -
      -void main() async {
      -    final client = Client()
      -        .setEndpoint('https://cloud.appwrite.io/v1')
      -        .setProject('[PROJECT_ID]');
      -
      -    final functions = Functions(client);
      -
      -    final execution = await functions.createExecution(
      -        functionId: '[FUNCTION_ID]'
      -    );
      -}
      -
      -
    • -
    • -

      Android

      - -
      -
      import io.appwrite.Client
      -import io.appwrite.services.Functions
      -
      -suspend fun main() {
      -    val client = Client(applicationContext)
      -        .setEndpoint("https://cloud.appwrite.io/v1")
      -        .setProject("[PROJECT_ID]")
      -
      -    val functions = Functions(client)
      -
      -    val execution = functions.createExecution(
      -        functionId = "[FUNCTION_ID]"
      -    )
      -}
      -
      -
    • -
    • -

      Apple

      - -
      -
      import Appwrite
      -
      -func main() async throws {
      -    let client = Client()
      -      .setEndpoint("https://cloud.appwrite.io/v1")
      -      .setProject("[PROJECT_ID]")
      -
      -    let functions = Functions(client)
      -
      -    let execution = try await functions.createExecution(
      -        functionId: "[FUNCTION_ID]"
      -    )
      -}
      -
      -
    • -
    • -

      GraphQL

      - -
      -
      mutation {
      -    functionsCreateExecution(functionId: "[FUNCTION_ID]") {
      -        _id
      -        statusCode
      -        response
      -        stdout
      -        stderr
      -        duration
      -    }
      -}
      -
      -
    • -
    - -

    Scheduled Execution

    -

    Appwrite supports scheduled function executions. You can schedule executions using cron expressions in the settings of your function. Cron supports recurring executions as frequently as every minute.

    - -

    Here are some cron expressions for common intervals.

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Cron ExpressionSchedule
    */15 * * * *Every 15 minutes
    0 * * * *Every Hour
    0 0 * * *Every day at 00:00
    0 0 * * 1Every monday at 00:00
    - -

    Abuse and Limits

    - -

    Appwrite Functions can be executed using Client or Server SDKs. Client SDKs must be authenticated with an account that has been granted execution permissions on the function's settings page. Server SDKs require an API key with the correct scopes.

    - -

    The Functions Service APIs are rate limited to 60 calls per minute per account when using a Client SDK. Learn more about rate limiting. The response size of a Cloud Function is limited to 1MB. Responses larger than 1MB should be handled using Appwrite's Databases or Storage service.

    - -

    Each execution has a default timeout of 15 seconds to prevent hanging functions from blocking resources. This timeout can be configured per function on a function's settings page or in appwrite.json for up to 900 seconds.

    - -

    Ignore Files

    - -

    Library folders such as node_modules or vendor should not be included in your tarball since these dependencies will be installed during your function's build process. Similarly, you should not include files containing secrets in your deployment. You can use the Appwite CLI's file ignoring feature to exclude specific files from a deployment.

    - -

    You can use the ignore property in your appwrite.json file to specify which files and folders should be ignored. This value must be an array of paths, as seen in the example below:

    - -
    -
    {
    -    ...
    -    "functions": [
    -        {
    -            "$id": "6213b58cb21dda6c3263",
    -            "name": "My Awesome Function",
    -            "runtime": "node-17.0",
    -            "path": "My Awesome Function",
    -            "entrypoint": "src/index.js",
    -            "ignore": [ "node_modules", ".env" ]
    -        },
    -        ...
    -    ],
    -}
    -
    - -

    The example configuration above would not deploy the folder node_modules and the file .env.

    - -

    Alternatively, you can add a .gitignore file into your function folder and Appwrite CLI will ignore files specified in there. Keep in mind that if present, the ignore configuration in appwrite.json will nullify your ignore file.

    - -

    If you need to use a .gitignore file for your version control but don't want the Appwrite CLI to use it, you can specify the ignore key in appwrite.json to be an empty array.

    - -

    Supported Runtimes

    - -

    Appwrite provides multiple code runtimes to execute your custom functions. Each runtime uses a Docker image tied to a specific language version to provide a safe, isolated playground to run your team's code.

    - -

    Below is a list of supported Cloud Functions runtimes. The Appwrite team continually adds support for new runtimes.

    - - - - - - - - - - - - $runtime): ?> - - - - - - - - -
    NameImageArchitectures
    Function Env.escape($key); ?> escape($runtime['image'] ?? ''); ?> escape(implode(' / ', $runtime['supports'] ?? [])); ?>
    - -

    By default, the following runtimes are enabled: node-16.0, php-8.0, python-3.9, ruby-3.0, and dart-2.17.

    - -

    Function Variables

    - -

    Function variables supplied by Appwrite in addition to your own defined function variables that you can access from your function code. These variables give you information about your execution runtime environment.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    - APPWRITE_FUNCTION_ID - Your function's unique ID.
    - APPWRITE_FUNCTION_NAME - Your function's name.
    - APPWRITE_FUNCTION_DEPLOYMENT - Your function's code deployment unique ID.
    - APPWRITE_FUNCTION_TRIGGER - Either 'event' when triggered by one of the selected scopes, 'http' when triggered by an HTTP request or the Appwrite Console, or 'schedule' when triggered by the cron schedule.
    - APPWRITE_FUNCTION_RUNTIME_NAME - Your function runtime name. Can be any of Appwrite supported execution runtimes.
    - APPWRITE_FUNCTION_RUNTIME_VERSION - Your function runtime version.
    - APPWRITE_FUNCTION_EVENT - Your function event name. This value is available only when your function trigger is 'event.' This variable value can be any of Appwrite system events.
    - APPWRITE_FUNCTION_EVENT_DATA - Your function event payload. This value is available only when your function trigger is 'event'. This variable value contains a string in JSON format with your specific event data.
    - APPWRITE_FUNCTION_DATA -

    version >= 0.8.0

    -
    Your function's custom execution data. This variable's value contains a string in any format. If the custom data is in JSON FORMAT, it must be parsed inside the function code. Note that this variable can be set only when triggering a function using the SDK or HTTP API and the Appwrite Dashboard.
    - APPWRITE_FUNCTION_PROJECT_ID -

    version >= 0.8.0

    -
    Your function's project ID.
    - APPWRITE_FUNCTION_USER_ID -

    version >= 0.8.0

    -
    The userId of the user that triggered your function's execution. Executions triggered in the Appwrite console will be prepended with "admin-".
    - APPWRITE_FUNCTION_JWT -

    version >= 0.8.0

    -
    A JSON Web Token generated for the user that executes your function.
    - APPWRITE_FUNCTION_EVENT_PAYLOAD -

    version < 0.8.0 (deprecated)

    -
    Your function event payload. Deprecated in favor of APPWRITE_FUNCTION_EVENT_DATA in version 0.8.0.
    - APPWRITE_FUNCTION_ENV_NAME -

    version < 0.8.0 (deprecated)

    -
    Your function environment name. Can be any of Appwrite supported execution environments.
    - APPWRITE_FUNCTION_ENV_VERSION -

    version < 0.8.0 (deprecated)

    -
    Your function environment version.
    - -
    -

    Using an Appwrite SDK in Your Function

    -

    Appwrite Server SDKs require an API key, an endpoint, and a project ID for authentication. Appwrite passes in your project ID with the function variable APPWRITE_FUNCTION_PROJECT_ID, but not the endpoint and API key. If you need to use a Server SDK, you will need to add function variables for your endpoint and API key in the Settings tab of your function.

    -

    If you are running a local Appwrite instance, you will need to pass in the machine's public IP instead of 'https://localhost/v1'. Localhost inside the function's runtime container is not the same as localhost of your machine.

    -
    - -

    Appwrite SDKs in Functions

    - -

    You can integrate Appwrite Functions with other Appwrite services by using the appropriate Server SDK for your runtime. You can find starter code for your function's runtime in the Appwrite Function Starter repository.

    - -

    To initialize a Server SDK in a function, you need to provide your Appwrite endpoint and an API key in the Variables tab of your Function. The ID of your Appwrite project is passed in automatically as APPWRITE_FUNCTION_PROJECT_ID.

    - -

    Monitor & Debug

    - -

    You can monitor your function execution usage stats and logs from your Appwrite console. To access your functions usage stats and logs, click the Usage tab in your function dashboard.

    - -

    The usage screen in your console will allow you to track the number of execution and your function CPU usage time. You can also review a detailed log of your function execution history, including the function exit code, output log, and error log.

    - -setParam('srcLight', '/images-ee/docs/functions-monitor-light.png') - ->setParam('srcDark', '/images-ee/docs/functions-monitor-dark.png') - ->setParam('alt', 'Function usage and logs tracking.') - ->setParam('description', 'Function usage and logs tracking.') - ->render(); -?> - -

    Demos & Examples

    - -

    There are many Cloud Function demos and examples created by the Appwrite team and community in multiple coding languages. These examples are available at our examples repository on GitHub. You can also submit your examples by submitting a pull-request.

    +

    + Appwrite Functions let you extend Appwrite by adding your own code and logic. + You can think of them as code snippets that are triggered by server events, webhooks, scheduled executions, or user invokation. + Each function will have their own URL, execute in their own isolated container, and have their own configurable environment variables and permissions. + With these features, Appwrite Functions unlock limitless potential to expand Appwrite's capabilities with custom logic and integrations. +

    + +

    Getting Started

    +

    + The quickest way to experience Appwrite functions is to [TODO: @matej What's a good example to show to give devs an idea of what Appwrite Functions workflow is like?] +

    + +

    Explore Features

    +

    + To fully harness the power of Appwrite Functions, explore the following features. +

    + +

    Templates

    +

    + If you need to integrate Appwrite with a third-party API or add a function for common utilities, + there might already be a function template made by the Appwrite community that fits your needs. + Function templates are Appwrite Functions repositories that you can clone and add to your Appwrite instance. +

    + +

    +Learn more about using function templates +

    + +

    Deploy

    +

    + Appwrite Functions are designed to be maintainable and fit into a familiar development workflow. + You can deploy them from a GitHub repository branch or using the Appwrite CLI. +

    +

    +Learn more about using deploying functions +

    + +

    Execute

    +

    +Appwrite Functions can be executed directly through a request to the API, or triggered by events, webhooks, or scheduled executions. +This flexible execution models unlocks many potential usecases for Appwrite functions. +Explore using Appwrite Functions to execute a complex routine of logic, or execute background tasks on a schedule. +

    +

    +Learn more about using executing functions +

    + +

    Syntax

    +

    +Writing Appwrite Functions should feel familiar to writing controllers in an HTTP server. +In your function, you'll receive a request object, add transformations and logic, then return a response. +Almost anything can be executed as code in an Appwrite Function. +

    +Learn more about using function syntax +

    +

    +

    Runtime

    +

    +Appwrite supports many open-source runtimes. Find your prefered language and start writing your functions. +

    +

    +Learn more about using runtimes +

    +

    Debug

    +

    +Let's be honest, we spend more time debugging our code than writing our code. +So it's important to be able to log, debug, and test your Appwrite Functions in development and production. +

    +

    +Learn more about debugging functions +

    \ No newline at end of file From 52bdc62f0470e2fdfdfce95f48d037c7f5ecbe2a Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 18 Jul 2023 15:48:58 +0000 Subject: [PATCH 05/80] Reorder index, add shell for quickstart example --- app/views/docs/functions.phtml | 136 ++++++++++++++++++++++++++++++--- app/views/docs/index.phtml | 2 +- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 0bdcb3d7e..b8b94d905 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -16,9 +16,124 @@ $runtimes = $this->getParam('runtimes', []);

    Getting Started

    - The quickest way to experience Appwrite functions is to [TODO: @matej What's a good example to show to give devs an idea of what Appwrite Functions workflow is like?] + Appwrite Functions unlock limitless possibilities, but it's simple to get started. You can deploy your first function and execute it in minutes.

    +
      +
    • +

      Node.js

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      PHP

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Python

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Ruby

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Deno

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Dart

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Swift

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      .NET

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Kotlin

      +
      +
      +
      
      +
      +            
      +
      + +
      +
    • +
    • +

      Java

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      C++

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    + +

    Explore Features

    To fully harness the power of Appwrite Functions, explore the following features. @@ -35,6 +150,16 @@ $runtimes = $this->getParam('runtimes', []); Learn more about using function templates

    +

    Syntax

    +

    +Writing Appwrite Functions should feel familiar to writing controllers in an HTTP server. +In your function, you'll receive a request object, add transformations and logic, then return a response. +Almost anything can be executed as code in an Appwrite Function. +

    +

    +Learn more about using function syntax +

    +

    Deploy

    Appwrite Functions are designed to be maintainable and fit into a familiar development workflow. @@ -54,15 +179,6 @@ Explore using Appwrite Functions to execute a complex routine of logic, or execu Learn more about using executing functions

    -

    Syntax

    -

    -Writing Appwrite Functions should feel familiar to writing controllers in an HTTP server. -In your function, you'll receive a request object, add transformations and logic, then return a response. -Almost anything can be executed as code in an Appwrite Function. -

    -Learn more about using function syntax -

    -

    Runtime

    Appwrite supports many open-source runtimes. Find your prefered language and start writing your functions. diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 7d9bb0b45..8b772de6c 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -96,9 +96,9 @@ $cols = [ Functions

    From 6b2da17e590d92da5d8225df2dcd9643ef3154f1 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 18 Jul 2023 21:18:47 +0000 Subject: [PATCH 06/80] Added scaffolding --- app/views/docs/functions-debugging.phtml | 4 +++ app/views/docs/functions-deploy.phtml | 37 ++++++++++++++++++++++++ app/views/docs/functions-develop.phtml | 22 ++++++++++++++ app/views/docs/functions-execute.phtml | 35 ++++++++++++++++++++++ app/views/docs/functions-runtimes.phtml | 5 ++++ app/views/docs/functions.phtml | 10 +++++-- app/views/docs/index.phtml | 2 +- 7 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 app/views/docs/functions-debugging.phtml create mode 100644 app/views/docs/functions-deploy.phtml create mode 100644 app/views/docs/functions-develop.phtml create mode 100644 app/views/docs/functions-execute.phtml create mode 100644 app/views/docs/functions-runtimes.phtml diff --git a/app/views/docs/functions-debugging.phtml b/app/views/docs/functions-debugging.phtml new file mode 100644 index 000000000..870f19cdd --- /dev/null +++ b/app/views/docs/functions-debugging.phtml @@ -0,0 +1,4 @@ +

    + Appwrite Functions... do we need this page? +

    + diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml new file mode 100644 index 000000000..79d8fba2c --- /dev/null +++ b/app/views/docs/functions-deploy.phtml @@ -0,0 +1,37 @@ +

    + Appwrite Functions are mini application in Appwrite with their own endpoint. + Each function can have many deployments, which can be thought of as versions of the mini-application. +

    + +

    + Functions can be deployed in different ways to meet your unique development habbits. + You can automatically deploy Appwrite Functions from source control, build your own deployment pipelines using the Appwrite CLI, or upload code files manually. + Here's everything you need to know to deploy your first Appwrite Function. +

    + +

    Version Control Systems

    +

    + The recommended way to manage your Appwrite Function deployments is to use a version control system, such as GitHub. + This offers simple versioning and collaboration that will easily fit into the rest of your development workflow. +

    + +[TODO: Describe how to create a GitHub deployment] + +

    Create

    +

    Deploy

    +

    ...

    +

    Profit?!

    + +

    Templates

    + +

    CLI

    +[TODO] + +

    Manual

    +[TODO] + +

    Deployment Life Cycle

    +

    Deployments will go through the following states throughout their life cycle.

    +[TODO: @matej, describe the life cycle of a deployment (like being uploaded, built, etc)] + +[TODO: @matej What else?] \ No newline at end of file diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml new file mode 100644 index 000000000..4555356b5 --- /dev/null +++ b/app/views/docs/functions-develop.phtml @@ -0,0 +1,22 @@ +

    + Appwrite Functions offer a familiar interface if you've developed REST endpoints. + Each function is handled following a request and response pattern. + Here's what you need to know to start writing your first Appwrite Function. +

    + +

    Function Life Cycle

    +[TODO: @matej, describe the life cycle of a function, maybe build a UML and give to jade to mock up (optional)] + +

    The Context Object

    + +

    Request

    + +

    Response

    + +

    Logging

    + +

    Accessing Environment Variables

    + +

    Using Appwrite in a Function

    + +

    Limitations

    diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml new file mode 100644 index 000000000..acc4ac356 --- /dev/null +++ b/app/views/docs/functions-execute.phtml @@ -0,0 +1,35 @@ +

    + Appwrite Functions executions can be invoked in several ways. + Functions can be invoked through the Appwrite SDK and visting its preview link. Functions can also be triggered by events, webhooks, and scheduled executions. + Here are all the different ways to consume your new Appwrite Functions. +

    + +

    SDK

    +

    + +

    + +

    Preview Link

    +

    + Each Appwrite function also has its own preview link. + When requests are made to this link, whether through a browser or through an HTTP requests, + the request information like request headers and request body will be passed to the function. + This unlocks interesting ways to integrate other apps and backends to your Appwrite project. +

    + +

    Events

    +

    + +

    + +

    Webhooks

    +

    + +

    + + +

    Schedule

    +

    + + +

    \ No newline at end of file diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml new file mode 100644 index 000000000..5686a2d28 --- /dev/null +++ b/app/views/docs/functions-runtimes.phtml @@ -0,0 +1,5 @@ +

    + Appwrite Functions supports ... + wait. wut we do with runtimes not yet on cloud??? +

    + diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index b8b94d905..5ad3d8853 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -157,7 +157,7 @@ In your function, you'll receive a request object, add transformations and logic Almost anything can be executed as code in an Appwrite Function.

    -Learn more about using function syntax +Learn more about using function syntax

    Deploy

    @@ -193,4 +193,10 @@ So it's important to be able to log, debug, and test your Appwrite Functions in

    Learn more about debugging functions -

    \ No newline at end of file +

    + +

    Upgrade

    +

    + Appwrite Functions received major upgrades in Appwrite version 1.4. + If you still have functions from previous versions, [TODO: @matej what is the consequence what should they do?] +

    diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 8b772de6c..5f41f6647 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -96,7 +96,7 @@ $cols = [ Functions
      -
    •    Syntax
    • +
    •    Develop
    •    Deploy
    •    Execute
    •    Runtimes
    • From 6fbc33ae2c2b258ba35cd552a4a37bb18b9e28c6 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Fri, 21 Jul 2023 21:56:49 +0000 Subject: [PATCH 07/80] Add node js example --- app/views/docs/functions-debugging.phtml | 4 ---- app/views/docs/functions-deploy.phtml | 24 ++++++++++++++++---- app/views/docs/functions-develop.phtml | 11 +++++++-- app/views/docs/functions-execute.phtml | 29 +++++++++++++----------- app/views/docs/functions.phtml | 10 ++++---- 5 files changed, 50 insertions(+), 28 deletions(-) delete mode 100644 app/views/docs/functions-debugging.phtml diff --git a/app/views/docs/functions-debugging.phtml b/app/views/docs/functions-debugging.phtml deleted file mode 100644 index 870f19cdd..000000000 --- a/app/views/docs/functions-debugging.phtml +++ /dev/null @@ -1,4 +0,0 @@ -

      - Appwrite Functions... do we need this page? -

      - diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 79d8fba2c..8f57badb0 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -9,20 +9,32 @@ Here's everything you need to know to deploy your first Appwrite Function.

      -

      Version Control Systems

      +

      Git

      - The recommended way to manage your Appwrite Function deployments is to use a version control system, such as GitHub. + The recommended way to manage your Appwrite Function deployments is to use Git. This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.

      [TODO: Describe how to create a GitHub deployment]

      Create

      +- Install Appwrite on your GitHub account/organization +- Select a repository +- Select a production branch +- Define root directory +- Silent mode -> No comments on PRs +- Build commands

      Deploy

      -

      ...

      -

      Profit?!

      +- Prod branch is automatically deployed on push and activated + - Non prod branches are also built but not activated automatically +- Push to branch +

      Debugging Builds

      +- Check logs and errors +- Redeploy button for non-code issues [TODO: @matej write an example]

      Templates

      +- Select repo -> Specify path +- Make repository

      CLI

      [TODO] @@ -30,8 +42,10 @@

      Manual

      [TODO] +[tar --exclude code.tar.gz -czf code.tar.gz .] +

      Deployment Life Cycle

      Deployments will go through the following states throughout their life cycle.

      -[TODO: @matej, describe the life cycle of a deployment (like being uploaded, built, etc)] +[TODO: @matej, describe the life cycle of a deployment (like being uploaded, built, build steps, etc.)] [TODO: @matej What else?] \ No newline at end of file diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 4555356b5..2997898fb 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -4,8 +4,7 @@ Here's what you need to know to start writing your first Appwrite Function.

      -

      Function Life Cycle

      -[TODO: @matej, describe the life cycle of a function, maybe build a UML and give to jade to mock up (optional)] +[TODO: CTA if you just want examples, click this link]

      The Context Object

      @@ -14,9 +13,17 @@

      Response

      Logging

      +-> LIST WHAT WE SHOW AND WHAT WE DONT SHOW

      Accessing Environment Variables

      +

      Depencies

      +

      Using Appwrite in a Function

      +[TODO: 2 examples -> JWT and API keys] + +

      Recipes

      +[TODO: Matej and Luke write examples]

      Limitations

      + \ No newline at end of file diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index acc4ac356..174d7d771 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -1,35 +1,38 @@

      Appwrite Functions executions can be invoked in several ways. - Functions can be invoked through the Appwrite SDK and visting its preview link. Functions can also be triggered by events, webhooks, and scheduled executions. + Functions can be invoked through the Appwrite SDK and visting its REST endpoint. Functions can also be triggered by events and scheduled executions. Here are all the different ways to consume your new Appwrite Functions.

      -

      SDK

      +

      Domain

      - -

      - -

      Preview Link

      -

      - Each Appwrite function also has its own preview link. + Each Appwrite function has its own domain. (can be custom) When requests are made to this link, whether through a browser or through an HTTP requests, the request information like request headers and request body will be passed to the function. This unlocks interesting ways to integrate other apps and backends to your Appwrite project.

      -

      Events

      +DOMAIN IGNORE PERMISSIONS AOSDHFGAKSHDJG + +

      SDK

      +- two examples + -> sync + -> async + -> pass body

      -

      Webhooks

      +

      Events

      Schedule

      -

      - +

      -

      \ No newline at end of file +

      + +[TODO: EXECUTION ARTIFACTS/LOGS/STUFF] + diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 5ad3d8853..86266c49a 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -24,9 +24,11 @@ $runtimes = $this->getParam('runtimes', []);

      Node.js

      -
      
      -
      -            
      +
      export default async ({ res }) => {
      +   return res.json({
      +    motto: 'Build Fast. Scale Big. All in One Place.'
      +  });
      +};
      @@ -150,7 +152,7 @@ $runtimes = $this->getParam('runtimes', []); Learn more about using function templates

      -

      Syntax

      +

      Develop

      Writing Appwrite Functions should feel familiar to writing controllers in an HTTP server. In your function, you'll receive a request object, add transformations and logic, then return a response. From 3610aadf00ef588ead24a9a00ba24d1789146a20 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 24 Jul 2023 21:35:34 +0000 Subject: [PATCH 08/80] More shell for the functions --- app/views/docs/functions-develop.phtml | 173 ++++++++++++++++++++++++- app/views/docs/functions.phtml | 8 +- 2 files changed, 177 insertions(+), 4 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 2997898fb..736039a0c 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -4,18 +4,189 @@ Here's what you need to know to start writing your first Appwrite Function.

      -[TODO: CTA if you just want examples, click this link] +
      +

      Just want the code?

      +

      If you prefer to learn through examples, explore the recipes section.

      +

      The Context Object

      +

      + Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite console. + All input, output, and logging **must be** handled through the context object passed in. +

      + +

      You'll find these properties in the context object.

      + + + + + + + + + + + + + + + + + + + + + + + + + + +
      PropertyDescription
      context.reqContains request information like method, body, and headers. See full examples here.
      context.resContains methods to build a response and return information. See full examples here.
      context.logLogs information to the Appwrite Console, end users will not be able to see these logs. See full examples here.
      context.errorLogs errors to the Appwrite Console, end users will not be able to see these errors. See full examples here.
      + +

      Some lamguages support unpacking. You'll see us use unpacking in examples, which has the following syntax.

      +[TODO: Example below for relevant languages.] +
        +
      • +

        Node.js

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        PHP

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Python

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Ruby

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Deno

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Dart

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Swift

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        .NET

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Kotlin

        +
        +
        +
        
        +
        +            
        +
        + +
        +
      • +
      • +

        Java

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        C++

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +

      Request

      +[TODO: full table of what's in a request] +[TODO: full examples of what's in a request]

      Response

      +[TODO: Full table of what's in a res] +[TODO: full examples of what's in a res] +

      Logging

      +

      + To protect user privacy, the request and response objects are not logged to the Appwrite Console by default. + This means, to see logs or debug function executions you need to use the context.log() and context.error() methods. + These logs are only visible to developers with access to the Appwrite Console. +

      -> LIST WHAT WE SHOW AND WHAT WE DONT SHOW

      Accessing Environment Variables

      +

      + If you need to pass constants or secrets to Appwrite Functions, you can use environment variables. +

      + +[TODO: ways to setup environment variables (link to deployment docs?)] + +

      + You can access the environment variables through the systems library of each language. +

      Depencies

      diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 86266c49a..b3cadd9b7 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -138,6 +138,8 @@ $runtimes = $this->getParam('runtimes', []);

      Explore Features

      + Appwrite Functions help you start small and scale big. + Now you've created your first Appwrite Function, it's time to learn the different ways to develop, deploy, and execute your Appwrite Functions. To fully harness the power of Appwrite Functions, explore the following features.

      @@ -159,7 +161,7 @@ In your function, you'll receive a request object, add transformations and logic Almost anything can be executed as code in an Appwrite Function.

      -Learn more about using function syntax +Learn more about developing functions

      Deploy

      @@ -168,7 +170,7 @@ Almost anything can be executed as code in an Appwrite Function. You can deploy them from a GitHub repository branch or using the Appwrite CLI.

      -Learn more about using deploying functions +Learn more about using deploying functions

      Execute

      @@ -178,7 +180,7 @@ This flexible execution models unlocks many potential usecases for Appwrite func Explore using Appwrite Functions to execute a complex routine of logic, or execute background tasks on a schedule.

      -Learn more about using executing functions +Learn more about using executing functions

      Runtime

      From 3858af8bc65bec51f93ea40f6d84d776a78ed4c1 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 25 Jul 2023 22:22:28 +0000 Subject: [PATCH 09/80] Add some request and response examples --- app/views/docs/functions-develop.phtml | 281 ++++++++++++++++++++++++- app/views/js.js | 32 +++ 2 files changed, 309 insertions(+), 4 deletions(-) create mode 100644 app/views/js.js diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 736039a0c..d547d2864 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -161,13 +161,286 @@

    Request

    -[TODO: full table of what's in a request] -[TODO: full examples of what's in a request] +

    + If you pass data into an Appwrite function, it'll be found in the request object. + This includes all invokation methods, such as data from Appwrite SDKs, HTTP calls, Appwrite events, and browsers visiting the configured domain. + Explore the request object with the following function, which logs all request params to the Appwrite Console. +

    + +
      +
    • +

      Node.js

      +
      +
      +
      export default async ({ req, res, log }) => {
      +    log(req.bodyString);  // Raw request body, contains request data
      +    log(req.body);        // Body parsed on content-type, only supports JSON
      +    log(req.headers);     // Request headers
      +    log(req.scheme);      // Value of the x-forwarded-proto header, usually http or https
      +    log(req.method);      // Request method, such as GET, POST, PUT, DELETE, PATCH, etc.
      +    log(req.url);         // Full URL, for example: http://awesome.appwrite.io:8000/v1/hooks?limit=12&offset=50
      +    log(req.host);        // Hostname from the host header, such as awesome.appwrite.io
      +    log(req.port);        // Port from the host header, for example 8000
      +    log(req.path);        // Path part of URL, for example /v1/hooks
      +    log(req.queryString); // Raw query params string. For example "limit=12&offset=50"
      +    log(req.query);       // Parsed query params. For example, req.query.limit
      +
      +    return res.send("All the request parameters are logged to the Appwrite Console.");
      +};
      +
      +
      +
    • +
    • +

      PHP

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Python

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Ruby

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Deno

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Dart

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Swift

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      .NET

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Kotlin

      +
      +
      +
      
      +
      +            
      +
      + +
      +
    • +
    • +

      Java

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      C++

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +

    Response

    -[TODO: Full table of what's in a res] -[TODO: full examples of what's in a res] +

    + If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. + The response information will not be logged to the Appwrite Console. + There are several possible ways to send a response, explore them in the following Appwrite Function. +

    +
      +
    • +

      Node.js

      +
      +
      +
      export default async ({ req, res, log }) => {
      +    switch (req.body) {
      +        case 'send':
      +            return res.send(
      +                "This is a text response", 
      +                200, 
      +                {
      +                    "content-type": "application/text"
      +                }
      +            );
      +        case 'json':
      +            return res.json(
      +                {
      +                    "type": "This is a JSON response"
      +                }, 
      +                200, 
      +                {
      +                    "content-type": "application/json"
      +                }
      +            );
      +        case 'redirect':
      +            return res.json(
      +                "https://appwrite.io", 
      +                301, 
      +                {
      +                    "content-type": "application/json"
      +                }
      +            );
      +        default:
      +            return res.empty();
      +    }
      +};
      +
      +
      +
    • +
    • +

      PHP

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Python

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Ruby

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Deno

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Dart

      +
      +
      +
      
       
      +            
      +
      +
      +
    • +
    • +

      Swift

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      .NET

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      Kotlin

      +
      +
      +
      
      +
      +            
      +
      + +
      +
    • +
    • +

      Java

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +
    • +

      C++

      +
      +
      +
      
      +
      +            
      +
      +
      +
    • +

    Logging

    diff --git a/app/views/js.js b/app/views/js.js new file mode 100644 index 000000000..e56b14ce6 --- /dev/null +++ b/app/views/js.js @@ -0,0 +1,32 @@ +export default async ({ req, res, log }) => { + switch (req.body) { + case 'send': + return res.send( + "This is a text response", + 200, + { + "content-type": "application/text" + } + ); + case 'json': + return res.json( + { + "type": "This is a JSON response" + }, + 200, + { + "content-type": "application/json" + } + ); + case 'redirect': + return res.json( + "https://appwrite.io", + 301, + { + "content-type": "application/json" + } + ); + default: + return res.empty(); + } +}; \ No newline at end of file From 71439b8ee898d03eaf5e490ebd18cbee59031de3 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 26 Jul 2023 00:22:19 +0000 Subject: [PATCH 10/80] Adds recipes page --- app/views/docs/functions-develop.phtml | 6 ++++-- app/views/docs/functions-recipes.phtml | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 app/views/docs/functions-recipes.phtml diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index d547d2864..8053f3f87 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -448,13 +448,15 @@ This means, to see logs or debug function executions you need to use the context.log() and context.error() methods. These logs are only visible to developers with access to the Appwrite Console.

    --> LIST WHAT WE SHOW AND WHAT WE DONT SHOW +

    Here's an example of using logs and errors.

    +[TODO] +

    You can access these logs through the following steps.

    +[TODO]

    Accessing Environment Variables

    If you need to pass constants or secrets to Appwrite Functions, you can use environment variables.

    - [TODO: ways to setup environment variables (link to deployment docs?)]

    diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml new file mode 100644 index 000000000..a8149e25f --- /dev/null +++ b/app/views/docs/functions-recipes.phtml @@ -0,0 +1,6 @@ +

    + Appwrite Functions is all about flexibility. + Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. + Take a look at the following. +

    +[TODO: @matej] \ No newline at end of file From 16267be21e81e3f2104366c2c2ce765beebdb383 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 26 Jul 2023 00:22:36 +0000 Subject: [PATCH 11/80] Fill in intro of recipes --- app/views/docs/functions-recipes.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index a8149e25f..df6e9baac 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -3,4 +3,4 @@ Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. Take a look at the following.

    -[TODO: @matej] \ No newline at end of file +[TODO: @matej @luke -> Let's have some simple recipes here for common actions] \ No newline at end of file From 9819ffbfc38637abf6678b193690d8970d0153aa Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 26 Jul 2023 21:17:26 +0000 Subject: [PATCH 12/80] Filled in more information from development to runtimes and deployment --- app/views/docs/functions-deploy.phtml | 32 ++- app/views/docs/functions-develop.phtml | 268 +++++++++++++++++++++++- app/views/docs/functions-execute.phtml | 54 ++++- app/views/docs/functions-recipes.phtml | 3 +- app/views/docs/functions-runtimes.phtml | 38 ++++ 5 files changed, 369 insertions(+), 26 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 8f57badb0..1994682c9 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -37,15 +37,31 @@ - Make repository

    CLI

    -[TODO] +- Setup cli +

    + To deploy with the Appwrite CLI, your function's folder structure must be configured in a specific way. + The folder path is then added to a config file called appwrite.json that tells the CLI where each function is stored. + To ensure the folder structure is setup correctly and appwrite.json is configured correctly, use the appwrite init function method to create a shell function. +

    +
    +
    appwrite init function
    +
    -

    Manual

    -[TODO] +

    + Give your function a name and choose your runtime. + This will create a new starter function in the current directory and also add it to your appwrite.json file. +

    + +

    + Edit the automatically generated code and add dependencies to the dependency files of your language or framework. + Then, deploy the function using the following command. +

    -[tar --exclude code.tar.gz -czf code.tar.gz .] +
    +
    appwrite deploy function
    +
    -

    Deployment Life Cycle

    -

    Deployments will go through the following states throughout their life cycle.

    -[TODO: @matej, describe the life cycle of a deployment (like being uploaded, built, build steps, etc.)] +

    Manual

    +[TODO] -[TODO: @matej What else?] \ No newline at end of file +[tar --exclude code.tar.gz -czf code.tar.gz .] \ No newline at end of file diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 8053f3f87..28ee83399 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -9,6 +9,20 @@

    If you prefer to learn through examples, explore the recipes section.

    +

    Function Flow

    +

    + There is a clear flow for all Appwrite Functions, from beginning to end. + Here's everything that happens during a function execution. +

    + +
      +
    1. Invokation, where Appwrite receives a event to execute a Function. This event could be a request from an SDK, a request to the function's domain, a scheduled execution, or an execution triggered by an event within your Appwrite project.
    2. +
    3. After a function is invoked, Appwrite passes request information to your function's executor.
    4. +
    5. The executor runs the function code you deployed and waits for it to return.
    6. +
    7. Function terminates either when the user returns with a method from context.res, when the user code throws an exception, or times out.
    8. +
    + +

    The Context Object

    Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite console. @@ -25,19 +39,19 @@ - context.req + context.req Contains request information like method, body, and headers. See full examples here. - context.res + context.res Contains methods to build a response and return information. See full examples here. - context.log + context.log Logs information to the Appwrite Console, end users will not be able to see these logs. See full examples here. - context.error + context.error Logs errors to the Appwrite Console, end users will not be able to see these errors. See full examples here. @@ -450,7 +464,122 @@

    Here's an example of using logs and errors.

    -[TODO] +

    You can access these logs through the following steps.

    [TODO]

    Accessing Environment Variables

    @@ -468,8 +597,133 @@

    Using Appwrite in a Function

    [TODO: 2 examples -> JWT and API keys] +

    Quick Example

    + + +

    Using Multiple Files

    + +

    Recipes

    -[TODO: Matej and Luke write examples] +

    + We have a dedicated page of recipes to implement common functionalities in Appwrite Functions, like parsing request path and params, or making requests to third party APIs. +

    + +

    +Explore examples and recipes +

    +

    Limitations

    - \ No newline at end of file +

    Libraries dependent on compiled binaries not available on the executors cannot be installed.

    \ No newline at end of file diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 174d7d771..85476f0c0 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -12,27 +12,61 @@ This unlocks interesting ways to integrate other apps and backends to your Appwrite project.

    -DOMAIN IGNORE PERMISSIONS AOSDHFGAKSHDJG

    SDK

    -- two examples - -> sync - -> async - -> pass body +TODO: two examples running the function sync and async form the SDK

    Events

    - + TODO: Describe steps to navigate the UI

    Schedule

    -

    - -

    +

    Appwrite supports scheduled function executions. You can schedule executions using cron expressions in the settings of your function. Cron supports recurring executions as frequently as every minute.

    + +

    Here are some cron expressions for common intervals.

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Cron ExpressionSchedule
    */15 * * * *Every 15 minutes
    0 * * * *Every Hour
    0 0 * * *Every day at 00:00
    0 0 * * 1Every monday at 00:00
    -[TODO: EXECUTION ARTIFACTS/LOGS/STUFF] +

    Permissions

    +

    + Appwrite Functions can be executed using Client or Server SDKs. + Client SDKs must be authenticated with an account that has been granted execution permissions on the function's settings page. + Server SDKs require an API key with the correct scopes. +

    +

    + If your function with a generated or custom domain, permissions are disabled for this function. + Anyone visiting this domain will be able to execute the function. + If you need to enforce permissions for functions with a domain, use authentication methods like JWT. +

    +

    Logs and results

    diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index df6e9baac..a6e6f7e09 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -3,4 +3,5 @@ Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. Take a look at the following.

    -[TODO: @matej @luke -> Let's have some simple recipes here for common actions] \ No newline at end of file +[TODO: @matej @luke -> Let's have some simple recipes here for common actions] +[TODO: @matej @luke -> Example with JWT, show both client and server code] \ No newline at end of file diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 5686a2d28..3ef464840 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -1,5 +1,43 @@ +getParam('events', []); +$runtimes = $this->getParam('runtimes', []); + +?> + +

    Appwrite Functions supports ... wait. wut we do with runtimes not yet on cloud???

    +

    Supported Runtimes

    + +

    Appwrite provides multiple code runtimes to execute your custom functions. Each runtime uses a Docker image tied to a specific language version to provide a safe, isolated playground to run your team's code.

    + +

    Below is a list of supported Cloud Functions runtimes. The Appwrite team continually adds support for new runtimes.

    + + + + + + + + + + + + $runtime): ?> + + + + + + + + +
    NameImageArchitectures
    Function Env.escape($key); ?> escape($runtime['image'] ?? ''); ?> escape(implode(' / ', $runtime['supports'] ?? [])); ?>
    + +[TODO: Label which ones are cloud only] From db582b56e9c942f3410f2f48b4850d2e09159456 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 27 Jul 2023 01:09:48 +0000 Subject: [PATCH 13/80] Add manual install command --- app/views/docs/functions-deploy.phtml | 49 +++++++++++++++++++++++-- app/views/docs/functions-runtimes.phtml | 4 +- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 1994682c9..42067dc6d 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -61,7 +61,50 @@
    appwrite deploy function
    -

    Manual

    -[TODO] +

    Manual Deployment

    +

    You can also upload your functions to be deployed using the Appwrite console. The example below shows a simple Node.JS function, but the same idea applies to any other language.

    -[tar --exclude code.tar.gz -czf code.tar.gz .] \ No newline at end of file +
    +
    .
    +├── package.json
    +└── index.js
    +
    +
    + +

    First, navigate inside the folder that contains your dependency file. Package your code files into the .tar.gz format with this tar command:

    + +
      +
    • +

      Unix

      + +
      +
      tar --exclude code.tar.gz -czf code.tar.gz .
      +
      +
    • +
    • +

      CMD

      + +
      +
      tar --exclude code.tar.gz -czf code.tar.gz .
      +
      +
    • +
    • +

      PowerShell

      + +
      +
      tar --exclude code.tar.gz -czf code.tar.gz .
      +
      +
    • +
    + +

    Next, navigate to your Appwrite console and upload the function.

    + +
      +
    1. Navigate to the function you want to deploy.
    2. +
    3. Click Create deployment.
    4. +
    5. Select the Manual tab.
    6. +
    7. Input the entry point of your function under Entrypoint. For the example above, it would be index.js.
    8. +
    9. Upload code.tar.gz.
    10. +
    11. Select Activate deployment after build to use your new function.
    12. +
    13. Click Create to deploy your function.
    14. +
    \ No newline at end of file diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 3ef464840..4e56f2812 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -9,8 +9,8 @@ $runtimes = $this->getParam('runtimes', []);

    - Appwrite Functions supports ... - wait. wut we do with runtimes not yet on cloud??? + Appwrite Functions supports an extensive list of runtimes to meet your unique tech preferences. + Not all runtimes are available on Appwrite Cloud, check for the Cloud label in each listed runtime to know whichones are available.

    Supported Runtimes

    From 4a5643369fbf11613961f1f98866179992d8aa82 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 30 Jul 2023 22:55:58 +0000 Subject: [PATCH 14/80] Add git instructions --- app/views/docs/functions-deploy.phtml | 65 ++++++++++++++++++++------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 42067dc6d..c5b92746a 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -4,7 +4,7 @@

    - Functions can be deployed in different ways to meet your unique development habbits. + Functions can be created and deployed in different ways to meet your unique development habits. You can automatically deploy Appwrite Functions from source control, build your own deployment pipelines using the Appwrite CLI, or upload code files manually. Here's everything you need to know to deploy your first Appwrite Function.

    @@ -15,27 +15,60 @@ This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.

    -[TODO: Describe how to create a GitHub deployment] +

    Create Funcion

    +

    Before deploying your function with Git, create a new function attached to your Git repo.

    +
      +
    1. + Navigate to Functions from the side bar of the Appwrite Console. +
    2. +
    3. + Click Create function. +
    4. +
    5. + When asked to Choose your source, under Connect Git repository, select your provider. +
    6. +
    7. + Search for the Git repository that hold your function and click connect. +
    8. +
    9. + Select a production branch. New commits made to the production branch will be automatically deployed and activated. +
    10. +
    11. + Input the root directory of the function inside the repository. +
    12. +
    13. + If you don't want deploy comments to be made on your PRs, select Silent mode. +
    14. +
    15. + Name your function, select a runtime that matches your function, and enter an entry point path, relative to the root directory from the previous step. +
    16. +
    17. + If you have build steps, like installing dependencies, input the commands into the Build settings heading's Command field. +
    18. +
    19. + Finally, configure the execute permissions of the function. For security, only provide execute permissions to the necessary roles. +
    20. +
    -

    Create

    -- Install Appwrite on your GitHub account/organization -- Select a repository -- Select a production branch -- Define root directory -- Silent mode -> No comments on PRs -- Build commands

    Deploy

    -- Prod branch is automatically deployed on push and activated - - Non prod branches are also built but not activated automatically -- Push to branch +
      +
    1. + Checkout your prodction branch in Git. +
    2. +
    3. + Create a new commit. +
    4. +
    5. + Push your new commit. +
    6. +
    7. + A new deployment will be automatically created. Deployments will be automatically activated when new commits are added to the production branch. +
    8. +

    Debugging Builds

    - Check logs and errors - Redeploy button for non-code issues [TODO: @matej write an example] -

    Templates

    -- Select repo -> Specify path -- Make repository -

    CLI

    - Setup cli

    From db2286b61f6200f67c82a8fde7559d930b9a4386 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 31 Jul 2023 16:34:00 +0100 Subject: [PATCH 15/80] feat: translate json response snippets --- app/views/docs/functions.phtml | 114 +++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 28 deletions(-) diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index b3cadd9b7..f1727b4e9 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -25,9 +25,9 @@ $runtimes = $this->getParam('runtimes', []);

    export default async ({ res }) => {
    -   return res.json({
    -    motto: 'Build Fast. Scale Big. All in One Place.'
    -  });
    +    return res.json({
    +        motto: 'Build Fast. Scale Big. All in One Place.'
    +    });
     };
    @@ -36,9 +36,11 @@ $runtimes = $this->getParam('runtimes', []);

    PHP

    -
    
    -
    -            
    +
    return function ($context) {
    +    return $context->res->json([
    +        'motto' => 'Build Fast. Scale Big. All in One Place.'
    +    ]);
    +}
    @@ -46,9 +48,10 @@ $runtimes = $this->getParam('runtimes', []);

    Python

    -
    
    -
    -            
    +
    def main(context):
    +    return context.res.json({
    +        "motto": "Build Fast. Scale Big. All in One Place.",
    +})
    @@ -56,9 +59,13 @@ $runtimes = $this->getParam('runtimes', []);

    Ruby

    -
    
    -
    -            
    +
    def main(context)
    +    return context.res.json(
    +        {
    +            "motto": "Build Fast. Scale Big. All in One Place."
    +        }
    +    )
    +end
    @@ -66,9 +73,11 @@ $runtimes = $this->getParam('runtimes', []);

    Deno

    -
    
    -
    -            
    +
    export default ({ req, res, log, error }: any) => {
    +  return res.json({
    +    motto: "Build Fast. Scale Big. All in One Place."
    +  });
    +};
    @@ -76,9 +85,13 @@ $runtimes = $this->getParam('runtimes', []);

    Dart

    -
    
    +            
    import 'dart:async';
     
    -            
    +Future main(final context) async { + return context.res.json({ + 'motto': 'Build Fast. Scale Big. All in One Place.', + }); +}
    @@ -86,9 +99,13 @@ $runtimes = $this->getParam('runtimes', []);

    Swift

    -
    
    +            
    import Foundation
     
    -            
    + func main(context: RuntimeContext) async throws -> RuntimeOutput { + return try context.res.json([ + "motto": "Build Fast. Scale Big. All in One Place." + ]) +}
    @@ -96,9 +113,16 @@ $runtimes = $this->getParam('runtimes', []);

    .NET

    -
    
    -
    -            
    +
    namespace DotNetRuntime;
    +public class Handler {
    +    public async Task Main(RuntimeContext Context) 
    +    {
    +        return Context.Res.Json(new Dictionary()
    +        {
    +            { "motto", "Build Fast. Scale Big. All in One Place." }
    +        });
    +    }
    +}
    @@ -106,9 +130,21 @@ $runtimes = $this->getParam('runtimes', []);

    Kotlin

    -
    
    +            
    package io.openruntimes.kotlin.src
     
    -            
    +import io.openruntimes.kotlin.RuntimeContext +import io.openruntimes.kotlin.RuntimeOutput + +class Main { + fun main(context: RuntimeContext): RuntimeOutput { + return context.res.json(mutableMapOf( + "motto" to "Build Fast. Scale Big. All in One Place.", + "learn" to "https://appwrite.io/docs", + "connect" to "https://appwrite.io/discord", + "getInspired" to "https://builtwith.appwrite.io" + )) + } +}
    @@ -117,9 +153,19 @@ $runtimes = $this->getParam('runtimes', []);

    Java

    -
    
    +            
    package io.openruntimes.java.src;
    +
    +import io.openruntimes.java.RuntimeContext;
    +import io.openruntimes.java.RuntimeOutput;
    +import java.util.HashMap;
     
    -            
    +public class Main { + public RuntimeOutput main(RuntimeContext context) throws Exception { + Map json = new HashMap<>(); + json.put("motto", "Build Fast. Scale Big. All in One Place."); + return context.getRes().json(json); + } +}
    @@ -127,9 +173,21 @@ $runtimes = $this->getParam('runtimes', []);

    C++

    -
    
    +            
    #include "../RuntimeResponse.h"
    +#include "../RuntimeRequest.h"
    +#include "../RuntimeOutput.h"
    +#include "../RuntimeContext.h"
     
    -            
    +namespace runtime { + class Handler { + public: + static RuntimeOutput main(RuntimeContext &context) { + Json::Value response; + response["motto"] = "Build Fast. Scale Big. All in One Place."; + return context.res.json(response); + } + }; +}
    From b5ec4432dd0d1215ddb7a27b1f869982f1383645 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 1 Aug 2023 12:19:32 +0100 Subject: [PATCH 16/80] feat: js currency convert recipe --- app/views/docs/functions-recipes.phtml | 110 ++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index a6e6f7e09..230b58619 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -1,7 +1,113 @@

    - Appwrite Functions is all about flexibility. + Appwrite Functions is all about flexibility. Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. Take a look at the following.

    [TODO: @matej @luke -> Let's have some simple recipes here for common actions] -[TODO: @matej @luke -> Example with JWT, show both client and server code] \ No newline at end of file + +

    Section 1: My First Function

    + +

    Creating the function

    + +
      +
    1. Create a new file, `index.js`.
    2. +
    3. Add the following code to `index.js`. This code will return 1.13 when the function is called, because 1€ equals approximately 1.13$. + +
      +
      module.exports = async function ({ res }) {
      +  return res.end('1.13');
      +};
      +
      + +
    4. Initialize a Git repository and add `index.js` to it with the following Bash commands: + +
      +
      git init
      +git add index.js
      +git commit -m "Initial commit"
      +
      +
      + +
    5. Then, create a function in the Appwrite console and add your Git repository as the remote source.
    6. +
    7. Finally, execute the function and visit the URL (like `ghrfu9ewji.functions.appwrite.app`) to see the response.
    8. +
    + +

    Section 2: Let's Use Payload

    + +

    Updating the function

    + +
      +
    1. Update `index.js` to use `req.query.amount` to calculate the conversion. The following code takes the amount in Euros from the URL and converts it to Dollars using a static conversion rate. + +
      +
      module.exports = async function ({ req, res }) {
      +  const amountInEuros = Number(req.query.amount);
      +  const amountInDollars = amountInEuros * 1.13;
      +  return res.end(amountInDollars.toString());
      +};
      +
      + +
    2. Commit your changes and push them to your Git repository.
    3. +
    + +

    Testing the function

    + +
      +
    1. Once the function is updated, you can test it by visiting the URL and providing different amounts to convert in the query string. For example, `ghrfu9ewji.functions.appwrite.app?amount=5` should return `5.65`.
    2. +
    + +

    Section 3: Installing Dependencies

    + +

    Preparing for dependencies

    + +
      +
    1. Run `npm init --yes` to create a `package.json` file. This file is used to manage your Node.js project's dependencies.
    2. +
    3. Install the `undici` library with `npm install undici`. This library includes a `fetch` function that you can use to make HTTP requests.
    4. +
    + +

    Updating the function

    + +
      +
    1. Update `index.js` to use `fetch` from `undici` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars. + +
      +
      const { fetch } = require('undici');
      +
      +module.exports = async function ({ req, res }) {
      +  const amountInEuros = Number(req.query.amount);
      +  const response = await fetch('https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/eur/usd.json');
      +  const data = await response.json();
      +  const amountInDollars = amountInEuros * data.usd;
      +  return res.end(amountInDollars.toString());
      +};
      +
      + +
    2. Commit your changes and push them to your Git repository.
    3. +
    + +

    Testing the function

    + +
      +
    1. After your function has updated, you can test it by visiting the URL and providing different amounts to convert in the query string. The conversion rate should now be more precise because we're using the current conversion rate.
    2. +
    + +

    Section 4: More Routes

    + +

    Updating the function

    + +
      +
    1. Edit `index.js` to support multiple paths like `/eur`, `/czk`, `/ft`, `/rub`. Each path will convert from that currency to dollars. Note that you will need to update the fetch URL for each new path.
    2. +
    3. Commit your changes and push them to your Git repository.
    4. +
    + +

    Testing the function

    + +
      +
    1. After your function has updated, you can try out the new paths. For example, `ghrfu9ewji.functions.appwrite.app/eur?amount=5` should convert Euros to Dollars, while `ghrfu9ewji.functions.appwrite.app/czk?amount=100` should convert Czech Koruna to Dollars.
    2. +
    + +

    Congratulations! You've built a powerful currency conversion function using Appwrite!

    + +[TODO: @luke -> translate code for other runtimes] + +[TODO: @matej @luke -> Example with JWT, show both client and server code] \ No newline at end of file From 57411541ebe90f1642a21ec4f3e49617cfea96f0 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 1 Aug 2023 18:54:59 +0000 Subject: [PATCH 17/80] added todo comments for develop page --- app/views/docs/functions-develop.phtml | 53 +++++++++++++++++++++----- app/views/docs/index.phtml | 2 +- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 28ee83399..803ce3f36 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -59,7 +59,7 @@

    Some lamguages support unpacking. You'll see us use unpacking in examples, which has the following syntax.

    -[TODO: Example below for relevant languages.] +[TODO: @luke for languages that have unpacking, let's add unpacking examples!]
    • Node.js

      @@ -181,6 +181,8 @@ Explore the request object with the following function, which logs all request params to the Appwrite Console.

      +[TODO: @luke Let's make sure we show an example for evert runtime with good string manip methods.] +
      • Node.js

        @@ -313,6 +315,8 @@ The response information will not be logged to the Appwrite Console. There are several possible ways to send a response, explore them in the following Appwrite Function.

        + +[TODO: @luke Let's make sure we show an example for evert runtime with good string manip methods.]
        • Node.js

          @@ -464,6 +468,7 @@

          Here's an example of using logs and errors.

          +[TODO: @luke Let's make sure we show an example for evert runtime with good string manip methods.]
          • Node.js

            @@ -581,21 +586,54 @@

          You can access these logs through the following steps.

          -[TODO] +
            +
          1. In Appwrite Console, navigate to Functions.
          2. +
          3. Click to open a function you wish to inspect.
          4. +
          5. Under the Executions tab, click on an execution.
          6. +
          7. In the Response section, you'll be able to view logs under the Logs and Errors tabs.
          8. +
          +

          Accessing Environment Variables

          If you need to pass constants or secrets to Appwrite Functions, you can use environment variables. + Environmental variables can be global, or function specific. +

          + +

          Local Environment Variables

          +

          + Local variables will only be accessible in the function they belong to. + Local variables will override global variables when they have conflicting names. +

          +
            +
          1. In Appwrite Console, navigate to Functions.
          2. +
          3. Click to open a function you wish to add variables to.
          4. +
          5. Under the Settings tab, navigate to Environment variables.
          6. +
          7. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
          8. +
          + +

          Global Variables

          +

          + Global variables are accessible to all Appwrite Functions. + Local variables will override global variables when they have conflicting names.

          -[TODO: ways to setup environment variables (link to deployment docs?)] +
            +
          1. In Appwrite Console, navigate to your project's Settings page.
          2. +
          3. Navigate to Global variables section.
          4. +
          5. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
          6. +

          You can access the environment variables through the systems library of each language.

          +[TODO: @luke show how you access environment varibles for every language (sorry for the pain!)] +

          Depencies

          +[TODO: @luke is this different for every language? Idk what to put here, do we need to also @matej?] +

          Using Appwrite in a Function

          -[TODO: 2 examples -> JWT and API keys] +[TODO: @luke @matej 2 examples -> JWT and API keys]

          Quick Example

            @@ -713,6 +751,7 @@

          Using Multiple Files

          +[TODO: @luke show how you can use multiple files in the same project]

          Recipes

          @@ -722,8 +761,4 @@

          Explore examples and recipes -

          - - -

          Limitations

          -

          Libraries dependent on compiled binaries not available on the executors cannot be installed.

          \ No newline at end of file +

          \ No newline at end of file diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 5f41f6647..44007ad43 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -100,7 +100,7 @@ $cols = [
        •    Deploy
        •    Execute
        •    Runtimes
        • -
        •    Debugging
        • +
        •    Recipes
      From 29fc3c15d9a80f6cfcda58f546d5f0a8ab543d32 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 1 Aug 2023 19:09:49 +0000 Subject: [PATCH 18/80] Fix up deployment docs --- app/views/docs/functions-deploy.phtml | 14 +++++++++++--- app/views/docs/index.phtml | 10 +++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index c5b92746a..fb9791ca2 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -66,11 +66,19 @@
    • Debugging Builds

      -- Check logs and errors -- Redeploy button for non-code issues [TODO: @matej write an example] +

      After deploying a function, you can find the status of the deployment and build logs in the Appwrite Console.

      +
        +
      1. In Appwrite Console, navigate to Functions.
      2. +
      3. Click to open a function you wish to inspect.
      4. +
      5. Under the Deployments tab, you'll find the status of the current active deployment and previous inactive deployments.
      6. +
      7. You can access build logs for the active deployment by clicking the Build logs button. You can click on an inactive function's three dots button to find their build logs.
      8. +

      CLI

      -- Setup cli +
      +

      CLI Setup

      +

      Before you can deploy with the Appwrite CLI, make sure you've installed and initialized the CLI

      +

      To deploy with the Appwrite CLI, your function's folder structure must be configured in a specific way. The folder path is then added to a config file called appwrite.json that tells the CLI where each function is stored. diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index 44007ad43..d82d95a21 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -96,11 +96,11 @@ $cols = [ Functions

    From 31c7c44813afa7fd43a4e8628a0a1985ea061a49 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 1 Aug 2023 19:24:05 +0000 Subject: [PATCH 19/80] Add comments and todo tags for all pages in Functions --- app/views/docs/functions-execute.phtml | 16 ++++++++++++++-- app/views/docs/functions-recipes.phtml | 20 +++++++++++++++++++- app/views/docs/functions-runtimes.phtml | 2 +- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 85476f0c0..387ca1dc3 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -14,15 +14,26 @@

    SDK

    -TODO: two examples running the function sync and async form the SDK +[TODO: @luke @matej, two examples running the function sync and async form the SDK]

    Events

    - TODO: Describe steps to navigate the UI + Changes in Appwrite emit events. + You can configure Functions to be executed in response to these events.

    +
      +
    1. In Appwrite Console, navigate to Functions.
    2. +
    3. Click to open a function you wish to add variables to.
    4. +
    5. Under the Settings tab, navigate to Events.
    6. +
    7. Add one or multiple events as triggers for the function.
    8. +
    9. + Be careful to avoid selecting events that can be caused by the function itself. + This can cause the function to trigger it's own execution, resulting in infinite recursions. +
    10. +

    Schedule

    @@ -70,3 +81,4 @@ TODO: two examples running the function sync and async form the SDK

    Logs and results

    +[TODO: @matej, decide what needs to go here.] diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index 230b58619..33f6c0834 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -110,4 +110,22 @@ module.exports = async function ({ req, res }) { [TODO: @luke -> translate code for other runtimes] -[TODO: @matej @luke -> Example with JWT, show both client and server code] \ No newline at end of file +[TODO: @matej @luke -> Example with JWT, show both client and server code] + + + + \ No newline at end of file diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 4e56f2812..0a1e057e9 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -40,4 +40,4 @@ $runtimes = $this->getParam('runtimes', []); -[TODO: Label which ones are cloud only] +[TODO: @matej Label which ones are cloud only, idk how to do cleanly] From d9c2d5d833ec38c0145fe7d48e791b212beeca08 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 2 Aug 2023 21:45:58 +0000 Subject: [PATCH 20/80] Add clarification that you need to run deploy command next to appwrite.json --- app/views/docs/command-line-deployment.phtml | 14 ++++++++++---- app/views/docs/functions-deploy.phtml | 9 ++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/views/docs/command-line-deployment.phtml b/app/views/docs/command-line-deployment.phtml index 79487ed3f..a0903c6a6 100644 --- a/app/views/docs/command-line-deployment.phtml +++ b/app/views/docs/command-line-deployment.phtml @@ -27,7 +27,7 @@ The Apprite CLI allows you to create and deploy databases, collections, buckets,

    Deploying Appwrite Functions

    -

    The CLI also handles the creation and deployment of Appwrite Functions. You can initialize a new function using:

    +

    The CLI also handles the creation and deployment of Appwrite Functions. Run this command in the folder holding your appwrite.json file.

    appwrite init function
    @@ -36,7 +36,9 @@ The Apprite CLI allows you to create and deploy databases, collections, buckets,
     ✓ Success
    -

    This command creates a new function My Awesome Function in your current Appwrite project and also creates a template function for you to get started. You can now deploy this function using:

    +

    This command creates a new function My Awesome Function in your current Appwrite project and also creates a template function for you to get started.

    + +

    You can now deploy this function by running this command in the folder holding your appwrite.json file.

    appwrite deploy function
    @@ -47,7 +49,9 @@ The Apprite CLI allows you to create and deploy databases, collections, buckets,
     
     

    Deploying Databases and Collections

    -

    The Appwrite CLI also helps you migrate your project's databases and collections from a development server to a production server. You can deploy all the databases and collections in your appwrite.json file using:

    +

    The Appwrite CLI also helps you migrate your project's databases and collections from a development server to a production server.

    + +

    You can deploy all the databases and collections in your appwrite.json file by running this command in the folder holding your appwrite.json file.

    appwrite deploy collection
    @@ -57,15 +61,17 @@ The Apprite CLI allows you to create and deploy databases, collections, buckets,

    The Appwrite CLI can create teams to organize users. Teams can be used to grant access permissions to a group of users. Learn more about permissions.

    +

    Deploy teams by running this command in the folder holding your appwrite.json file.

    appwrite deploy team
    -

    Deploying Storage Buckets

    The Appwrite CLI allows you to configure and deploy buckets across projects. All the bucket's settings are available through the appwrite.json file.

    +

    Deploy storage buckets by running this command in the folder holding your appwrite.json file.

    +
    appwrite deploy bucket
    diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index fb9791ca2..bebd906f0 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -80,9 +80,12 @@

    Before you can deploy with the Appwrite CLI, make sure you've installed and initialized the CLI

    - To deploy with the Appwrite CLI, your function's folder structure must be configured in a specific way. - The folder path is then added to a config file called appwrite.json that tells the CLI where each function is stored. - To ensure the folder structure is setup correctly and appwrite.json is configured correctly, use the appwrite init function method to create a shell function. + To deploy with the Appwrite CLI, your function must be added to appwrite.json that tells the CLI where each function is stored. + To ensure the folder structure is setup correctly and appwrite.json is configured correctly, use the appwrite init function method to create a shell function, then paste in your function code. +

    + +

    + Run the following command in the folder holding the appwrite.json file.

    appwrite init function
    From 2296a8b69b1d9936466be531c538fe691039d594 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:23:53 +0100 Subject: [PATCH 21/80] feat: port functions recipes --- .vscode/settings.json | 9 + app/views/docs/functions-recipes.phtml | 280 ++++++++++++------ composer.lock | 374 ++++++++++++++++--------- 3 files changed, 437 insertions(+), 226 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..8e639cda8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.formatOnSave": false, + "[html][phtml]": { + "editor.formatOnSave": false + }, + "[php]": { + "editor.formatOnSave": false + } +} diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index 230b58619..75287d4d4 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -1,111 +1,213 @@

    - Appwrite Functions is all about flexibility. - Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. - Take a look at the following. + Appwrite Functions is all about flexibility. + Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. + Take a look at the following.

    [TODO: @matej @luke -> Let's have some simple recipes here for common actions] -

    Section 1: My First Function

    +

    Getting Started

    -

    Creating the function

    - -
      -
    1. Create a new file, `index.js`.
    2. -
    3. Add the following code to `index.js`. This code will return 1.13 when the function is called, because 1€ equals approximately 1.13$. +

      + In this recipe, we'll build a simple function for currency conversion. +

      -
      -
      module.exports = async function ({ res }) {
      -  return res.end('1.13');
      +
        +
      • +

        Node.js

        +
        +

        Create a new file, `index.js`.

        +

        Add the following code to `index.js`.

        +
        +
        export default async function ({ res }) {
        +  return res.send('1.13');
         };
        -
        - -
      • Initialize a Git repository and add `index.js` to it with the following Bash commands: - -
        -
        git init
        -git add index.js
        -git commit -m "Initial commit"
        -
        -
        - -
      • Then, create a function in the Appwrite console and add your Git repository as the remote source.
      • -
      • Finally, execute the function and visit the URL (like `ghrfu9ewji.functions.appwrite.app`) to see the response.
      • -
    - -

    Section 2: Let's Use Payload

    - -

    Updating the function

    - -
      -
    1. Update `index.js` to use `req.query.amount` to calculate the conversion. The following code takes the amount in Euros from the URL and converts it to Dollars using a static conversion rate. - -
      -
      module.exports = async function ({ req, res }) {
      +      
      +
    + +
  • +

    PHP

    +
    +

    Create a new file, `index.php`.

    +

    Add the following code to `index.php`.

    +
    +
    <?php
    +return function ($context) {
    +  return $context->res->send('1.13');
    +};
    +
    +
    +
  • +
  • +

    Python

    +
    +

    Create a new file, `index.py`.

    +

    Add the following code to `index.py`.

    +
    +
    def main(context):
    +  return context.res.send('1.13')
    +
    +
    +
  • +
  • +

    Dart

    +
    +

    Create a new file, `index.dart`.

    +

    Add the following code to `index.dart`.

    +
    +
    import 'dart:async';
    +
    +Future<dynamic> main(final context) async {
    +  return context.res.send('1.13');
    +}
    +
    +
    +
  • +
  • +

    Ruby

    +
    +

    Create a new file, `index.rb`.

    +

    Add the following code to `index.rb`.

    +
    +
    def main(context)
    +  return context.res.send('1.13')
    +end
    +
    +
    +
  • + + +

    This code will return `1.13` when the function is called, because 1€ equals approximately 1.13$.

    +

    Now, create a function in the Appwrite console, adding your Git repository as the remote source and using the path file you created as the entry point.

    +
  • Finally, execute the function and visit the URL (like `ghrfu9ewji.functions.appwrite.app`) to see the response.
  • + +

    Currency Conversion

    + +

    Now, let's update the function to use the request payload.

    +

    You can use a query string to pass data to your function. For example, `ghrfu9ewji.functions.appwrite.app?amount=5` will pass `5` as the `amount` parameter.

    + +
      +
    • +

      Node.js

      +
      +

      Update `index.js` to use `req.query.amount` to access the `amount` parameter, and return the conversion result.

      +
      +
      export default async function ({ req, res }) {
         const amountInEuros = Number(req.query.amount);
         const amountInDollars = amountInEuros * 1.13;
      -  return res.end(amountInDollars.toString());
      +  return res.send(amountInDollars.toString());
       };
      -
      - -
    • Commit your changes and push them to your Git repository.
    • - - -

      Testing the function

      - -
        -
      1. Once the function is updated, you can test it by visiting the URL and providing different amounts to convert in the query string. For example, `ghrfu9ewji.functions.appwrite.app?amount=5` should return `5.65`.
      2. -
      - -

      Section 3: Installing Dependencies

      - -

      Preparing for dependencies

      +
    + + +
  • +

    PHP

    +
    +

    Update `index.php` to use `$context->req->query['amount']` to access the `amount` parameter, and return the conversion result.

    +
    +
    <?php
    +return function ($context) {
    +  $amountInEuros = $context->req->query['amount'];
    +  $amountInDollars = $amountInEuros * 1.13;
    +  return $context->res->send($amountInDollars);
    +};
    +
    +
    +
  • + -
      -
    1. Run `npm init --yes` to create a `package.json` file. This file is used to manage your Node.js project's dependencies.
    2. -
    3. Install the `undici` library with `npm install undici`. This library includes a `fetch` function that you can use to make HTTP requests.
    4. -
    +

    Commit your changes and push them to your Git repository.

    -

    Updating the function

    -
      -
    1. Update `index.js` to use `fetch` from `undici` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars. +

      Testing the function

      -
      -
      const { fetch } = require('undici');
      +

      + Execute the function and visit the URL (like `ghrfu9ewji.functions.appwrite.app?amount=5`) to see the response. +

      +

      + You should see the result of the conversion, like `5.65`. +

      -module.exports = async function ({ req, res }) { +

      Adding Dependencies

      + +
        +
      • +

        Node.js

        +
        +

        Run the following bash command to create a `package.json` file. This file is used to manage your Node.js project's dependencies.

        +
        +
        npm init -y
        +
        +

        Install the `undici` library. This library includes a `fetch` function that you can use to make HTTP requests.

        +
        +
        npm install undici
        +
        +

        Finally, we need to add `npm install` to your function's build commands in the Appwrite console.

        +
        +
      • +
      + +

      Using Dependencies

      + +
        +
      • +

        Node.js

        +
        +

        Use `fetch` from `undici` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

        +
        +
        import { fetch } from 'undici';
        +
        +export default async function ({ req, res }) {
           const amountInEuros = Number(req.query.amount);
        -  const response = await fetch('https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/eur/usd.json');
        +  const response = await fetch('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
           const data = await response.json();
        -  const amountInDollars = amountInEuros * data.usd;
        -  return res.end(amountInDollars.toString());
        +  const amountInDollars = amountInEuros * data.rates.USD;
        +  return res.send(amountInDollars.toString());
         };
        -
        - -
      • Commit your changes and push them to your Git repository.
      • -
    - -

    Testing the function

    - -
      -
    1. After your function has updated, you can test it by visiting the URL and providing different amounts to convert in the query string. The conversion rate should now be more precise because we're using the current conversion rate.
    2. -
    - -

    Section 4: More Routes

    - -

    Updating the function

    - -
      -
    1. Edit `index.js` to support multiple paths like `/eur`, `/czk`, `/ft`, `/rub`. Each path will convert from that currency to dollars. Note that you will need to update the fetch URL for each new path.
    2. -
    3. Commit your changes and push them to your Git repository.
    4. -
    - -

    Testing the function

    - -
      -
    1. After your function has updated, you can try out the new paths. For example, `ghrfu9ewji.functions.appwrite.app/eur?amount=5` should convert Euros to Dollars, while `ghrfu9ewji.functions.appwrite.app/czk?amount=100` should convert Czech Koruna to Dollars.
    2. -
    + + + + + +

    Deploy your changes

    +

    After your function has updated, you can test it by visiting the URL and providing different amounts to convert in the query string. The conversion rate should now be more precise because we're using the current conversion rate.

    + +

    Adding More Routes

    + +

    Let's add support multiple paths like `/eur` and `/inr`. Each path will convert from that currency to dollars.

    + +
      +
    • +

      Node.js

      +
      +

      +

      +
      import { fetch } from 'undici';
      +
      +export default async function ({ req, res }) {
      +  if (req.path === '/eur') {
      +    const amountInEuros = Number(req.query.amount);
      +    const response = await fetch('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
      +    const data = await response.json();
      +    const amountInDollars = amountInEuros * data.rates.USD;
      +    return res.send(amountInDollars.toString());
      +  }
      +
      +  if (req.path === '/inr') {
      +    const amountInRupees = Number(req.query.amount);
      +    const response = await fetch('https://api.exchangerate.host/latest?base=INR&symbols=USD');
      +    const data = await response.json();
      +    const amountInDollars = amountInRupees * data.rates.USD;
      +    return res.send(amountInDollars.toString());
      +  }
      +
      +  return res.send('Invalid path');
      +};
      +
      +
      +
    • +
    +

    After your function has updated, you can try out the new paths. For example, `ghrfu9ewji.functions.appwrite.app/eur?amount=5` should convert Euros to Dollars, while `ghrfu9ewji.functions.appwrite.app/inr?amount=100` should convert Indian Rupees to Dollars.

    Congratulations! You've built a powerful currency conversion function using Appwrite!

    [TODO: @luke -> translate code for other runtimes] diff --git a/composer.lock b/composer.lock index 9c76a9193..f84377e3e 100644 --- a/composer.lock +++ b/composer.lock @@ -58,24 +58,24 @@ }, { "name": "utopia-php/framework", - "version": "0.19.21", + "version": "0.19.1", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "3b7bd8e4acf84fd7d560ced8e0142221d302575d" + "reference": "cc7629b5f7a8f45912ec2e069b7f14e361e41c34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/3b7bd8e4acf84fd7d560ced8e0142221d302575d", - "reference": "3b7bd8e4acf84fd7d560ced8e0142221d302575d", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/cc7629b5f7a8f45912ec2e069b7f14e361e41c34", + "reference": "cc7629b5f7a8f45912ec2e069b7f14e361e41c34", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=7.3.0" }, "require-dev": { - "phpunit/phpunit": "^9.5.10", - "vimeo/psalm": "4.13.1" + "phpunit/phpunit": "^9.4", + "vimeo/psalm": "4.0.1" }, "type": "library", "autoload": { @@ -101,24 +101,24 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.19.21" + "source": "https://github.com/utopia-php/framework/tree/0.19.1" }, - "time": "2022-05-12T18:42:28+00:00" + "time": "2021-11-25T16:11:40+00:00" } ], "packages-dev": [ { "name": "amphp/amp", - "version": "dev-master", + "version": "2.x-dev", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + "reference": "c5ea79065f98f93f7b16a4d5a504fe5d69451447" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "url": "https://api.github.com/repos/amphp/amp/zipball/c5ea79065f98f93f7b16a4d5a504fe5d69451447", + "reference": "c5ea79065f98f93f7b16a4d5a504fe5d69451447", "shasum": "" }, "require": { @@ -133,7 +133,6 @@ "psalm/phar": "^3.11@dev", "react/promise": "^2" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -187,7 +186,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" + "source": "https://github.com/amphp/amp/tree/master" }, "funding": [ { @@ -195,11 +194,11 @@ "type": "github" } ], - "time": "2022-02-20T17:52:18+00:00" + "time": "2022-08-21T11:55:21+00:00" }, { "name": "amphp/byte-stream", - "version": "dev-master", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", @@ -223,7 +222,6 @@ "phpunit/phpunit": "^6 || ^7 || ^8", "psalm/phar": "^3.11.4" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -355,12 +353,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + "reference": "fa1ec24f0ab1efe642671ec15c51a3ab879f59bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "url": "https://api.github.com/repos/composer/semver/zipball/fa1ec24f0ab1efe642671ec15c51a3ab879f59bf", + "reference": "fa1ec24f0ab1efe642671ec15c51a3ab879f59bf", "shasum": "" }, "require": { @@ -411,9 +409,9 @@ "versioning" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" + "source": "https://github.com/composer/semver/tree/main" }, "funding": [ { @@ -429,7 +427,7 @@ "type": "tidelift" } ], - "time": "2022-04-01T19:23:25+00:00" + "time": "2023-01-13T15:47:53+00:00" }, { "name": "composer/xdebug-handler", @@ -532,6 +530,54 @@ }, "time": "2019-12-04T15:06:13+00:00" }, + { + "name": "doctrine/deprecations", + "version": "1.1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "bdaa697ed9c7f5ee2f7d3b5f9c2a6f2519523cd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/bdaa697ed9c7f5ee2f7d3b5f9c2a6f2519523cd9", + "reference": "bdaa697ed9c7f5ee2f7d3b5f9c2a6f2519523cd9", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.x" + }, + "time": "2023-07-29T16:12:19+00:00" + }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -691,12 +737,12 @@ "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "2d589921f23d869846a52c541247e0bafca61ab3" + "reference": "cfc54e30a4f5e5af5f9d9ce86697cfcc5f7e7c24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2d589921f23d869846a52c541247e0bafca61ab3", - "reference": "2d589921f23d869846a52c541247e0bafca61ab3", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/cfc54e30a4f5e5af5f9d9ce86697cfcc5f7e7c24", + "reference": "cfc54e30a4f5e5af5f9d9ce86697cfcc5f7e7c24", "shasum": "" }, "require": { @@ -707,6 +753,7 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, + "default-branch": true, "bin": [ "bin/php-parse" ], @@ -739,7 +786,7 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/4.x" }, - "time": "2022-06-04T10:44:36+00:00" + "time": "2023-07-30T21:38:32+00:00" }, { "name": "openlss/lib-array2xml", @@ -853,24 +900,30 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9455bde915e322a823d464a2c41e5c0de03512a6" + "reference": "7b217217725dc991a0ae7b995041cee1d5019561" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9455bde915e322a823d464a2c41e5c0de03512a6", - "reference": "9455bde915e322a823d464a2c41e5c0de03512a6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7b217217725dc991a0ae7b995041cee1d5019561", + "reference": "7b217217725dc991a0ae7b995041cee1d5019561", "shasum": "" }, "require": { "ext-filter": "*", "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "1.x-dev@dev", + "phpstan/phpdoc-parser": "^1.7", "webmozart/assert": "^1.9.1" }, "require-dev": { "mockery/mockery": "~1.3.5", - "psalm/phar": "^4.8" + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.26" }, "default-branch": true, "type": "library", @@ -903,7 +956,7 @@ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" }, - "time": "2022-04-02T20:16:01+00:00" + "time": "2023-03-12T10:50:44+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -911,21 +964,29 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" + "reference": "07100e65d09fd50608d649fc656cae1c921a2495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/07100e65d09fd50608d649fc656cae1c921a2495", + "reference": "07100e65d09fd50608d649fc656cae1c921a2495", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "default-branch": true, "type": "library", @@ -952,34 +1013,76 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2022-03-15T21:29:03+00:00" + "time": "2023-07-20T19:57:33+00:00" }, { - "name": "psr/container", - "version": "dev-master", + "name": "phpstan/phpdoc-parser", + "version": "1.23.x-dev", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "4a1ab8e11e9957f9cc9f89f87a7c912489f08119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/4a1ab8e11e9957f9cc9f89f87a7c912489f08119", + "reference": "4a1ab8e11e9957f9cc9f89f87a7c912489f08119", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" }, "default-branch": true, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] } }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.x" + }, + "time": "2023-07-24T11:53:35+00:00" + }, + { + "name": "psr/container", + "version": "1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1006,9 +1109,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "source": "https://github.com/php-fig/container/tree/1.1.2" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2021-11-05T16:50:12+00:00" }, { "name": "psr/log", @@ -1062,16 +1165,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1116,7 +1219,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0" }, "funding": [ { @@ -1124,7 +1227,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "symfony/console", @@ -1132,12 +1235,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "09a5561288c73b1ac9aaa00d64ebe6a6782a6a3b" + "reference": "b504a3d266ad2bb632f196c0936ef2af5ff6e273" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/09a5561288c73b1ac9aaa00d64ebe6a6782a6a3b", - "reference": "09a5561288c73b1ac9aaa00d64ebe6a6782a6a3b", + "url": "https://api.github.com/repos/symfony/console/zipball/b504a3d266ad2bb632f196c0936ef2af5ff6e273", + "reference": "b504a3d266ad2bb632f196c0936ef2af5ff6e273", "shasum": "" }, "require": { @@ -1202,7 +1305,7 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], @@ -1223,30 +1326,29 @@ "type": "tidelift" } ], - "time": "2022-06-27T16:58:25+00:00" + "time": "2023-07-19T20:11:33+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "dev-main", + "version": "2.5.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "4912000e79dc2d6df029d35d8755be1ed79b6691" + "reference": "80d075412b557d41002320b96a096ca65aa2c98d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/4912000e79dc2d6df029d35d8755be1ed79b6691", - "reference": "4912000e79dc2d6df029d35d8755be1ed79b6691", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.1" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1275,7 +1377,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/main" + "source": "https://github.com/symfony/deprecation-contracts/tree/2.5" }, "funding": [ { @@ -1291,7 +1393,7 @@ "type": "tidelift" } ], - "time": "2022-05-20T13:56:22+00:00" + "time": "2023-01-24T14:02:46+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1299,12 +1401,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -1320,7 +1422,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1358,7 +1460,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/main" }, "funding": [ { @@ -1374,7 +1476,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -1382,12 +1484,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -1400,7 +1502,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1440,7 +1542,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/main" }, "funding": [ { @@ -1456,7 +1558,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -1464,12 +1566,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -1482,7 +1584,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1525,7 +1627,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/main" }, "funding": [ { @@ -1541,7 +1643,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -1549,12 +1651,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -1570,7 +1672,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1609,7 +1711,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/main" }, "funding": [ { @@ -1625,7 +1727,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php73", @@ -1633,12 +1735,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", "shasum": "" }, "require": { @@ -1648,7 +1750,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1689,7 +1791,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php73/tree/main" }, "funding": [ { @@ -1705,7 +1807,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php80", @@ -1713,12 +1815,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -1728,7 +1830,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1773,7 +1875,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php80/tree/main" }, "funding": [ { @@ -1789,25 +1891,26 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/service-contracts", - "version": "dev-main", + "version": "2.5.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "8336f2b06febd99d6309550ccdf4ca4cd054e73a" + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/8336f2b06febd99d6309550ccdf4ca4cd054e73a", - "reference": "8336f2b06febd99d6309550ccdf4ca4cd054e73a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^2.0" + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -1815,11 +1918,10 @@ "suggest": { "symfony/service-implementation": "" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1829,10 +1931,7 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1859,7 +1958,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/main" + "source": "https://github.com/symfony/service-contracts/tree/2.5" }, "funding": [ { @@ -1875,37 +1974,38 @@ "type": "tidelift" } ], - "time": "2022-05-20T13:56:22+00:00" + "time": "2023-04-21T15:04:16+00:00" }, { "name": "symfony/string", - "version": "6.2.x-dev", + "version": "5.4.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "9c4a9a2eacc3edb31ec95c4fb6b189d28406537e" + "reference": "1181fe9270e373537475e826873b5867b863883c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/9c4a9a2eacc3edb31ec95c4fb6b189d28406537e", - "reference": "9c4a9a2eacc3edb31ec95c4fb6b189d28406537e", + "url": "https://api.github.com/repos/symfony/string/zipball/1181fe9270e373537475e826873b5867b863883c", + "reference": "1181fe9270e373537475e826873b5867b863883c", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": ">=3.0" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" }, "type": "library", "autoload": { @@ -1944,7 +2044,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/6.2" + "source": "https://github.com/symfony/string/tree/5.4" }, "funding": [ { @@ -1960,7 +2060,7 @@ "type": "tidelift" } ], - "time": "2022-06-26T16:35:21+00:00" + "time": "2023-06-28T12:46:07+00:00" }, { "name": "vimeo/psalm", From 661781125a1ce90c878f6d333d69f3924a34d2d5 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 7 Aug 2023 13:56:41 +0100 Subject: [PATCH 22/80] =?UTF-8?q?feat:=20more=20snippet=20translations=20?= =?UTF-8?q?=F0=9F=98=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/docs/functions-recipes.phtml | 283 ++++++++++++++++++++++++- 1 file changed, 282 insertions(+), 1 deletion(-) diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index 3ab4bbd0a..b5cb305a7 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -113,6 +113,46 @@ return function ($context) { +
  • +

    Python

    +
    +

    Update `index.py` to use `context.req.query['amount']` to access the `amount` parameter, and return the conversion result.

    +
    +
    def main(context):
    +  amountInEuros = context.req.query['amount']
    +  amountInDollars = amountInEuros * 1.13
    +  return context.res.send(amountInDollars)
    +
    +
    +
  • +
  • +

    Dart

    +
    +

    Update `index.dart` to use `context.req.query['amount']` to access the `amount` parameter, and return the conversion result.

    +
    +
    import 'dart:async';
    +
    +Future<dynamic> main(final context) async {
    +  final amountInEuros = context.req.query['amount'];
    +  final amountInDollars = amountInEuros * 1.13;
    +  return context.res.send(amountInDollars);
    +}
    +
    +
    +
  • +
  • +

    Ruby

    +
    +

    Update `index.rb` to use `context.req.query['amount']` to access the `amount` parameter, and return the conversion result.

    +
    +
    def main(context)
    +  amountInEuros = context.req.query['amount']
    +  amountInDollars = amountInEuros * 1.13
    +  return context.res.send(amountInDollars)
    +end
    +
    +
    +
  • Commit your changes and push them to your Git repository.

    @@ -141,7 +181,89 @@ return function ($context) {
    npm install undici
    -

    Finally, we need to add `npm install` to your function's build commands in the Appwrite console.

    +

    Finally, add `npm install` to your function's build commands in the Appwrite console.

    + + +
  • +

    PHP

    +
    +

    You can use Composer to manage your PHP project's dependencies. Install it from getcomposer.org/download.

    +

    Run the following bash command to create a `composer.json` file. This file is used to manage your PHP project's dependencies.

    +
    +
    composer init -y
    +
    +

    Install the `guzzlehttp/guzzle` library. This library includes a `get` function that you can use to make HTTP requests.

    +
    +
    composer require guzzlehttp/guzzle
    +
    +

    Finally, add `composer install` to your function's build commands in the Appwrite console.

    +
    +
  • +
  • +

    Python

    +
    +

    Run the following bash command to create a `requirements.txt` file. This file is used to manage your Python project's dependencies.

    +
    +
    touch requirements.txt
    +
    +

    Install the `requests` library. This library includes a `get` function that you can use to make HTTP requests.

    +
    +
    echo "requests" >> requirements.txt
    +pip -r requirements.txt
    +
    +

    Finally, add `pip install -r requirements.txt` to your function's build commands in the Appwrite console.

    +
    +
  • +
  • +

    + Dart +

    +
    +

    + Create a `pubspec.yaml` file with the following contents. This file is used to manage your Dart project's dependencies. +

    +
    +
    name: appwrite_function
    +description: Appwrite Function
    +version: 1.0.0
    +environment:
    +  sdk: '>=2.12.0 <3.0.0'
    +
    +
    +

    + Install the `http` library. This library includes a `get` function that you can use to make HTTP requests. +

    +
    +
    
    +          pub install http
    +      
    +

    + Finally, add `pub get` to your function's build commands in the Appwrite console. +

    +
    +
  • +
  • +

    + Ruby +

    +
    +

    + Create a `Gemfile` file with the following contents. This file is used to manage your Ruby project's dependencies. +

    +
    +
    source 'https://rubygems.org'
    +        
    +
    +

    + Install the `httparty` library. This library includes a `get` function that you can use to make HTTP requests. +

    +
    +
    echo "gem 'httparty'" >> Gemfile
    +bundle install
    +
    +

    + Finally, add `bundle install` to your function's build commands in the Appwrite console. +

  • @@ -166,6 +288,79 @@ export default async function ({ req, res }) { +
  • +

    PHP

    +
    +

    +

    +
    <?php
    +
    +require(__DIR__ . '/../vendor/autoload.php');
    +
    +use GuzzleHttp\Client;
    +
    +return function ($context) {
    +  $amountInEuros = $context->getRequest()->getQuery('amount');
    +  $client = new Client();
    +  $response = $client->get('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
    +  $data = json_decode($response->getBody(), true);
    +  $amountInDollars = $amountInEuros * $data['rates']['USD'];
    +  return $context->res->send(strval($amountInDollars));
    +};
    +
    +
    +
  • +
  • +

    Python

    +
    +

    Use `get` from `requests` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

    +
    +
    import requests
    +
    +def main(context):
    +  amount_in_euros = float(context.req.query['amount'])
    +  response = requests.get('https://api.exchangerate.host/latest?base=EUR&symbols=USD')
    +  data = response.json()
    +  amount_in_dollars = amount_in_euros * data['rates']['USD']
    +  return context.res.send(str(amount_in_dollars))
    +
    +
    +
  • +
  • +

    Dart

    +
    +

    Use `get` from `http` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

    +
    +
    import 'dart:async';
    +import 'package:http/http.dart' as http;
    +
    +Future<dynamic> main(final context) async {
    +  final amountInEuros = double.parse(context.req.query['amount'])
    +  final response = await http.get(Uri.parse('https://api.exchangerate.host/latest?base=EUR&symbols=USD'));
    +  final data = json.decode(response.body);
    +  final amountInDollars = amountInEuros * data['rates']['USD'];
    +  return context.res.send(amountInDollars.toString());
    +}
    +
    +
    +
  • +
  • +

    Ruby

    +
    +

    Use `get` from `httparty` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

    +
    +
    require 'httparty'
    +
    +def main(context)
    +  amount_in_euros = context.req.query['amount'].to_f
    +  response = HTTParty.get('https://api.exchangerate.host/latest?base=EUR&symbols=USD')
    +  data = JSON.parse(response.body)
    +  amount_in_dollars = amount_in_euros * data['rates']['USD']
    +  return context.res.send(amount_in_dollars.to_s)
    +end
    +
    +
    +
  • Deploy your changes

    @@ -205,6 +400,92 @@ export default async function ({ req, res }) { +
  • +

    Python

    +
    +

    +

    +
    import requests
    +
    +def main(context):
    +  if context.req.path == '/eur':
    +    amount_in_euros = float(context.req.query['amount'])
    +    response = requests.get('https://api.exchangerate.host/latest?base=EUR&symbols=USD')
    +    data = response.json()
    +    amount_in_dollars = amount_in_euros * data['rates']['USD']
    +    return context.res.send(str(amount_in_dollars))
    +
    +  if context.req.path == '/inr':
    +    amount_in_rupees = float(context.req.query['amount'])
    +    response = requests.get('https://api.exchangerate.host/latest?base=INR&symbols=USD')
    +    data = response.json()
    +    amount_in_dollars = amount_in_rupees * data['rates']['USD']
    +    return context.res.send(str(amount_in_dollars))
    +
    +  return 'Invalid path'
    +
    +
    +
  • +
  • +

    Dart

    +
    +

    +

    +
    import 'dart:async';
    +import 'package:http/http.dart' as http;
    +
    +Future<dynamic> main(final context) async {
    +  if (context.req.path == '/eur') {
    +    final amountInEuros = double.parse(context.req.query['amount'])
    +    final response = await http.get(Uri.parse('https://api.exchangerate.host/latest?base=EUR&symbols=USD'));
    +    final data = json.decode(response.body);
    +    final amountInDollars = amountInEuros * data['rates']['USD'];
    +    return context.res.send(amountInDollars.toString());
    +  }
    +
    +  if (context.req.path == '/inr') {
    +    final amountInRupees = double.parse(context.req.query['amount'])
    +    final response = await http.get(Uri.parse('https://api.exchangerate.host/latest?base=INR&symbols=USD'));
    +    final data = json.decode(response.body);
    +    final amountInDollars = amountInRupees * data['rates']['USD'];
    +    return context.res.send(amountInDollars.toString());
    +  }
    +
    +  return 'Invalid path';
    +}
    +
    +
    +
  • +
  • +

    Ruby

    +
    +

    +

    +
    require 'httparty'
    +
    +def main(context)
    +  if context.request.path == '/eur'
    +    amount_in_euros = context.request.query['amount'].to_f
    +    response = HTTParty.get('https://api.exchangerate.host/latest?base=EUR&symbols=USD')
    +    data = JSON.parse(response.body)
    +    amount_in_dollars = amount_in_euros * data['rates']['USD']
    +    return context.response.send(amount_in_dollars.to_s)
    +  end
    +
    +  if context.request.path == '/inr'
    +    amount_in_rupees = context.request.query['amount'].to_f
    +    response = HTTParty.get('https://api.exchangerate.host/latest?base=INR&symbols=USD')
    +    data = JSON.parse(response.body)
    +    amount_in_dollars = amount_in_rupees * data['rates']['USD']
    +    return context.response.send(amount_in_dollars.to_s)
    +  end
    +
    +  return 'Invalid path'
    +end
    +
    +
    +
  • +

    After your function has updated, you can try out the new paths. For example, `ghrfu9ewji.functions.appwrite.app/eur?amount=5` should convert Euros to Dollars, while `ghrfu9ewji.functions.appwrite.app/inr?amount=100` should convert Indian Rupees to Dollars.

    From 1e34f712dd496814a2b234880ba9acee4a497869 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 7 Aug 2023 18:13:45 +0100 Subject: [PATCH 23/80] feat: destructuring examples --- app/views/docs/functions-develop.phtml | 110 +++---------------------- 1 file changed, 11 insertions(+), 99 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 803ce3f36..68411f460 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -58,46 +58,17 @@ -

    Some lamguages support unpacking. You'll see us use unpacking in examples, which has the following syntax.

    -[TODO: @luke for languages that have unpacking, let's add unpacking examples!] +

    Some languages support destructuring. You'll see us use destructing in examples, which has the following syntax.

    • Node.js

      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      PHP

      -
      -
      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      Python

      -
      -
      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      Ruby

      -
      -
      -
      
      -
      -            
      +
      export default async function ({ req, res, log, error }) {
      +    log('This is a log!');
      +    error('This is an error!');
      +    return res.send(`This function was called with ${req.method} method!`)
      +}
    • @@ -105,70 +76,11 @@

      Deno

      -
      
      -
      -            
      -
      -
      - -
    • -

      Dart

      -
      -
      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      Swift

      -
      -
      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      .NET

      -
      -
      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      Kotlin

      -
      -
      -
      
      -
      -            
      -
      - -
      -
    • -
    • -

      Java

      -
      -
      -
      
      -
      -            
      -
      -
      -
    • -
    • -

      C++

      -
      -
      -
      
      -
      -            
      +
      export default async function ({ req, res, log, error }: any) {
      +    log('This is a log!');
      +    error('This is an error!');
      +    return res.send(`This function was called with ${req.method} method!`)
      +}
    • From 30e2d2922437fd1ca13d15b829f3b4d064775c9a Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 7 Aug 2023 20:44:06 +0100 Subject: [PATCH 24/80] feat: env vars --- app/views/docs/functions-develop.phtml | 96 +++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 68411f460..998b51f6a 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -93,7 +93,7 @@ Explore the request object with the following function, which logs all request params to the Appwrite Console.

      -[TODO: @luke Let's make sure we show an example for evert runtime with good string manip methods.] +[TODO: @luke Let's make sure we show an example for event runtime with good string manip methods.]
      • @@ -101,7 +101,7 @@
        export default async ({ req, res, log }) => {
        -    log(req.bodyString);  // Raw request body, contains request data
        +    log(req.bodyRaw);     // Raw request body, contains request data
             log(req.body);        // Body parsed on content-type, only supports JSON
             log(req.headers);     // Request headers
             log(req.scheme);      // Value of the x-forwarded-proto header, usually http or https
        @@ -538,7 +538,97 @@
             You can access the environment variables through the systems library of each language.
         

        -[TODO: @luke show how you access environment varibles for every language (sorry for the pain!)] +
          +
        • +

          Node.js

          +
          +
          +
          process.env.MY_VAR
          +
          +
          +
        • +
        • +

          PHP

          +
          +
          +
          getenv('MY_VAR')
          +
          +
          +
        • +
        • +

          Python

          +
          +
          +
          os.environ['MY_VAR']
          +
          +
          +
        • +
        • +

          Ruby

          +
          +
          +
          ENV['MY_VAR']
          +
          +
          +
        • +
        • +

          Deno

          +
          +
          +
          Deno.env.get('MY_VAR')
          +
          +
          +
        • +
        • +

          Dart

          +
          +
          +
          Platform.environment['MY_VAR']
          +
          +
          +
        • +
        • +

          Swift

          +
          +
          +
          ProcessInfo.processInfo.environment["MY_VAR"]
          +
          +
          +
        • +
        • +

          .NET

          +
          +
          +
          Environment.GetEnvironmentVariable("MY_VAR")
          +
          +
          +
        • +
        • +

          Kotlin

          +
          +
          +
          System.getenv("MY_VAR")
          +
          + +
          +
        • +
        • +

          Java

          +
          +
          +
          System.getenv("MY_VAR")
          +
          +
          +
        • +
        • +

          C++

          +
          +
          +
          std::getenv("MY_VAR")
          +
          +
          +
        • +

        Depencies

        From df0754825a99e89179ba643178f9463ee1960f09 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 8 Aug 2023 15:10:29 +0100 Subject: [PATCH 25/80] feat: discussion --- app/views/docs/functions-develop.phtml | 22 +++-- app/views/docs/functions-execute.phtml | 119 ++++++++++++++++++++++++- app/views/docs/functions-recipes.phtml | 117 ++++++++++++------------ app/views/docs/functions.phtml | 4 +- 4 files changed, 195 insertions(+), 67 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 998b51f6a..0ff6a2dca 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -102,8 +102,8 @@
        export default async ({ req, res, log }) => {
             log(req.bodyRaw);     // Raw request body, contains request data
        -    log(req.body);        // Body parsed on content-type, only supports JSON
        -    log(req.headers);     // Request headers
        +    log(JSON.stringify(req.body));        // Body parsed on content-type, only supports JSON
        +    log(JSON.stringify(req.headers));     // Request headers
             log(req.scheme);      // Value of the x-forwarded-proto header, usually http or https
             log(req.method);      // Request method, such as GET, POST, PUT, DELETE, PATCH, etc.
             log(req.url);         // Full URL, for example: http://awesome.appwrite.io:8000/v1/hooks?limit=12&offset=50
        @@ -235,13 +235,13 @@
                 
        export default async ({ req, res, log }) => {
        -    switch (req.body) {
        +    switch (req.query.type) {
                 case 'send':
                     return res.send(
                         "This is a text response", 
                         200, 
                         {
        -                    "content-type": "application/text"
        +                    "content-type": "text/plain"
                         }
                     );
                 case 'json':
        @@ -630,10 +630,22 @@
             
      -

      Depencies

      +

      Dependencies

      [TODO: @luke is this different for every language? Idk what to put here, do we need to also @matej?] +- The NodeJS runtime has npm installed. +- The PHP runtime has composer installed. +- The Python runtime has pip installed. +- The Ruby runtime has bundler installed. +- The Deno runtime has deno installed. +- The Dart runtime has pub installed. +- The Swift runtime has swift installed. +- The .NET runtime has nuget installed. +- The Kotlin runtime has gradle installed. +- The Java runtime has gradle installed. + +

      Using Appwrite in a Function

      [TODO: @luke @matej 2 examples -> JWT and API keys] diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 387ca1dc3..d3080d5c1 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -16,9 +16,126 @@

      SDK

      [TODO: @luke @matej, two examples running the function sync and async form the SDK]

      - + You can invoke your Appwrite Functions directly from the Appwrite SDKs.

      +Learn more about using the Appwrite SDKs + +
        +
      • +

        Node.js

        +
        +
        +
        
        +            
        +
        +
        +
      • +
      • +

        PHP

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Python

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Ruby

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Deno

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Dart

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Swift

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        .NET

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        Kotlin

        +
        +
        +
        
        +
        +            
        +
        + +
        +
      • +
      • +

        Java

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      • +

        C++

        +
        +
        +
        
        +
        +            
        +
        +
        +
      • +
      + + +

      Events

      Changes in Appwrite emit events. diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-recipes.phtml index b5cb305a7..d8896f77c 100644 --- a/app/views/docs/functions-recipes.phtml +++ b/app/views/docs/functions-recipes.phtml @@ -15,8 +15,8 @@

    • Node.js

      -

      Create a new file, `index.js`.

      -

      Add the following code to `index.js`.

      +

      Create a new file, index.js.

      +

      Add the following code to index.js.

      export default async function ({ res }) {
         return res.send('1.13');
      @@ -27,12 +27,12 @@
         
    • PHP

      -

      Create a new file, `index.php`.

      -

      Add the following code to `index.php`.

      +

      Create a new file, index.php.

      +

      Add the following code to index.php.

      <?php
       return function ($context) {
      -  return $context->res->send('1.13');
      +  return $context->res->send('1.13');
       };
      @@ -40,8 +40,8 @@ return function ($context) {
    • Python

      -

      Create a new file, `index.py`.

      -

      Add the following code to `index.py`.

      +

      Create a new file, index.py.

      +

      Add the following code to index.py.

      def main(context):
         return context.res.send('1.13')
      @@ -51,12 +51,12 @@ return function ($context) {
    • Dart

      -

      Create a new file, `index.dart`.

      -

      Add the following code to `index.dart`.

      +

      Create a new file, index.dart.

      +

      Add the following code to index.dart.

      import 'dart:async';
       
      -Future<dynamic> main(final context) async {
      +Future<dynamic> main(final context) async {
         return context.res.send('1.13');
       }
      @@ -65,8 +65,8 @@ Future<dynamic> main(final context) async {
    • Ruby

      -

      Create a new file, `index.rb`.

      -

      Add the following code to `index.rb`.

      +

      Create a new file, index.rb.

      +

      Add the following code to index.rb.

      def main(context)
         return context.res.send('1.13')
      @@ -76,20 +76,20 @@ end
    -

    This code will return `1.13` when the function is called, because 1€ equals approximately 1.13$.

    +

    This code will return 1.13 when the function is called, because 1€ equals approximately 1.13$.

    Now, create a function in the Appwrite console, adding your Git repository as the remote source and using the path file you created as the entry point.

    -
  • Finally, execute the function and visit the URL (like `ghrfu9ewji.functions.appwrite.app`) to see the response.
  • +
  • Finally, execute the function and visit the URL (like ghrfu9ewji.functions.appwrite.app) to see the response.
  • Currency Conversion

    Now, let's update the function to use the request payload.

    -

    You can use a query string to pass data to your function. For example, `ghrfu9ewji.functions.appwrite.app?amount=5` will pass `5` as the `amount` parameter.

    +

    You can use a query string to pass data to your function. For example, ghrfu9ewji.functions.appwrite.app?amount=5 will pass 5 as the amount parameter.

    • Node.js

      -

      Update `index.js` to use `req.query.amount` to access the `amount` parameter, and return the conversion result.

      +

      Update index.js to use req.query.amount to access the amount parameter, and return the conversion result.

      export default async function ({ req, res }) {
         const amountInEuros = Number(req.query.amount);
      @@ -102,13 +102,13 @@ end
    • PHP

      -

      Update `index.php` to use `$context->req->query['amount']` to access the `amount` parameter, and return the conversion result.

      +

      Update index.php to use $context->req->query['amount'] to access the amount parameter, and return the conversion result.

      <?php
       return function ($context) {
      -  $amountInEuros = $context->req->query['amount'];
      +  $amountInEuros = $context->req->query['amount'];
         $amountInDollars = $amountInEuros * 1.13;
      -  return $context->res->send($amountInDollars);
      +  return $context->res->send($amountInDollars);
       };
      @@ -116,7 +116,7 @@ return function ($context) {
    • Python

      -

      Update `index.py` to use `context.req.query['amount']` to access the `amount` parameter, and return the conversion result.

      +

      Update index.py to use context.req.query['amount'] to access the amount parameter, and return the conversion result.

      def main(context):
         amountInEuros = context.req.query['amount']
      @@ -128,11 +128,11 @@ return function ($context) {
         
    • Dart

      -

      Update `index.dart` to use `context.req.query['amount']` to access the `amount` parameter, and return the conversion result.

      +

      Update index.dart to use context.req.query['amount'] to access the amount parameter, and return the conversion result.

      import 'dart:async';
       
      -Future<dynamic> main(final context) async {
      +Future<dynamic> main(final context) async {
         final amountInEuros = context.req.query['amount'];
         final amountInDollars = amountInEuros * 1.13;
         return context.res.send(amountInDollars);
      @@ -143,7 +143,7 @@ Future<dynamic> main(final context) async {
         
    • Ruby

      -

      Update `index.rb` to use `context.req.query['amount']` to access the `amount` parameter, and return the conversion result.

      +

      Update index.rb to use context.req.query['amount'] to access the amount parameter, and return the conversion result.

      def main(context)
         amountInEuros = context.req.query['amount']
      @@ -161,10 +161,10 @@ end

      Testing the function

      - Execute the function and visit the URL (like `ghrfu9ewji.functions.appwrite.app?amount=5`) to see the response. + Execute the function and visit the URL (like ghrfu9ewji.functions.appwrite.app?amount=5) to see the response.

      - You should see the result of the conversion, like `5.65`. + You should see the result of the conversion, like 5.65.

      Adding Dependencies

      @@ -173,45 +173,45 @@ end
    • Node.js

      -

      Run the following bash command to create a `package.json` file. This file is used to manage your Node.js project's dependencies.

      +

      Run the following bash command to create a package.json file. This file is used to manage your Node.js project's dependencies.

      npm init -y
      -

      Install the `undici` library. This library includes a `fetch` function that you can use to make HTTP requests.

      +

      Install the undici library. This library includes a fetch function that you can use to make HTTP requests.

      npm install undici
      -

      Finally, add `npm install` to your function's build commands in the Appwrite console.

      +

      Finally, add npm install to your function's build commands in the Appwrite console.

    • PHP

      You can use Composer to manage your PHP project's dependencies. Install it from getcomposer.org/download.

      -

      Run the following bash command to create a `composer.json` file. This file is used to manage your PHP project's dependencies.

      +

      Run the following bash command to create a composer.json file. This file is used to manage your PHP project's dependencies.

      composer init -y
      -

      Install the `guzzlehttp/guzzle` library. This library includes a `get` function that you can use to make HTTP requests.

      +

      Install the guzzlehttp/guzzle library. This library includes a get function that you can use to make HTTP requests.

      composer require guzzlehttp/guzzle
      -

      Finally, add `composer install` to your function's build commands in the Appwrite console.

      +

      Finally, add composer install to your function's build commands in the Appwrite console.

    • Python

      -

      Run the following bash command to create a `requirements.txt` file. This file is used to manage your Python project's dependencies.

      +

      Run the following bash command to create a requirements.txt file. This file is used to manage your Python project's dependencies.

      touch requirements.txt
      -

      Install the `requests` library. This library includes a `get` function that you can use to make HTTP requests.

      +

      Install the requests library. This library includes a get function that you can use to make HTTP requests.

      -
      echo "requests" >> requirements.txt
      +        
      echo "requests" >> requirements.txt
       pip -r requirements.txt
      -

      Finally, add `pip install -r requirements.txt` to your function's build commands in the Appwrite console.

      +

      Finally, add pip install -r requirements.txt to your function's build commands in the Appwrite console.

    • @@ -220,25 +220,25 @@ pip -r requirements.txt
    • - Create a `pubspec.yaml` file with the following contents. This file is used to manage your Dart project's dependencies. + Create a pubspec.yaml file with the following contents. This file is used to manage your Dart project's dependencies.

      name: appwrite_function
       description: Appwrite Function
       version: 1.0.0
       environment:
      -  sdk: '>=2.12.0 <3.0.0'
      +  sdk: '>=2.12.0 <3.0.0'
       

      - Install the `http` library. This library includes a `get` function that you can use to make HTTP requests. + Install the http library. This library includes a get function that you can use to make HTTP requests.

      
                 pub install http
             

      - Finally, add `pub get` to your function's build commands in the Appwrite console. + Finally, add pub get to your function's build commands in the Appwrite console.

    • @@ -248,21 +248,21 @@ environment:

      - Create a `Gemfile` file with the following contents. This file is used to manage your Ruby project's dependencies. + Create a Gemfile file with the following contents. This file is used to manage your Ruby project's dependencies.

      source 'https://rubygems.org'
               

      - Install the `httparty` library. This library includes a `get` function that you can use to make HTTP requests. + Install the httparty library. This library includes a get function that you can use to make HTTP requests.

      -
      echo "gem 'httparty'" >> Gemfile
      +        
      echo "gem 'httparty'" >> Gemfile
       bundle install

      - Finally, add `bundle install` to your function's build commands in the Appwrite console. + Finally, add bundle install to your function's build commands in the Appwrite console.

      @@ -274,7 +274,7 @@ bundle install
    • Node.js

      -

      Use `fetch` from `undici` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      +

      Use fetch from undici to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      import { fetch } from 'undici';
       
      @@ -300,12 +300,12 @@ require(__DIR__ . '/../vendor/autoload.php');
       use GuzzleHttp\Client;
       
       return function ($context) {
      -  $amountInEuros = $context->getRequest()->getQuery('amount');
      +  $amountInEuros = $context->getRequest()->getQuery('amount');
         $client = new Client();
      -  $response = $client->get('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
      -  $data = json_decode($response->getBody(), true);
      +  $response = $client->get('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
      +  $data = json_decode($response->getBody(), true);
         $amountInDollars = $amountInEuros * $data['rates']['USD'];
      -  return $context->res->send(strval($amountInDollars));
      +  return $context->res->send(strval($amountInDollars));
       };
      @@ -313,7 +313,7 @@ return function ($context) {
    • Python

      -

      Use `get` from `requests` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      +

      Use get from requests to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      import requests
       
      @@ -329,12 +329,12 @@ def main(context):
         
    • Dart

      -

      Use `get` from `http` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      +

      Use get from http to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      import 'dart:async';
       import 'package:http/http.dart' as http;
       
      -Future<dynamic> main(final context) async {
      +Future<dynamic> main(final context) async {
         final amountInEuros = double.parse(context.req.query['amount'])
         final response = await http.get(Uri.parse('https://api.exchangerate.host/latest?base=EUR&symbols=USD'));
         final data = json.decode(response.body);
      @@ -347,7 +347,7 @@ Future<dynamic> main(final context) async {
         
    • Ruby

      -

      Use `get` from `httparty` to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      +

      Use get from httparty to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

      require 'httparty'
       
      @@ -368,7 +368,7 @@ end

      Adding More Routes

      -

      Let's add support multiple paths like `/eur` and `/inr`. Each path will convert from that currency to dollars.

      +

      Let's add support multiple paths like /eur and /inr. Each path will convert from that currency to dollars.

      • @@ -434,7 +434,7 @@ def main(context):
        import 'dart:async';
         import 'package:http/http.dart' as http;
         
        -Future<dynamic> main(final context) async {
        +Future<dynamic> main(final context) async {
           if (context.req.path == '/eur') {
             final amountInEuros = double.parse(context.req.query['amount'])
             final response = await http.get(Uri.parse('https://api.exchangerate.host/latest?base=EUR&symbols=USD'));
        @@ -488,24 +488,21 @@ end
      -

      After your function has updated, you can try out the new paths. For example, `ghrfu9ewji.functions.appwrite.app/eur?amount=5` should convert Euros to Dollars, while `ghrfu9ewji.functions.appwrite.app/inr?amount=100` should convert Indian Rupees to Dollars.

      +

      After your function has updated, you can try out the new paths. For example, ghrfu9ewji.functions.appwrite.app/eur?amount=5 should convert Euros to Dollars, while ghrfu9ewji.functions.appwrite.app/inr?amount=100 should convert Indian Rupees to Dollars.

      Congratulations! You've built a powerful currency conversion function using Appwrite!

      -[TODO: @luke -> translate code for other runtimes] - -[TODO: @matej @luke -> Example with JWT, show both client and server code] +[TODO: @luke -> Example with JWT, show both client and server code] +[TODO: @luke -> Example with API Key] From a11daf828fcb4f7620489526072675c094ce1cfa Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 14 Aug 2023 19:57:00 +0000 Subject: [PATCH 43/80] Add information about environment variables that now became headers --- app/views/docs/functions-develop.phtml | 20 +++++++++++++++++++- app/views/docs/functions.phtml | 3 +++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index fd43c4d4a..d32bdd973 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -26,7 +26,7 @@

      The Context Object

      Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite console. - All input, output, and logging **must be** handled through the context object passed in. + All input, output, anddlogging **must be** handled through the context object passed in.

      You'll find these properties in the context object.

      @@ -940,6 +940,24 @@ namespace runtime { Environmental variables can be global, or function specific.

      +

      Default Environment Variables

      +

      Appwrite Runtimes have some default environment variables. These are always accesible

      + + + + + + + + + + + + + +
      VariableDescription
      + +

      Local Environment Variables

      Local variables will only be accessible in the function they belong to. diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 686e75906..93715532d 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -299,5 +299,8 @@ So it's important to be able to log, debug, and test your Appwrite Functions in This prevents confusing errors when functions are terminated prematurely before a response is sent. Learn about response.

    • +
    • + Some variables about how a function was triggered are now found in the context.req object as headers. +
    • From d3645b2d4b22e565e9e8ac85974f8fa4af8a0784 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 14 Aug 2023 20:44:50 +0000 Subject: [PATCH 44/80] Move upgrade checklist to develop --- app/views/docs/functions-develop.phtml | 49 +++++++++++++++++++++++++- app/views/docs/functions.phtml | 49 +------------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index d32bdd973..42771b3ae 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -2069,4 +2069,51 @@ public class Main {

      Explore examples and recipes -

      \ No newline at end of file +

      + +

      Upgrade

      +

      + Appwrite Functions received major updates in Appwrite version 1.4. + If you still have functions from previous versions, they will be read-only in Appwrite 1.4. + You will have to migrate your old functions to follow new runtime syntax. +

      + +

      + Here's a checklist of things you need to know. +

      + +
        +
      1. + The parameter passed into functions has changed. + req and res has been replaced by context, which contains new logger methods. + Learn about context. +
      2. +
      3. + To improve privacy and logging reliability, we provide new context.log() and context.error() functions. + You can no longer use native logging methods. + Learn about logging. +
      4. +
      5. + The old way of req.variables has been deprecated. + You can now access variables passed into each function as environment variables. + Learn about environment variables. +
      6. +
      7. + The req object has been updated to use terminology consistent with typical HTTP concepts. + You'll now find familiar concepts like headers, body, HTTP methods, and others. + Learn about request. +
      8. +
      9. + The response object has been updated. + You can now specify headers, as well as use new methods like return redirects or empty responses. + Learn about response. +
      10. +
      11. + Now, you must return a response such as return context.res.send(""). + This prevents confusing errors when functions are terminated prematurely before a response is sent. + Learn about response. +
      12. +
      13. + Some variables about how a function was triggered are now found in the context.req object as headers. +
      14. +
      \ No newline at end of file diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 93715532d..a014e4511 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -193,6 +193,7 @@ namespace runtime {
    +[TODO: explore features should be like: Familiar HTTP concepts, many ways to execute, deploy from Git or xxx, built in templates, etc.]

    Explore Features

    @@ -256,51 +257,3 @@ So it's important to be able to log, debug, and test your Appwrite Functions in

    Learn more about debugging functions

    - -

    Upgrade

    -

    - Appwrite Functions received major updates in Appwrite version 1.4. - If you still have functions from previous versions, they will be read-only in Appwrite 1.4. - You will have to migrate your old functions to follow new runtime syntax. -

    - -

    - Here's a checklist of things you need to know. -

    - -
      -
    1. - The parameter passed into functions has changed. - req and res has been replaced by context, which contains new logger methods. - Learn about context. -
    2. -
    3. - To improve privacy and logging reliability, we provide new context.log() and context.error() functions. - You can no longer use native logging methods. - Learn about logging. -
    4. -
    5. - The old way of req.variables has been deprecated. - You can now access variables passed into each function as environment variables. - Learn about environment variables. -
    6. -
    7. - The req object has been updated to use terminology consistent with typical HTTP concepts. - You'll now find familiar concepts like headers, body, HTTP methods, and others. - Learn about request. -
    8. -
    9. - The response object has been updated. - You can now specify headers, as well as use new methods like return redirects or empty responses. - Learn about response. -
    10. -
    11. - Now, you must return a response such as return context.res.send(""). - This prevents confusing errors when functions are terminated prematurely before a response is sent. - Learn about response. -
    12. -
    13. - Some variables about how a function was triggered are now found in the context.req object as headers. -
    14. -
    - From 1ffc8e293eef1a4ac0cbd795f65c8bc8c3857bbc Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 14 Aug 2023 21:00:37 +0000 Subject: [PATCH 45/80] Add cloud/self-hosted tags as labels in runtimes --- app/views/docs/functions-runtimes.phtml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 0a1e057e9..63d29e494 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -4,7 +4,12 @@ use Appwrite\Utopia\View; $events = $this->getParam('events', []); $runtimes = $this->getParam('runtimes', []); - +$runtimes['node-16.0']["cloud"] = true; +$runtimes['node-18.0']["cloud"] = true; +$runtimes['php-8.0']["cloud"] = true; +$runtimes['ruby-3.0']["cloud"] = true; +$runtimes['python-3.9']["cloud"] = true; +$runtimes['dart-2.17']["cloud"] = true; ?> @@ -26,6 +31,7 @@ $runtimes = $this->getParam('runtimes', []); Name Image Architectures + Platforms @@ -35,6 +41,12 @@ $runtimes = $this->getParam('runtimes', []); escape($key); ?> escape($runtime['image'] ?? ''); ?> escape(implode(' / ', $runtime['supports'] ?? [])); ?> + + + Cloud + + Self-hosted + From ca020879140790bfc666581366e531c570c01538 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 14 Aug 2023 23:21:35 +0000 Subject: [PATCH 46/80] Some minor improvements --- app/views/docs/functions-deploy.phtml | 6 ++++- app/views/docs/functions-execute.phtml | 32 ++++++++++++++++++------- app/views/docs/functions-runtimes.phtml | 10 ++++---- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index c16a93e3d..0fac2e804 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -160,4 +160,8 @@
  • Upload code.tar.gz.
  • Select Activate deployment after build to use your new function.
  • Click Create to deploy your function.
  • - \ No newline at end of file + + +

    Domains

    + +[TODO: @matej steps to add a custom domain thanks] diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 88ca4327d..dbfb93d9c 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -4,11 +4,21 @@ Here are all the different ways to consume your new Appwrite Functions.

    -

    Domain

    +

    Domains

    - Each Appwrite function has its own domain. You can find this in the Appwrite Console, under the Function overview. - It looks something like this.https://64d4d22db370ae41a32e.functions.cloud.appwrite.io - Alternatively you can add a custom domain to your Appwrite project, and use that instead. + Each Appwrite function has its own domain. + You can find this in the Appwrite Console, under the Function overview. +

    + +

    + The generated domains will look like this. +

    +
    +
    https://64d4d22db370ae41a32e.functions.cloud.appwrite.io
    +
    + +

    + Alternatively you can add a custom domain to your Appwrite project.

    @@ -18,10 +28,12 @@

    REST API

    -
    curl -X POST [APPWRITE_FUNCTION_DOMAIN] \
    --H "X-Custom-Header: 123" \
    --H "Content-Type: application/json" \
    --d '{"foo":"bar"}'
    +
    +
    curl -X POST https://64d4d22db370ae41a32e.functions.cloud.appwrite.io \
    +    -H "X-Custom-Header: 123" \
    +    -H "Content-Type: application/json" \
    +    -d '{"data":"this is json data"}'
    +

    SDK

    @@ -455,6 +467,8 @@ public static void main(String[] args) throws Exception {

    Permissions

    +

    Permissions

    +

    Appwrite Functions can be executed using Client or Server SDKs. Client SDKs must be authenticated with an account that has been granted execution permissions on the function's settings page. @@ -466,7 +480,7 @@ public static void main(String[] args) throws Exception { If you need to enforce permissions for functions with a domain, use authentication methods like JWT.

    -

    Logs and results

    +

    Logs and results

    You can view the logs your function executions in the Appwrite Console. Navigate to Functions and click on a function to view it's executions. diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 63d29e494..cce760392 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -15,14 +15,12 @@ $runtimes['dart-2.17']["cloud"] = true;

    Appwrite Functions supports an extensive list of runtimes to meet your unique tech preferences. - Not all runtimes are available on Appwrite Cloud, check for the Cloud label in each listed runtime to know whichones are available. + Not all runtimes are available on Appwrite Cloud, check for the Cloud label in each listed runtime to know which ones are available.

    Supported Runtimes

    -

    Appwrite provides multiple code runtimes to execute your custom functions. Each runtime uses a Docker image tied to a specific language version to provide a safe, isolated playground to run your team's code.

    - -

    Below is a list of supported Cloud Functions runtimes. The Appwrite team continually adds support for new runtimes.

    +

    Below is a list of supported Functions runtimes. The Appwrite team continually adds support for new runtimes.

    @@ -52,4 +50,6 @@ $runtimes['dart-2.17']["cloud"] = true;
    -[TODO: @matej Label which ones are cloud only, idk how to do cleanly] +

    +

  • Learn more about permissions
  • +

    \ No newline at end of file From dbabc60a9d10d266804cb20e3796b96a791882cc Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 14 Aug 2023 23:24:35 +0000 Subject: [PATCH 47/80] Moved a p tag --- app/views/docs/functions-execute.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index dbfb93d9c..54335a66b 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -21,13 +21,13 @@ Alternatively you can add a custom domain to your Appwrite project.

    +

    REST API

    When requests are made to this domain, whether through a browser or through an HTTP requests, the request information like request headers and request body will be passed to the function. This unlocks interesting ways to integrate other apps and backends to your Appwrite project.

    -

    REST API

    curl -X POST https://64d4d22db370ae41a32e.functions.cloud.appwrite.io \
         -H "X-Custom-Header: 123" \
    
    From 54097a42de357adf7e6ba1b668607241b1d043b1 Mon Sep 17 00:00:00 2001
    From: "Luke B. Silver" <22452787+loks0n@users.noreply.github.com>
    Date: Tue, 15 Aug 2023 13:20:29 +0100
    Subject: [PATCH 48/80] fix: remove vscode settings
    
    ---
     .vscode/settings.json | 9 ---------
     1 file changed, 9 deletions(-)
     delete mode 100644 .vscode/settings.json
    
    diff --git a/.vscode/settings.json b/.vscode/settings.json
    deleted file mode 100644
    index 8e639cda8..000000000
    --- a/.vscode/settings.json
    +++ /dev/null
    @@ -1,9 +0,0 @@
    -{
    -  "editor.formatOnSave": false,
    -  "[html][phtml]": {
    -    "editor.formatOnSave": false
    -  },
    -  "[php]": {
    -    "editor.formatOnSave": false
    -  }
    -}
    
    From b04e153eb7576ebb35aeb57b26d8e415c58067f3 Mon Sep 17 00:00:00 2001
    From: "Vincent (Wen Yu) Ge" 
    Date: Tue, 15 Aug 2023 21:07:54 +0000
    Subject: [PATCH 49/80] sell functions better in overview
    
    ---
     app/views/docs/functions.phtml | 51 +++++++++++++---------------------
     1 file changed, 20 insertions(+), 31 deletions(-)
    
    diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml
    index a014e4511..971ac5fac 100644
    --- a/app/views/docs/functions.phtml
    +++ b/app/views/docs/functions.phtml
    @@ -202,58 +202,47 @@ namespace runtime {
         To fully harness the power of Appwrite Functions, explore the following features.
     

    -

    Templates

    +

    Familiar Concepts

    - If you need to integrate Appwrite with a third-party API or add a function for common utilities, - there might already be a function template made by the Appwrite community that fits your needs. - Function templates are Appwrite Functions repositories that you can clone and add to your Appwrite instance. + Appwrite Functions follow common HTTP concepts you already know. + You can start building with minimal learning curve, without needing to learn niche concepts that don't apply elsewhere.

    -

    -Learn more about using function templates -

    - -

    Develop

    -

    -Writing Appwrite Functions should feel familiar to writing controllers in an HTTP server. -In your function, you'll receive a request object, add transformations and logic, then return a response. -Almost anything can be executed as code in an Appwrite Function. -

    Learn more about developing functions

    -

    Deploy

    +

    Automated Deployment

    - Appwrite Functions are designed to be maintainable and fit into a familiar development workflow. - You can deploy them from a GitHub repository branch or using the Appwrite CLI. + Appwrite Functions can be deployed automatically from GitHub. + Integrate Appwrite Functions seemlessly into your existing development workflow, without needing annoying CI/CD configuration.

    -Learn more about using deploying functions +Learn more about deploying functions

    -

    Execute

    +

    Flexible Execution

    -Appwrite Functions can be executed directly through a request to the API, or triggered by events, webhooks, or scheduled executions. -This flexible execution models unlocks many potential usecases for Appwrite functions. -Explore using Appwrite Functions to execute a complex routine of logic, or execute background tasks on a schedule. + Appwrite Functions can be executed through HTTP requests, async or synchronous SDK calls, webhooks, event or scheduled triggers, and even serve webpages to browsers. + Integrate Appwrite with infinite possibilities through a simple function.

    -Learn more about using executing functions +Learn more about executing functions

    -

    Runtime

    +

    All Your Favorite Runtimes

    -Appwrite supports many open-source runtimes. Find your prefered language and start writing your functions. + Appwrite supports a growing list of 10+ runtimes. + Keep your codebase simple by writing functions in a language you already work with.

    -Learn more about using runtimes +Learn more about using function runtimes

    -

    Debug

    +

    Start with a Template

    -Let's be honest, we spend more time debugging our code than writing our code. -So it's important to be able to log, debug, and test your Appwrite Functions in development and production. + Appwrite Functions has many built in templates that help you jumpstart your creativity. + Add integrations by using templates out of the box, or clone the template to customize and fit your needs.

    -Learn more about debugging functions -

    +Learn more about using function templates +

    \ No newline at end of file From f088a1e8114b0562ef5cbea23d82a899bd0ddf5a Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 17 Aug 2023 03:09:04 +0000 Subject: [PATCH 50/80] Add headers + env vars --- app/views/docs/functions-develop.phtml | 109 ++++++++++++++++++++++++- app/views/docs/functions.phtml | 1 - 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 42771b3ae..0fde8fa78 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -353,6 +353,67 @@ public class Main { +

    Headers

    +

    + Appwrite Functions will always receive a set of headers that provide meta data about the function execution. + These are provided along side any custom headers sent to the function. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    VariableDescription
    x-appwrite-trigger + Describes how the function execution was invoked. +
    x-appwrite-event + If the function execution was triggered by an event, describes the triggering event. +
    x-appwrite-user-id + If the function execution was invoked by an authenticated user, display the user ID. + This doesn't apply to Appwrite Console users or API keys. +
    x-appwrite-user-jwt + [TODO: @meldiron whats this] +
    x-appwrite-country-code + Displays the country code of the configured locale. +
    x-appwrite-continent-code + Displays the continent code of the configured locale. +
    x-appwrite-continent-eu + Describes if the configured local is within the EU. +
    + +

    Response

    If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. @@ -941,7 +1002,19 @@ namespace runtime {

    Default Environment Variables

    -

    Appwrite Runtimes have some default environment variables. These are always accesible

    +

    + Appwrite runtimes passes in some environment variables by default. + These are always accesible for every function at runtime. +

    + +
    +

    Appwrite API keys

    +

    + If your function is using an Appwrite SDK with an API key, this API key needs to be generated and passed in manually. + API keys are not passed by default for security reasons. +

    +
    + @@ -951,13 +1024,43 @@ namespace runtime { - + + + + + + + + + + + + + + + + + + + + + + The runtime version of the running function.
    APPWRITE_FUNCTION_ID + The ID of the running function. +
    APPWRITE_FUNCTION_NAME + The Name of the running function. +
    APPWRITE_FUNCTION_DEPLOYMENT + The deployment ID of the running function. +
    APPWRITE_FUNCTION_PROJECT_ID + The project ID of the running function. +
    APPWRITE_FUNCTION_RUNTIME_NAME + The runtime of the running function. +
    APPWRITE_FUNCTION_RUNTIME_VERSION
    -

    Local Environment Variables

    Local variables will only be accessible in the function they belong to. diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 971ac5fac..970befc03 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -193,7 +193,6 @@ namespace runtime { -[TODO: explore features should be like: Familiar HTTP concepts, many ways to execute, deploy from Git or xxx, built in templates, etc.]

    Explore Features

    From 2cd56dd807e90646a67e3c60989085f407b4faec Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 17 Aug 2023 22:34:44 +0000 Subject: [PATCH 51/80] Improve overview page for functions and rename recipes to examples --- app/views/docs/functions-develop.phtml | 10 +- ...recipes.phtml => functions-examples.phtml} | 0 app/views/docs/functions.phtml | 229 ++---------------- app/views/docs/index.phtml | 2 +- 4 files changed, 27 insertions(+), 214 deletions(-) rename app/views/docs/{functions-recipes.phtml => functions-examples.phtml} (100%) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 0fde8fa78..84a562cc7 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -6,7 +6,7 @@

    Just want the code?

    -

    If you prefer to learn through examples, explore the recipes section.

    +

    If you prefer to learn through examples, explore the examples page.

    Function Flow

    @@ -2165,14 +2165,6 @@ public class Main { -

    Recipes

    -

    - We have a dedicated page of recipes to implement common functionalities in Appwrite Functions, like parsing request path and params, or making requests to third party APIs. -

    - -

    -Explore examples and recipes -

    Upgrade

    diff --git a/app/views/docs/functions-recipes.phtml b/app/views/docs/functions-examples.phtml similarity index 100% rename from app/views/docs/functions-recipes.phtml rename to app/views/docs/functions-examples.phtml diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 970befc03..39b8c8a27 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -2,246 +2,67 @@ use Appwrite\Utopia\View; -$events = $this->getParam('events', []); -$runtimes = $this->getParam('runtimes', []); - ?>

    - Appwrite Functions let you extend Appwrite by adding your own code and logic. - You can think of them as code snippets that are triggered by server events, webhooks, scheduled executions, or user invokation. + Appwrite Functions unlock limitless potential for developers to extend Appwrite with code snippets. + Appwrite Functions are user defined functions that can start small and scale big, deploying automatically from source control. + These Functions can be triggered by HTTP requests, SDK methods, server events, webhooks, scheduled executions. Each function will have their own URL, execute in their own isolated container, and have their own configurable environment variables and permissions. - With these features, Appwrite Functions unlock limitless potential to expand Appwrite's capabilities with custom logic and integrations.

    Getting Started

    - Appwrite Functions unlock limitless possibilities, but it's simple to get started. You can deploy your first function and execute it in minutes. + Appwrite Functions let you build anything you can imagine, but this flexibility makes is difficult to know where to start. + Start exploring by cloning one of the quick start templates or using a template with pre-built integration to quickly implement features.

    -
      -
    • -

      Node.js

      -
      -
      -
      export default async ({ res }) => {
      -    return res.json({
      -        motto: 'Build Fast. Scale Big. All in One Place.'
      -    });
      -};
      -
      -
      -
    • -
    • -

      PHP

      -
      -
      -
      return function ($context) {
      -    return $context->res->json([
      -        'motto' => 'Build Fast. Scale Big. All in One Place.'
      -    ]);
      -}
      -
      -
      -
    • -
    • -

      Python

      -
      -
      -
      def main(context):
      -    return context.res.json({
      -        "motto": "Build Fast. Scale Big. All in One Place.",
      -})
      -
      -
      -
    • -
    • -

      Ruby

      -
      -
      -
      def main(context)
      -    return context.res.json(
      -        {
      -            "motto": "Build Fast. Scale Big. All in One Place."
      -        }
      -    )
      -end
      -
      -
      -
    • -
    • -

      Deno

      -
      -
      -
      export default ({ req, res, log, error }: any) => {
      -  return res.json({
      -    motto: "Build Fast. Scale Big. All in One Place."
      -  });
      -};
      -
      -
      -
    • -
    • -

      Dart

      -
      -
      -
      import 'dart:async';
      -
      -Future main(final context) async {
      -    return context.res.json({
      -        'motto': 'Build Fast. Scale Big. All in One Place.',
      -    });
      -}
      -
      -
      -
    • -
    • -

      Swift

      -
      -
      -
      import Foundation
      -
      -            func main(context: RuntimeContext) async throws -> RuntimeOutput {
      -    return try context.res.json([
      -        "motto": "Build Fast. Scale Big. All in One Place."
      -    ])
      -}
      -
      -
      -
    • -
    • -

      .NET

      -
      -
      -
      namespace DotNetRuntime;
      -public class Handler {
      -    public async Task Main(RuntimeContext Context) 
      -    {
      -        return Context.Res.Json(new Dictionary()
      -        {
      -            { "motto", "Build Fast. Scale Big. All in One Place." }
      -        });
      -    }
      -}
      -
      -
      -
    • -
    • -

      Kotlin

      -
      -
      -
      package io.openruntimes.kotlin.src
      -
      -import io.openruntimes.kotlin.RuntimeContext
      -import io.openruntimes.kotlin.RuntimeOutput
      -
      -class Main {
      -    fun main(context: RuntimeContext): RuntimeOutput {
      -        return context.res.json(mutableMapOf(
      -            "motto" to "Build Fast. Scale Big. All in One Place.",
      -            "learn" to "https://appwrite.io/docs",
      -            "connect" to "https://appwrite.io/discord",
      -            "getInspired" to "https://builtwith.appwrite.io"
      -        ))
      -    }
      -}
      -
      - -
      -
    • -
    • -

      Java

      -
      -
      -
      package io.openruntimes.java.src;
      -
      -import io.openruntimes.java.RuntimeContext;
      -import io.openruntimes.java.RuntimeOutput;
      -import java.util.HashMap;
      -
      -public class Main {
      -    public RuntimeOutput main(RuntimeContext context) throws Exception {
      -        Map json = new HashMap<>();
      -        json.put("motto", "Build Fast. Scale Big. All in One Place.");
      -        return context.getRes().json(json);
      -    }
      -}
      -
      -
      -
    • -
    • -

      C++

      -
      -
      -
      #include "../RuntimeResponse.h"
      -#include "../RuntimeRequest.h"
      -#include "../RuntimeOutput.h"
      -#include "../RuntimeContext.h"
      -
      -namespace runtime {
      -  class Handler {
      -    public:
      -      static RuntimeOutput main(RuntimeContext &context) {
      -        Json::Value response;
      -        response["motto"] = "Build Fast. Scale Big. All in One Place.";
      -        return context.res.json(response);
      -      }
      -  };
      -}
      -
      -
      -
    • -
    - +setParam('srcLight', '/images-ee/docs/functions-starter-light.png') + ->setParam('srcDark', '/images-ee/docs/functions-starter-dark.png') + ->setParam('alt', 'Function settings page.') + ->setParam('description', 'Function settings page.') + ->render(); +?>

    Explore Features

    - Appwrite Functions help you start small and scale big. - Now you've created your first Appwrite Function, it's time to learn the different ways to develop, deploy, and execute your Appwrite Functions. - To fully harness the power of Appwrite Functions, explore the following features. -

    - -

    Familiar Concepts

    -

    - Appwrite Functions follow common HTTP concepts you already know. - You can start building with minimal learning curve, without needing to learn niche concepts that don't apply elsewhere. + Appwrite Functions use familiar HTTP concepts, so you can learn quickly and grain transferable skills.

    -

    Learn more about developing functions

    -

    Automated Deployment

    - Appwrite Functions can be deployed automatically from GitHub. - Integrate Appwrite Functions seemlessly into your existing development workflow, without needing annoying CI/CD configuration. + Appwrite Functions can be deployed automatically from Git, through the Appwrite CLI, or be uploaded manually. + Develop and deploy with the workflow you're already comfortable with.

    Learn more about deploying functions

    -

    Flexible Execution

    - Appwrite Functions can be executed through HTTP requests, async or synchronous SDK calls, webhooks, event or scheduled triggers, and even serve webpages to browsers. - Integrate Appwrite with infinite possibilities through a simple function. + Appwrite Functions can be triggered by HTTP requests, SDK methods, server events, webhooks, scheduled executions. + Explore how Appwrite Functions can be invoked.

    Learn more about executing functions

    -

    All Your Favorite Runtimes

    - Appwrite supports a growing list of 10+ runtimes. - Keep your codebase simple by writing functions in a language you already work with. + Appwrite supports a growing list of 10+ runtimes. + Avoid adding additional complexity to your codebase by coding in languages you already use and love.

    Learn more about using function runtimes

    -

    Start with a Template

    +

    - Appwrite Functions has many built in templates that help you jumpstart your creativity. - Add integrations by using templates out of the box, or clone the template to customize and fit your needs. + Like to learn from examples? + Here's a curated list of examples that showcase Appwrite Function's capabilies.

    -Learn more about using function templates +Learn more about using function examples

    \ No newline at end of file diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index d82d95a21..f4cc3e7be 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -100,7 +100,7 @@ $cols = [
  •    Deploy
  •    Execute
  •    Runtimes
  • -
  •    Recipes
  • +
  •    Examples
  • From e7b9b1cf6d48a301181da7e8e7257efff7336b48 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 17 Aug 2023 23:03:49 +0000 Subject: [PATCH 52/80] Fix style in index and add alt text to functions --- app/views/docs/functions.phtml | 4 ++-- app/views/docs/index.phtml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 39b8c8a27..0dc52264d 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -22,8 +22,8 @@ $image = new View(__DIR__.'/../general/image.phtml'); echo $image ->setParam('srcLight', '/images-ee/docs/functions-starter-light.png') ->setParam('srcDark', '/images-ee/docs/functions-starter-dark.png') - ->setParam('alt', 'Function settings page.') - ->setParam('description', 'Function settings page.') + ->setParam('alt', '"Create Function" page.') + ->setParam('description', '"Create Function" page.') ->render(); ?> diff --git a/app/views/docs/index.phtml b/app/views/docs/index.phtml index f4cc3e7be..b1e885dbe 100644 --- a/app/views/docs/index.phtml +++ b/app/views/docs/index.phtml @@ -94,7 +94,6 @@ $cols = [
  • Functions -
    •    Develop
    •    Deploy
    • From 780b5e408ce5b03df62ed4a474583b286d91a104 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Fri, 18 Aug 2023 15:53:48 +0000 Subject: [PATCH 53/80] Talk about JWT and trigger headers --- app/views/docs/functions-develop.phtml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 84a562cc7..c3174a8c1 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -370,7 +370,8 @@ public class Main { x-appwrite-trigger - Describes how the function execution was invoked. + Describes how the function execution was invoked. + Possible values are http, schedule or event. @@ -389,7 +390,8 @@ public class Main { x-appwrite-user-jwt - [TODO: @meldiron whats this] + JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. + Learn more about JWT tokens. From b2902ed45317760ea295233e8d4ea94a73294a8b Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Mon, 21 Aug 2023 20:19:15 +0000 Subject: [PATCH 54/80] Improve formatting and arrange items more logically --- app/views/docs/functions-deploy.phtml | 4 +- app/views/docs/functions-develop.phtml | 944 +++++++++++++++--------- app/views/docs/functions-execute.phtml | 21 +- app/views/docs/functions-runtimes.phtml | 6 +- 4 files changed, 605 insertions(+), 370 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 0fac2e804..0244a4e5f 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -9,9 +9,9 @@ Here's everything you need to know to deploy your first Appwrite Function.

      -

      Git

      +

      VCS (Version Control System)

      - The recommended way to manage your Appwrite Function deployments is to use Git. + The recommended way to manage your Appwrite Function deployments is to use a version control system, like Git. This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.

      diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index c3174a8c1..aa5cfc8b1 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -1,14 +1,8 @@

      Appwrite Functions offer a familiar interface if you've developed REST endpoints. Each function is handled following a request and response pattern. - Here's what you need to know to start writing your first Appwrite Function.

      -
      -

      Just want the code?

      -

      If you prefer to learn through examples, explore the examples page.

      -
      -

      Function Flow

      There is a clear flow for all Appwrite Functions, from beginning to end. @@ -16,17 +10,443 @@

        -
      1. Invocation, where Appwrite receives a event to execute a Function. This event could be a request from an SDK, a request to the function's domain, a scheduled execution, or an execution triggered by an event within your Appwrite project.
      2. -
      3. After a function is invoked, Appwrite passes request information to your function's executor.
      4. -
      5. The executor runs the function code you deployed and waits for it to return.
      6. -
      7. Function terminates either when the user returns with a method from res, when the user code throws an exception, or times out.
      8. +
      9. The function is invoked.
      10. +
      11. Appwrite passes in request information like headers and environment variables through the context.req object.
      12. +
      13. The runtime executes the code you defined, you can log through the context.log or context.error methods.
      14. +
      15. Function terminates when you return results using context.res.
      +

      You'll find all of these steps in a simple function like this.

      + +
        +
      • +

        Node.js

        +
        +
        import { Client } from 'node-appwrite';
        +
        +// This is your Appwrite function
        +// It's executed each time we get a request
        +export default async ({ req, res, log, error }) => {
        +  // Why not try the Appwrite SDK?
        +  //
        +  // const client = new Client()
        +  //    .setEndpoint('https://cloud.appwrite.io/v1')
        +  //    .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        +  //    .setKey(process.env.APPWRITE_API_KEY);
        +
        +  // You can log messages to the console
        +  log('Hello, Logs!');
        +
        +  // If something goes wrong, log an error
        +  error('Hello, Errors!');
        +
        +  // The `req` object contains the request data
        +  if (req.method === 'GET') {
        +    // Send a response with the res object helpers
        +    // `res.send()` dispatches a string back to the client
        +    return res.send('Hello, World!');
        +  }
        +
        +  // `res.json()` is a handy helper for sending JSON
        +  return res.json({
        +    motto: 'Build Fast. Scale Big. All in One Place.',
        +    learn: 'https://appwrite.io/docs',
        +    connect: 'https://appwrite.io/discord',
        +    getInspired: 'https://builtwith.appwrite.io',
        +  });
        +};
        +
        +
      • +
      • +

        PHP

        +
        +
        require(__DIR__ . '/../vendor/autoload.php');
        +
        +use Appwrite\Client;
        +use Appwrite\Exception;
        +
        +// This is your Appwrite function
        +// It's executed each time we get a request
        +return function ($context) {
        +    // Why not try the Appwrite SDK?
        +    //
        +    // $client = new Client();
        +    // $client
        +    //     ->setEndpoint('https://cloud.appwrite.io/v1')
        +    //     ->setProject(getenv('APPWRITE_FUNCTION_PROJECT_ID'))
        +    //      ->setKey(getenv('APPWRITE_API_KEY'));
        +
        +    // You can log messages to the console
        +    $context->log('Hello, Logs!');
        +
        +    // If something goes wrong, log an error
        +    $context->error('Hello, Errors!');
        +
        +    // The `req` object contains the request data
        +    if ($context->req->method === 'GET') {
        +        // Send a response with the res object helpers
        +        // `res.send()` dispatches a string back to the client
        +        return $context->res->send('Hello, World!');
        +    }
        +
        +    // `res.json()` is a handy helper for sending JSON
        +    return $context->res->json([
        +        'motto' => 'Build Fast. Scale Big. All in One Place.',
        +        'learn' => 'https://appwrite.io/docs',
        +        'connect' => 'https://appwrite.io/discord',
        +        'getInspired' => 'https://builtwith.appwrite.io',
        +    ]);
        +};
        +
        +
      • +
      • +

        Python

        +
        +
        from appwrite.client import Client
        +import os
        +
        +
        +# This is your Appwrite function
        +# It's executed each time we get a request
        +def main(context):
        +    # Why not try the Appwrite SDK?
        +    #
        +    # client = (
        +    #     Client()
        +    #     .set_endpoint("https://cloud.appwrite.io/v1")
        +    #     .set_project(os.environ["APPWRITE_FUNCTION_PROJECT_ID"])
        +    #     .set_key(os.environ["APPWRITE_API_KEY"])
        +    # )
        +
        +    # You can log messages to the console
        +    context.log("Hello, Logs!")
        +
        +    # If something goes wrong, log an error
        +    context.error("Hello, Errors!")
        +
        +    # The `ctx.req` object contains the request data
        +    if context.req.method == "GET":
        +        # Send a response with the res object helpers
        +        # `ctx.res.send()` dispatches a string back to the client
        +        return context.res.send("Hello, World!")
        +
        +    # `ctx.res.json()` is a handy helper for sending JSON
        +    return context.res.json(
        +        {
        +            "motto": "Build Fast. Scale Big. All in One Place.",
        +            "learn": "https://appwrite.io/docs",
        +            "connect": "https://appwrite.io/discord",
        +            "getInspired": "https://builtwith.appwrite.io",
        +        }
        +    )
        +
        +
      • +
      • +

        Ruby

        +
        +
        require "appwrite"
        +
        +# This is your Appwrite function
        +# It's executed each time we get a request
        +def main(context)
        +  # Why not try the Appwrite SDK?
        +  #
        +  # client = Appwrite::Client.new
        +  # client
        +  #   .set_endpoint('https://cloud.appwrite.io/v1')
        +  #   .set_project(ENV['APPWRITE_FUNCTION_PROJECT_ID'])
        +  #   .set_key(ENV['APPWRITE_API_KEY'])
        +
        +  # You can log messages to the console
        +  context.log("Hello, Logs!")
        +
        +  # If something goes wrong, log an error
        +  context.error("Hello, Errors!")
        +
        +  # The `ctx.req` object contains the request data
        +  if (context.req.method == "GET")
        +    # Send a response with the res object helpers
        +    # `ctx.res.send()` dispatches a string back to the client
        +    return context.res.send("Hello, World!")
        +  end
        +
        +  # `ctx.res.json()` is a handy helper for sending JSON
        +  return context.res.json(
        +           {
        +             "motto": "Build Fast. Scale Big. All in One Place.",
        +             "learn": "https://appwrite.io/docs",
        +             "connect": "https://appwrite.io/discord",
        +             "getInspired": "https://builtwith.appwrite.io",
        +           }
        +         )
        +end
        +
        +
      • +
      • +

        Deno

        +
        +
        import { Client } from "https://deno.land/x/appwrite@7.0.0/mod.ts";
        +
        +// This is your Appwrite function
        +// It's executed each time we get a request
        +export default ({ req, res, log, error }: any) => {
        +  // Why not try the Appwrite SDK?
        +  //
        +  // const client = new Client()
        +  //    .setEndpoint('https://cloud.appwrite.io/v1')
        +  //    .setProject(Deno.env.get("APPWRITE_FUNCTION_PROJECT_ID"))
        +  //    .setKey(Deno.env.get("APPWRITE_API_KEY"));
        +
        +  // You can log messages to the console
        +  log("Hello, Logs!");
        +
        +  // If something goes wrong, log an error
        +  error("Hello, Errors!");
        +
        +  // The `req` object contains the request data
        +  if (req.method === "GET") {
        +    // Send a response with the res object helpers
        +    // `res.send()` dispatches a string back to the client
        +    return res.send("Hello, World!");
        +  }
        +
        +  // `res.json()` is a handy helper for sending JSON
        +  return res.json({
        +    motto: "Build Fast. Scale Big. All in One Place.",
        +    learn: "https://appwrite.io/docs",
        +    connect: "https://appwrite.io/discord",
        +    getInspired: "https://builtwith.appwrite.io",
        +  });
        +};
        +
        +
      • +
      • +

        Dart

        +
        +
        import 'dart:async';
        +import 'package:dart_appwrite/dart_appwrite.dart';
        +
        +// This is your Appwrite function
        +// It's executed each time we get a request
        +Future main(final context) async {
        +// Why not try the Appwrite SDK?
        +  //
        +  // final client = Client()
        +  //    .setEndpoint('https://cloud.appwrite.io/v1')
        +  //    .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        +  //    .setKey(process.env.APPWRITE_API_KEY);
        +
        +  // You can log messages to the console
        +  context.log('Hello, Logs!');
        +
        +  // If something goes wrong, log an error
        +  context.error('Hello, Errors!');
        +
        +  // The `req` object contains the request data
        +  if (context.req.method == 'GET') {
        +    // Send a response with the res object helpers
        +    // `res.send()` dispatches a string back to the client
        +    return context.res.send('Hello, World!');
        +  }
        +
        +  // `res.json()` is a handy helper for sending JSON
        +  return context.res.json({
        +    'motto': 'Build Fast. Scale Big. All in One Place.',
        +    'learn': 'https://appwrite.io/docs',
        +    'connect': 'https://appwrite.io/discord',
        +    'getInspired': 'https://builtwith.appwrite.io',
        +  });
        +}
        +
        +
      • +
      • +

        Swift

        +
        +
        import Appwrite
        +import AppwriteModels
        +import Foundation
        +
        +// This is your Appwrite function
        +// It's executed each time we get a request
        +func main(context: RuntimeContext) async throws -> RuntimeOutput {
        +    // Why not try the Appwrite SDK?
        +    //
        +    // let client = Client()
        +    //    .setEndpoint("https://cloud.appwrite.io/v1")
        +    //    .setProject(ProcessInfo.processInfo.environment["APPWRITE_FUNCTION_PROJECT_ID"])
        +    //    .setKey(ProcessInfo.processInfo.environment["APPWRITE_API_KEY"]);
        +
        +    // You can log messages to the console
        +    context.log("Hello, Logs!")
        +
        +    // If something goes wrong, log an error
        +    context.error("Hello, Errors!")
        +
        +    // The `context.req` object contains the request data
        +    if context.req.method == "GET" {
        +        // Send a response with the res object helpers
        +        // `res.send()` dispatches a string back to the client
        +        return try context.res.send("Hello, World!")
        +    }
        +
        +    // `context.res.json()` is a handy helper for sending JSON
        +    return try context.res.json([
        +        "motto": "Build Fast. Scale Big. All in One Place.",
        +        "learn": "https://appwrite.io/docs",
        +        "connect": "https://appwrite.io/discord",
        +        "getInspired": "https://builtwith.appwrite.io",
        +    ])
        +}
        +
        +
      • +
      • +

        .NET

        +
        +
        namespace DotNetRuntime;
        +
        +using Appwrite;
        +using Appwrite.Services;
        +using Appwrite.Models;
        +
        +public class Handler {
        +
        +    // This is your Appwrite function
        +    // It"s executed each time we get a request
        +    public async Task Main(RuntimeContext Context) 
        +    {
        +        // Why not try the Appwrite SDK?
        +        //
        +        // var client = new Client()
        +        //     .SetEndpoint("http://cloud.appwrite.io/v1")  
        +        //     .SetProject(Environment.GetEnvironmentVariable("APPWRITE_FUNCTION_PROJECT_ID"))        
        +        //     .SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"))
        +
        +        // You can log messages to the console
        +        Context.Log("Hello, Logs!");
        +
        +        // If something goes wrong, log an error
        +        Context.Error("Hello, Errors!");
        +
        +        // The `Context.Req` object contains the request data
        +        if (Context.Req.Method == "GET") {
        +            // Send a response with the res object helpers
        +            // `Context.Res.Send()` dispatches a string back to the client
        +            return Context.Res.Send("Hello, World!");
        +        }
        +
        +        // `Context.Res.Json()` is a handy helper for sending JSON
        +        return Context.Res.Json(new Dictionary()
        +        {
        +            { "motto", "Build Fast. Scale Big. All in One Place." },
        +            { "learn", "https://appwrite.io/docs" },
        +            { "connect", "https://appwrite.io/discord" },
        +            { "getInspired", "https://builtwith.appwrite.io" },
        +        });
        +    }
        +}
        +
        +
      • +
      • +

        Kotlin

        +
        +
        package io.openruntimes.kotlin.src
        +
        +import io.openruntimes.kotlin.RuntimeContext
        +import io.openruntimes.kotlin.RuntimeOutput
        +import io.appwrite.Client
        +import java.util.HashMap
        +
        +class Main {
        +    // This is your Appwrite function
        +    // It's executed each time we get a request
        +    fun main(context: RuntimeContext): RuntimeOutput {
        +        // Why not try the Appwrite SDK?
        +        // val client = Client().apply {
        +        //    setEndpoint("https://cloud.appwrite.io/v1")
        +        //    setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID"))
        +        //    setKey(System.getenv("APPWRITE_API_KEY"))
        +        // }
        +
        +        // You can log messages to the console
        +        context.log("Hello, Logs!")
        +
        +        // If something goes wrong, log an error
        +        context.error("Hello, Errors!")
        +
        +        // The `context.req` object contains the request data
        +        if (context.req.method == "GET") {
        +            // Send a response with the res object helpers
        +            // `context.res.send()` dispatches a string back to the client
        +            return context.res.send("Hello, World!")
        +        }
        +
        +        // `context.res.json()` is a handy helper for sending JSON
        +        return context.res.json(mutableMapOf(
        +            "motto" to "Build Fast. Scale Big. All in One Place.",
        +            "learn" to "https://appwrite.io/docs",
        +            "connect" to "https://appwrite.io/discord",
        +            "getInspired" to "https://builtwith.appwrite.io"
        +        ))
        +    }
        +}
        +
        +
      • +
      • +

        Java

        +
        +
        package io.openruntimes.java.src;
        +
        +import io.openruntimes.java.RuntimeContext;
        +import io.openruntimes.java.RuntimeOutput;
        +import java.util.HashMap;
        +import io.appwrite.Client;
        +
        +public class Main {
        +
        +    // This is your Appwrite function
        +    // It's executed each time we get a request
        +    public RuntimeOutput main(RuntimeContext context) throws Exception {
        +        // Why not try the Appwrite SDK?
        +        //
        +        // Client client = new Client();
        +        // client
        +        //         .setEndpoint("https://cloud.appwrite.io/v1")
        +        //         .setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID"))
        +        //         .setKey(System.getenv("APPWRITE_API_KEY"));
        +
        +        // You can log messages to the console
        +        context.log("Hello, Logs!");
        +
        +        // If something goes wrong, log an error
        +        context.error("Hello, Errors!");
        +
        +        // The `context.getReq()` object contains the request data
        +        if (context.getReq().getMethod().equals("GET")) {
        +            // Send a response with the res object helpers
        +            // `context.getRes().send()` dispatches a string back to the client
        +            return context.getRes().send("Hello, World!");
        +        }
        +
        +        Map json = new HashMap<>();
        +        json.put("motto", "Build Fast. Scale Big. All in One Place.");
        +        json.put("learn", "https://appwrite.io/docs");
        +        json.put("connect", "https://appwrite.io/discord");
        +        json.put("getInspired", "https://builtwith.appwrite.io");
        +        
        +        // `context.getRes().json()` is a handy helper for sending JSON
        +        return context.getRes().json(json);
        +    }
        +}
        +
        +
      • +
      + +

      If you prefer to learn through more examples like this, explore the examples page.

      The Context Object

      Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite console. - All input, output, anddlogging **must be** handled through the context object passed in. + All input, output, anddlogging must be handled through the context object passed in.

      You'll find these properties in the context object.

      @@ -58,11 +478,11 @@ +

      Destructuring Assignment

      Some languages, namely JavaScript, support destructuring. You'll see us use destructing in examples, which has the following syntax.

      • Node.js

        -
        export default async function ({ req, res, log, error }) {
             log('This is a log!');
        @@ -70,11 +490,9 @@
             return res.send(`This function was called with ${req.method} method!`)
         }
        -
      • Deno

        -
        export default async function ({ req, res, log, error }: any) {
             log('This is a log!');
        @@ -82,7 +500,6 @@
             return res.send(`This function was called with ${req.method} method!`)
         }
        -
      @@ -96,7 +513,6 @@
      • Node.js

        -
        export default async ({ req, res, log }) => {
             log(req.bodyRaw);                     // Raw request body, contains request data
        @@ -114,11 +530,9 @@
             return res.send("All the request parameters are logged to the Appwrite Console.");
         };
        -
      • PHP

        -
        <?php
         return function ($context) {
        @@ -137,11 +551,9 @@ return function ($context) {
             return $context->res->send("All the request parameters are logged to the Appwrite Console.");
         }
        -
      • Python

        -
        import json
         
        @@ -160,11 +572,9 @@ def main(context):
         
             return context.res.send("All the request parameters are logged to the Appwrite Console.")
        -
      • Ruby

        -
        require 'json'
                         
        @@ -184,11 +594,9 @@ def main(context)
             return context.res.send("All the request parameters are logged to the Appwrite Console.")
         end
        -
      • Deno

        -
        export default async ({ req, res, log }: any) => {
             log(req.bodyRaw);                 // Raw request body, contains request data
        @@ -205,11 +613,9 @@ end
        return res.send("All the request parameters are logged to the Appwrite Console.");
  • -
  • Dart

    -
    import 'dart:async';
     import 'dart:convert';
    @@ -230,11 +636,9 @@ Future<dynamic> main(final context) async {
         return context.res.send("All the request parameters are logged to the Appwrite Console.");
     }
    -
  • Swift

    -
    import Foundation
     import Foundation
    @@ -255,11 +659,9 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
         return try context.res.send("All the request parameters are logged to the Appwrite Console.")
     }
    -
  • .NET

    -
    namespace DotNetRuntime;
     
    @@ -284,11 +686,9 @@ public class Handler {
         }
     }
    -
  • Kotlin

    -
    package io.openruntimes.kotlin.src
     
    @@ -316,12 +716,9 @@ class Main {
         }
     }
    - -
  • Java

    -
    package io.openruntimes.java;
     
    @@ -349,7 +746,6 @@ public class Main {
         }
     }
    -
  • @@ -426,199 +822,115 @@ public class Main {
    • Node.js

      -
      export default async ({ req, res, log }) => {
       
           switch (req.query.type) {
               case 'text':
      -            return res.send(
      -                "This is a text response", 
      -                200
      -            );
      +            return res.send("This is a text response", 200);
               case 'json':
      -            return res.json(
      -                {
      -                    "type": "This is a JSON response"
      -                }, 
      -                200
      -            );
      +            return res.json({"type": "This is a JSON response"}, 200);
               case 'redirect':
      -            return res.redirect(
      -                "https://appwrite.io", 
      -                301
      -            );
      +            return res.redirect("https://appwrite.io", 301);
               case 'html':
                   return res.send(
      -                "<h1>This is an HTML response</h1>", 
      -                200,
      -                {
      +                "<h1>This is an HTML response</h1>", 200, {
                           "content-type": "text/html"
      -                }
      -            );
      +                });
               default:
                   return res.empty();
           }
       }
      -
    • PHP

      -
      <?php
       
       return function ($context) {
           switch ($context->req->query['type']) {
               case 'text':
      -            return $context->res->send(
      -                "This is a text response", 
      -                200
      -            );
      +            return $context->res->send("This is a text response", 200);
               case 'json':
      -            return $context->res->json(
      -                [
      -                    "type" => "This is a JSON response"
      -                ], 
      -                200
      -            );
      +            return $context->res->json(["type" => "This is a JSON response"], 200);
               case 'redirect':
      -            return $context->res->redirect(
      -                "https://appwrite.io", 
      -                301
      -            );
      +            return $context->res->redirect("https://appwrite.io", 301);
               case 'html':
      -            return $context->res->send(
      -                "<h1>This is an HTML response</h1>", 
      -                200,
      -                [
      -                    "content-type" => "text/html"
      -                ]
      -            );
      +            return $context->res->send("<h1>This is an HTML response</h1>", 200, [
      +                "content-type" => "text/html"
      +                ]);
               default:
                   return $context->res->empty();
           }
       };
      -
    • Python

      -
      def main(context):
           switch context.req.query['type']:
               case 'text':
      -            return context.res.send(
      -                "This is a text response", 
      -                200
      -            )
      +            return context.res.send("This is a text response", 200)
               case 'json':
      -            return context.res.json(
      -                {
      -                    "type": "This is a JSON response"
      -                }, 
      -                200
      -            )
      +            return context.res.json({"type": "This is a JSON response"}, 200)
               case 'redirect':
      -            return context.res.redirect(
      -                "https://appwrite.io", 
      -                301
      -            )
      +            return context.res.redirect("https://appwrite.io", 301)
               case 'html':
      -            return context.res.send(
      -                "<h1>This is an HTML response</h1>", 
      -                200,
      -                {
      -                    "content-type": "text/html"
      -                }
      -            )
      +            return context.res.send("<h1>This is an HTML response</h1>", 200, {
      +                "content-type": "text/html"
      +            })
               default:
                   return context.res.empty()
      -
    • Ruby

      -
      def main(context)
           case context.req.query['type'] 
               when 'text'
      -            return context.res.send(
      -                "This is a text response", 
      -                200
      -            )
      +            return context.res.send("This is a text response", 200)
               when 'json'
      -            return context.res.json(
      -                {
      -                    "type": "This is a JSON response"
      -                }, 
      -                200
      -            )
      +            return context.res.json({"type": "This is a JSON response"}, 200)
               when 'redirect'
      -            return context.res.redirect(
      -                "https://appwrite.io", 
      -                301
      -            )
      +            return context.res.redirect("https://appwrite.io", 301)
               when 'html'
      -            return context.res.send(
      -                "<h1>This is an HTML response</h1>", 
      -                200,
      -                {
      -                    "content-type": "text/html"
      -                }
      -            )
      +            return context.res.send("<h1>This is an HTML response</h1>", 200, {
      +                "content-type": "text/html"
      +            })
               else
                   return context.res.empty()
           end
       end
      -
    • Deno

      -
      export default async ({ req, res, log }) => {
       
           switch (req.query.type) {
               case 'text':
      -            return res.send(
      -                "This is a text response", 
      -                200
      -            );
      +            return res.send("This is a text response", 200);
               case 'json':
      -            return res.json(
      -                {
      -                    "type": "This is a JSON response"
      -                }, 
      -                200
      -            );
      +            return res.json({type": "This is a JSON response"}, 200);
               case 'redirect':
      -            return res.redirect(
      -                "https://appwrite.io", 
      -                301
      -            );
      +            return res.redirect("https://appwrite.io", 301);
               case 'html':
                   return res.send(
      -                "<h1>This is an HTML response</h1>", 
      -                200,
      -                {
      +                "<h1>This is an HTML response</h1>", 200, {
                           "content-type": "text/html"
      -                }
      -            );
      +                });
               default:
                   return res.empty();
           }
       }
      -
    • Dart

      -
      import 'dart:async';
       
      @@ -629,9 +941,7 @@ Future<dynamic> main(final context) async {
                       .send('This is a text response', 200);
               case 'json':
                   return context.res
      -                .json({
      -                    'type': 'This is a JSON response'
      -                });
      +                .json({'type': 'This is a JSON response'});
               case 'redirect':
                   return context.res
                       .redirect('https://appwrite.io', 301);
      @@ -646,11 +956,9 @@ Future<dynamic> main(final context) async {
           }
       }
      -
    • Swift

      -
      import Foundation
       
      @@ -663,17 +971,17 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
               case "redirect":
                   return try await context.redirect("https://appwrite.io", 301)
               case "html":
      -            return try await context.send("<h1>This is an HTML response</h1>", 200, ["content-type": "text/html"])
      +            return try await context.send("<h1>This is an HTML response</h1>", 200, [
      +                "content-type": "text/html"
      +                ])
               default:
                   return try await context.empty()
           }
       }
      -
    • .NET

      -
      namespace DotNetRuntime;
       
      @@ -689,18 +997,18 @@ public class Handler {
                   case "redirect":
                       return await Context.Redirect("https://appwrite.io", 301);
                   case "html":
      -                return await Context.Send("<h1>This is an HTML response</h1>", 200, new Dictionary<string, string>() { { "content-type", "text/html" } });
      +                return await Context.Send("<h1>This is an HTML response</h1>", 200, new Dictionary<string, string>() {
      +                    { "content-type", "text/html" } 
      +                });
                   default:    
                       return await Context.Empty();
               }
           }
       }
      -
    • Kotlin

      -
      package io.openruntimes.kotlin.src
       
      @@ -719,11 +1027,9 @@ class Main {
           }
       }
      -
    • Java

      -
      package io.openruntimes.java.src;
       
      @@ -751,11 +1057,9 @@ public class Main {
           }
       }
      -
    • C++

      -
      #include "../RuntimeResponse.h"
       #include "../RuntimeRequest.h"
      @@ -787,7 +1091,6 @@ namespace runtime {
         };
       }
      -
    @@ -802,7 +1105,6 @@ namespace runtime {
    • Node.js

      -
      export default async ({ res, log, error }) => {
           log("This is a log, use for logging information to console");
      @@ -812,11 +1114,9 @@ namespace runtime {
           return res.send("Check the Appwrite Console to see logs and errors!");
       };
      -
    • PHP

      -
      <?php
       
      @@ -828,11 +1128,9 @@ return function ($context) {
           return $context->send("Check the Appwrite Console to see logs and errors!");
       };
      -
    • Python

      -
      def main(context):
           context.log("This is a log, use for logging information to console")
      @@ -841,11 +1139,9 @@ return function ($context) {
       
           return context.send("Check the Appwrite Console to see logs and errors!")
      -
    • Ruby

      -
      def main(context)
           context.log("This is a log, use for logging information to console")
      @@ -855,11 +1151,9 @@ return function ($context) {
           return context.send("Check the Appwrite Console to see logs and errors!")
       end
      -
    • Deno

      -
      export default async ({ res, log, error }: any) => {
           log("This is a log, use for logging information to console");
      @@ -869,11 +1163,9 @@ end
      return res.send("Check the Appwrite Console to see logs and errors!"); };
      -
    • Dart

      -
      import 'dart:async';
       
      @@ -885,11 +1177,9 @@ Future<dynamic> main(final context) async {
           return context.send("Check the Appwrite Console to see logs and errors!");
       }
      -
    • Swift

      -
      import Foundation
       
      @@ -901,11 +1191,9 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
           return try context.send("Check the Appwrite Console to see logs and errors!")
       }
      -
    • .NET

      -
      namespace DotNetRuntime;
       
      @@ -920,11 +1208,9 @@ public class Handler {
           }
       }
      -
    • Kotlin

      -
      package io.openruntimes.kotlin.src
       
      @@ -941,11 +1227,9 @@ class Main {
           }
       }
      -
    • Java

      -
      package io.openruntimes.java.src;
       
      @@ -962,11 +1246,9 @@ public class Main {
           }
       }
      -
    • C++

      -
      #include "../RuntimeResponse.h"
       #include "../RuntimeRequest.h"
      @@ -986,7 +1268,6 @@ namespace runtime {
         };
       }
      -

    You can access these logs through the following steps.

    @@ -1057,16 +1338,17 @@ namespace runtime { APPWRITE_FUNCTION_RUNTIME_VERSION + The runtime version of the running function. - +

    Local Environment Variables

    - Local variables will only be accessible in the function they belong to. - Local variables will override global variables when they have conflicting names. + Local environment variables will only be accessible in the function they belong to. + Local environment variables will override global environment variables when they have conflicting names.

    1. In Appwrite Console, navigate to Functions.
    2. @@ -1075,10 +1357,10 @@ namespace runtime {
    3. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
    -

    Global Variables

    +

    Global Environment Variables

    - Global variables are accessible to all Appwrite Functions. - Local variables will override global variables when they have conflicting names. + Global environment variables are accessible to all Appwrite Functions. + Local environment variables will override global environment variables when they have conflicting names.

    1. In Appwrite Console, navigate to your project's Settings page.
    2. @@ -1093,7 +1375,6 @@ namespace runtime {
      • Node.js

        -
        process.env.MY_VAR
        @@ -1101,7 +1382,6 @@ namespace runtime {
      • PHP

        -
        getenv('MY_VAR')
        @@ -1109,75 +1389,57 @@ namespace runtime {
      • Python

        -
        os.environ['MY_VAR']
        -
      • Ruby

        -
        ENV['MY_VAR']
        -
      • Deno

        -
        Deno.env.get('MY_VAR')
        -
      • Dart

        -
        Platform.environment['MY_VAR']
        -
      • Swift

        -
        ProcessInfo.processInfo.environment["MY_VAR"]
        -
      • .NET

        -
        Environment.GetEnvironmentVariable("MY_VAR")
        -
      • Kotlin

        -
        System.getenv("MY_VAR")
        -
      • Java

        -
        System.getenv("MY_VAR")
        -
      • C++

        -
        std::getenv("MY_VAR")
        -
      @@ -1196,7 +1458,7 @@ namespace runtime { Language Package Manager - Install Command + Commands @@ -1262,13 +1524,15 @@ namespace runtime {

      Authenticating with Appwrite is done via an API key or a JWT token. You can read more about authentication in the Server Authentication section of the docs.

      - - -

      Using JWT

      +

      Using with API Key

      +

      + API keys have defined scopes when you create them. + They ignore permissions and operate without a sessions. + Use API keys if the function should act as an admin type role, instead of acting on behalf of a user. +

      • Node.js

        -
        import { Client, Databases, ID } from 'node-appwrite';
         
        @@ -1277,13 +1541,9 @@ export default async ({ req, res, log }) => {
           const client = new Client()
             .setEndpoint('https://cloud.appwrite.io/v1')
             .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        +    // Pass in your API key as an environment variable. Never share API keys with users.
        +    .setKey(process.env.APPWRITE_API_KEY);
         
        -  if (req.headers['x-appwrite-jwt']) {
        -    client.setJWT(req.headers['x-appwrite-jwt'])
        -  } else {
        -    return res.send("Please sign in, JWT not found")
        -  }
        -    
           const databases = new Databases(client);
         
           try {
        @@ -1295,11 +1555,9 @@ export default async ({ req, res, log }) => {
           return res.send("Document created")
         }
        -
      • PHP

        -
        <?php
         
        @@ -1315,12 +1573,8 @@ return function ($context) {
             $client
                 ->setEndpoint('https://cloud.appwrite.io/v1')
                 ->setProject(getenv('APPWRITE_FUNCTION_PROJECT_ID'))
        -    
        -    if (isset($context->req->headers['x-appwrite-jwt'])) {
        -        $client->setJWT($context->req->headers['x-appwrite-jwt']);
        -    } else {
        -        return $context->res->send("Please sign in, JWT not found");
        -    }
        +        // Pass in your API key as an environment variable. Never share API keys with users.
        +        ->setKey(getenv('APPWRITE_API_KEY'));
             
             $databases = new Databases($client);
         
        @@ -1333,11 +1587,9 @@ return function ($context) {
             return $context->res->send("Document created");
         };
        -
      • Python

        -
        from appwrite.client import Client
         from appwrite.services.databases import Databases
        @@ -1345,19 +1597,15 @@ from appwrite.id import ID
         
         import os
         
        -
         def main(context):
             client = (
                 Client()
                 .set_endpoint("https://cloud.appwrite.io/v1")
                 .set_project(os.environ["APPWRITE_FUNCTION_PROJECT_ID"])
        +        # Pass in your API key as an environment variable. Never share API keys with users.
        +        .set_key(os.environ["APPWRITE_API_KEY"])
             )
         
        -    if "x-appwrite-jwt" in context.req.headers:
        -        client.set_jwt(context.req.headers["x-appwrite-jwt"])
        -    else:
        -        return context.res.send("Please sign in, JWT not found")
        -
             databases = Databases(client)
         
             try:
        @@ -1367,11 +1615,9 @@ def main(context):
         
             return context.response.send("Document created")
        -
      • Ruby

        -
        require "appwrite"
         
        @@ -1380,12 +1626,8 @@ def main(context)
           client
             .set_endpoint('https://cloud.appwrite.io/v1')
             .set_project(req.variables['APPWRITE_FUNCTION_PROJECT_ID'])
        -    
        -    if context.request.headers['x-appwrite-jwt']
        -        client.set_jwt(context.request.headers['x-appwrite-jwt'])
        -    else
        -        return context.response.send("Please sign in, JWT not found")
        -    end
        +    # Pass in your API key as an environment variable. Never share API keys with users.
        +    .set_key(req.variables['APPWRITE_API_KEY'])
         
             databases = Appwrite::Databases.new(client)
         
        @@ -1398,11 +1640,9 @@ def main(context)
             return context.response.send("Document created")
         end
        -
      • Deno

        -
        import { Client, Databases, ID } from "https://deno.land/x/appwrite/mod.ts";
                         
        @@ -1411,12 +1651,8 @@ export default function ({req, res}: any){
             client
                 .setEndpoint("https://cloud.appwrite.io/v1")
                 .setProject(Deno.env.get("APPWRITE_FUNCTION_PROJECT_ID") || "")
        -    
        -    if (req.headers["x-appwrite-jwt"]) {
        -        client.setJWT(req.headers["x-appwrite-jwt"]);
        -    } else {
        -        return res.send("Please sign in, JWT not found");
        -    }
        +        // Pass in your API key as an environment variable. Never share API keys with users.
        +        .setKey(Deno.env.get("APPWRITE_API_KEY") || "");
             
             const databases = new Databases(client);
             
        @@ -1429,11 +1665,9 @@ export default function ({req, res}: any){
             return res.send("Document created");
         }
        -
      • Dart

        -
        import 'dart:async';
         import 'package:dart_appwrite/dart_appwrite.dart';
        @@ -1443,12 +1677,8 @@ Future<dynamic> main(final context) async {
             final client = Client()
                 .setEndpoint('https://cloud.appwrite.io/v1')
                 .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        -    
        -    if (context.req.headers['x-appwrite-jwt'] != null) {
        -        client.setJWT(context.req.headers['x-appwrite-jwt']);
        -    } else {
        -        return context.res.send("Please sign in, JWT not found");
        -    }
        +        // Pass in your API key as an environment variable. Never share API keys with users.
        +        .setKey(process.env.APPWRITE_API_KEY);
             
             final databases = Databases(client);
             
        @@ -1461,11 +1691,9 @@ Future<dynamic> main(final context) async {
             return context.res.send("Document created");
         }
        -
      • Swift

        -
        import Appwrite
         import AppwriteModels
        @@ -1475,12 +1703,8 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
             let client = Client()
                .setEndpoint("https://cloud.appwrite.io/v1")
                .setProject(ProcessInfo.processInfo.environment["APPWRITE_FUNCTION_PROJECT_ID"])
        -    
        -    if let jwt = context.req.headers["x-appwrite-jwt"] {
        -        client.setJWT(jwt)
        -    } else {
        -        return context.res.send("Please sign in, JWT not found")
        -    }
        +       // Pass in your API key as an environment variable. Never share API keys with users.
        +       .setKey(ProcessInfo.processInfo.environment["APPWRITE_API_KEY"]);
         
             let databases = Databases(client: client)
         
        @@ -1493,11 +1717,9 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
             return context.res.send("Document created")
         }
        -
      • .NET

        -
        namespace DotNetRuntime;
         
        @@ -1512,30 +1734,24 @@ public class Handler {
                 var client = new Client()
                    .SetEndpoint("http://cloud.appwrite.io/v1")  
                    .SetProject(Environment.GetEnvironmentVariable("APPWRITE_FUNCTION_PROJECT_ID"))       
        -        
        -        if (Context.Req.Headers.ContainsKey("x-appwrite-jwt")) {
        -            client.SetJWT(Context.Req.Headers["x-appwrite-jwt"]);
        -        } else {
        -            return Context.Res.Send("Please sign in, JWT not found");
        -        }
        +            // Pass in your API key as an environment variable. Never share API keys with users.
        +           .SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"))
         
                 var databases = new Databases(client);
         
                 try {
                     await databases.CreateDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.Unique(), new Dictionary<string, object>());
                 } catch (Exception e) {
        -            return Context.Res.Send("We messed up, document not created");
        +            return Context.Response.Send("We messed up, document not created");
                 }
         
        -        return Context.Res.Send("Document created");
        +        return Context.Response.Send("Document created");
             }
         }
        -
      • Kotlin

        -
        package io.openruntimes.kotlin.src
         
        @@ -1551,12 +1767,8 @@ class Main {
                 val client = Client().apply {
                    setEndpoint("https://cloud.appwrite.io/v1")
                    setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID"))
        -        }
        -
        -        if (context.req.headers["x-appwrite-jwt"] != null) {
        -            client.setJWT(context.req.headers["x-appwrite-jwt"])
        -        } else {
        -            return context.res.send("Please sign in, JWT not found")
        +           // Pass in your API key as an environment variable. Never share API keys with users.
        +           setKey(System.getenv("APPWRITE_API_KEY"))
                 }
         
                 val databases = Databases(client)
        @@ -1571,11 +1783,9 @@ class Main {
             }
         }
        -
      • Java

        -
        package io.openruntimes.java.src;
         
        @@ -1590,12 +1800,8 @@ public class Main {
                 client
                         .setEndpoint("https://cloud.appwrite.io/v1")
                         .setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID"))
        -                
        -        if (context.req.headers.containsKey("x-appwrite-jwt")) {
        -            client.setJWT(context.req.headers.get("x-appwrite-jwt"));
        -        } else {
        -            return context.res.send("Please sign in, JWT not found");
        -        }
        +                // Pass in your API key as an environment variable. Never share API keys with users.
        +                .setKey(System.getenv("APPWRITE_API_KEY"));
         
                 Databases databases = new Databases(client);
         
        @@ -1610,15 +1816,24 @@ public class Main {
             }
         }
        -
      -

      Using with API Key

      + +

      Using JWT

      +

      + JWTs allow you to act on behalf of an user in your Appwrite Function. + When using JWTs, you will be able to access and change only the resources with the same permissions as the user account that signed the JWT. + This preserves the permissions you configured on each resource. +

      + +

      + If the Appwrite Function is invoked by an authenticated user, the x-appwrite-user-jwt header is automatically passed in. +

      +
      • Node.js

        -
        import { Client, Databases, ID } from 'node-appwrite';
         
        @@ -1627,9 +1842,13 @@ export default async ({ req, res, log }) => {
           const client = new Client()
             .setEndpoint('https://cloud.appwrite.io/v1')
             .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        -    // Put your secret key in environment variables, so it doesn't leak
        -    .setKey(process.env.APPWRITE_API_KEY);
         
        +  if (req.headers['x-appwrite-user-jwt']) {
        +    client.setJWT(req.headers['x-appwrite-user-jwt'])
        +  } else {
        +    return res.send("Please sign in, JWT not found")
        +  }
        +    
           const databases = new Databases(client);
         
           try {
        @@ -1641,11 +1860,9 @@ export default async ({ req, res, log }) => {
           return res.send("Document created")
         }
        -
      • PHP

        -
        <?php
         
        @@ -1661,8 +1878,12 @@ return function ($context) {
             $client
                 ->setEndpoint('https://cloud.appwrite.io/v1')
                 ->setProject(getenv('APPWRITE_FUNCTION_PROJECT_ID'))
        -        // Put your secret key in environment variables, so it doesn't leak
        -        ->setKey(getenv('APPWRITE_API_KEY'));
        +    
        +    if (isset($context->req->headers['x-appwrite-user-jwt'])) {
        +        $client->setJWT($context->req->headers['x-appwrite-user-jwt']);
        +    } else {
        +        return $context->res->send("Please sign in, JWT not found");
        +    }
             
             $databases = new Databases($client);
         
        @@ -1675,11 +1896,9 @@ return function ($context) {
             return $context->res->send("Document created");
         };
        -
      • Python

        -
        from appwrite.client import Client
         from appwrite.services.databases import Databases
        @@ -1693,10 +1912,13 @@ def main(context):
                 Client()
                 .set_endpoint("https://cloud.appwrite.io/v1")
                 .set_project(os.environ["APPWRITE_FUNCTION_PROJECT_ID"])
        -        # Put your secret key in environment variables, so it doesn't leak
        -        .set_key(os.environ["APPWRITE_API_KEY"])
             )
         
        +    if "x-appwrite-user-jwt" in context.req.headers:
        +        client.set_jwt(context.req.headers["x-appwrite-user-jwt"])
        +    else:
        +        return context.res.send("Please sign in, JWT not found")
        +
             databases = Databases(client)
         
             try:
        @@ -1706,11 +1928,9 @@ def main(context):
         
             return context.response.send("Document created")
        -
      • Ruby

        -
        require "appwrite"
         
        @@ -1719,8 +1939,12 @@ def main(context)
           client
             .set_endpoint('https://cloud.appwrite.io/v1')
             .set_project(req.variables['APPWRITE_FUNCTION_PROJECT_ID'])
        -    # Put your secret key in environment variables, so it doesn't leak
        -    .set_key(req.variables['APPWRITE_API_KEY'])
        +    
        +    if context.request.headers['x-appwrite-user-jwt']
        +        client.set_jwt(context.request.headers['x-appwrite-user-jwt'])
        +    else
        +        return context.response.send("Please sign in, JWT not found")
        +    end
         
             databases = Appwrite::Databases.new(client)
         
        @@ -1733,11 +1957,9 @@ def main(context)
             return context.response.send("Document created")
         end
        -
      • Deno

        -
        import { Client, Databases, ID } from "https://deno.land/x/appwrite/mod.ts";
                         
        @@ -1746,8 +1968,12 @@ export default function ({req, res}: any){
             client
                 .setEndpoint("https://cloud.appwrite.io/v1")
                 .setProject(Deno.env.get("APPWRITE_FUNCTION_PROJECT_ID") || "")
        -        // Put your secret key in environment variables, so it doesn't leak
        -        .setKey(Deno.env.get("APPWRITE_API_KEY") || "");
        +    
        +    if (req.headers["x-appwrite-user-jwt"]) {
        +        client.setJWT(req.headers["x-appwrite-user-jwt"]);
        +    } else {
        +        return res.send("Please sign in, JWT not found");
        +    }
             
             const databases = new Databases(client);
             
        @@ -1760,11 +1986,9 @@ export default function ({req, res}: any){
             return res.send("Document created");
         }
        -
      • Dart

        -
        import 'dart:async';
         import 'package:dart_appwrite/dart_appwrite.dart';
        @@ -1774,8 +1998,12 @@ Future<dynamic> main(final context) async {
             final client = Client()
                 .setEndpoint('https://cloud.appwrite.io/v1')
                 .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        -        // Put your secret key in environment variables, so it doesn't leak
        -        .setKey(process.env.APPWRITE_API_KEY);
        +    
        +    if (context.req.headers['x-appwrite-user-jwt'] != null) {
        +        client.setJWT(context.req.headers['x-appwrite-user-jwt']);
        +    } else {
        +        return context.res.send("Please sign in, JWT not found");
        +    }
             
             final databases = Databases(client);
             
        @@ -1788,11 +2016,9 @@ Future<dynamic> main(final context) async {
             return context.res.send("Document created");
         }
        -
      • Swift

        -
        import Appwrite
         import AppwriteModels
        @@ -1802,8 +2028,12 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
             let client = Client()
                .setEndpoint("https://cloud.appwrite.io/v1")
                .setProject(ProcessInfo.processInfo.environment["APPWRITE_FUNCTION_PROJECT_ID"])
        -       // Put your secret key in environment variables, so it doesn't leak
        -       .setKey(ProcessInfo.processInfo.environment["APPWRITE_API_KEY"]);
        +    
        +    if let jwt = context.req.headers["x-appwrite-user-jwt"] {
        +        client.setJWT(jwt)
        +    } else {
        +        return context.res.send("Please sign in, JWT not found")
        +    }
         
             let databases = Databases(client: client)
         
        @@ -1816,11 +2046,9 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
             return context.res.send("Document created")
         }
        -
      • .NET

        -
        namespace DotNetRuntime;
         
        @@ -1835,26 +2063,28 @@ public class Handler {
                 var client = new Client()
                    .SetEndpoint("http://cloud.appwrite.io/v1")  
                    .SetProject(Environment.GetEnvironmentVariable("APPWRITE_FUNCTION_PROJECT_ID"))       
        -            // Put your secret key in environment variables, so it doesn't leak
        -           .SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"))
        +        
        +        if (Context.Req.Headers.ContainsKey("x-appwrite-user-jwt")) {
        +            client.SetJWT(Context.Req.Headers["x-appwrite-user-jwt"]);
        +        } else {
        +            return Context.Res.Send("Please sign in, JWT not found");
        +        }
         
                 var databases = new Databases(client);
         
                 try {
                     await databases.CreateDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.Unique(), new Dictionary<string, object>());
                 } catch (Exception e) {
        -            return Context.Response.Send("We messed up, document not created");
        +            return Context.Res.Send("We messed up, document not created");
                 }
         
        -        return Context.Response.Send("Document created");
        +        return Context.Res.Send("Document created");
             }
         }
        -
      • Kotlin

        -
        package io.openruntimes.kotlin.src
         
        @@ -1870,8 +2100,12 @@ class Main {
                 val client = Client().apply {
                    setEndpoint("https://cloud.appwrite.io/v1")
                    setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID"))
        -           // Put your secret key in environment variables, so it doesn't leak
        -           setKey(System.getenv("APPWRITE_API_KEY"))
        +        }
        +
        +        if (context.req.headers["x-appwrite-user-jwt"] != null) {
        +            client.setJWT(context.req.headers["x-appwrite-user-jwt"])
        +        } else {
        +            return context.res.send("Please sign in, JWT not found")
                 }
         
                 val databases = Databases(client)
        @@ -1886,11 +2120,9 @@ class Main {
             }
         }
        -
      • Java

        -
        package io.openruntimes.java.src;
         
        @@ -1905,8 +2137,12 @@ public class Main {
                 client
                         .setEndpoint("https://cloud.appwrite.io/v1")
                         .setProject(System.getenv("APPWRITE_FUNCTION_PROJECT_ID"))
        -                // Put your secret key in environment variables, so it doesn't leak
        -                .setKey(System.getenv("APPWRITE_API_KEY"));
        +                
        +        if (context.req.headers.containsKey("x-appwrite-user-jwt")) {
        +            client.setJWT(context.req.headers.get("x-appwrite-user-jwt"));
        +        } else {
        +            return context.res.send("Please sign in, JWT not found");
        +        }
         
                 Databases databases = new Databases(client);
         
        @@ -1921,15 +2157,19 @@ public class Main {
             }
         }
        -
      -

      Using Multiple Files

      + +

      Code Splitting

      +

      + As your functions grow, you may find yourself needing to split your code into multiple files. + This helps you keep your codebase maintainable and easy to read. + Here's how you can accomplish code splitting. +

      • Node.js

        -
        // src/utils.js
         
        @@ -1947,11 +2187,9 @@ export defaut function ({res}) {
             return res.send(add(1, 2));
         }
        -
      • PHP

        -
        // src/utils.php
         
        @@ -1972,7 +2210,6 @@ return function ($context) {
             
      • Python

        -
        // src/utils.py
         
        @@ -1988,11 +2225,9 @@ import utils
         def main(context):
             return context.res.send(utils.add(1, 2))
        -
      • Ruby

        -
        # lib/utils.rb
         
        @@ -2009,11 +2244,9 @@ def main(context)
             return context.res.send(add(1, 2))
         end
        -
      • Deno

        -
        // src/utils.ts
         
        @@ -2030,11 +2263,9 @@ export default function ({res}: {res: any}) {
             return res.send(add(1, 2));
         }
        -
      • Dart

        -
        // lib/utils.dart
         
        @@ -2050,11 +2281,9 @@ Future<dynamic> main(final context) async {
             return context.res.send(add(1, 2));
         }
        -
      • Swift

        -
        // Sources/utils.swift
         
        @@ -2072,11 +2301,9 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
             return context.res.send(add(1, 2))
         }
        -
      • .NET

        -
        // src/Utils.cs
         
        @@ -2102,11 +2329,9 @@ public class Handler {
             }
         }
        -
      • Kotlin

        -
        // src/Utils.kt
         
        @@ -2133,11 +2358,9 @@ class Main {
             }
         }
        -
      • Java

        -
        
         // src/Utils.java
        @@ -2163,7 +2386,6 @@ public class Main {
             }
         }
        -
      diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 54335a66b..4447b3e81 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -14,7 +14,7 @@ The generated domains will look like this.

      -
      https://64d4d22db370ae41a32e.functions.cloud.appwrite.io
      +
      https://64d4d22db370ae41a32e.appwrite.global

      @@ -29,7 +29,7 @@

      -
      curl -X POST https://64d4d22db370ae41a32e.functions.cloud.appwrite.io \
      +    
      curl -X POST https://64d4d22db370ae41a32e.appwrite.global \
           -H "X-Custom-Header: 123" \
           -H "Content-Type: application/json" \
           -d '{"data":"this is json data"}'
      @@ -41,7 +41,9 @@ You can invoke your Appwrite Functions directly from the Appwrite SDKs.

      +

      Learn more about using the Appwrite SDKs +

      Client SDKs

      @@ -416,6 +418,21 @@ public static void main(String[] args) throws Exception { +

      Console

      +

      + Another easy way to test a function is directly in the Appwrite console. + You test a function by hitting the Execute now button and +

      + +setParam('srcLight', '/images-ee/docs/functions-execute-light.png') + ->setParam('srcDark', '/images-ee/docs/functions-execute-dark.png') + ->setParam('alt', '"Create Function" page.') + ->setParam('description', '"Create Function" page.') + ->render(); +?>

      Events

      diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index cce760392..96119f04d 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -48,8 +48,4 @@ $runtimes['dart-2.17']["cloud"] = true; - - -

      -

    3. Learn more about permissions
    4. -

      \ No newline at end of file + \ No newline at end of file From e4e8b0ac2adb26053953c61ddb1f27da490b6483 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 23 Aug 2023 14:43:27 +0000 Subject: [PATCH 55/80] fix view import --- app/views/docs/functions-execute.phtml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 4447b3e81..e66d845da 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -1,3 +1,8 @@ +

      Appwrite Functions executions can be invoked in several ways. Functions can be invoked through the Appwrite SDK and visting its REST endpoint. Functions can also be triggered by events and scheduled executions. From 75e79cbfcdbe59fcfe41dd08b97fb13bab03b8fe Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 23 Aug 2023 15:26:17 +0000 Subject: [PATCH 56/80] fix view import + improve env var examples --- app/views/docs/functions-develop.phtml | 148 ++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 16 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index aa5cfc8b1..89f5a3d2a 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -1372,73 +1372,191 @@ namespace runtime { You can access the environment variables through the systems library of each language.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      VariableDescription
      x-appwrite-trigger + Describes how the function execution was invoked. + Possible values are http, schedule or event. +
      x-appwrite-event + If the function execution was triggered by an event, describes the triggering event. +
      x-appwrite-user-id + If the function execution was invoked by an authenticated user, display the user ID. + This doesn't apply to Appwrite Console users or API keys. +
      x-appwrite-user-jwt + JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. + Learn more about JWT tokens. +
      x-appwrite-country-code + Displays the country code of the configured locale. +
      x-appwrite-continent-code + Displays the continent code of the configured locale. +
      x-appwrite-continent-eu + Describes if the configured local is within the EU. +
      + + +

      Response

      +

      + If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. + The response information will not be logged to the Appwrite Console. + There are several possible ways to send a response, explore them in the following Appwrite Function. +

      +
      • Node.js

        -
        process.env.MY_VAR
        -
        +
        export default async ({ req, res, log }) => {
        +    return res.send(process.env.MY_VAR, 200);
        +}
    5. PHP

      -
      getenv('MY_VAR')
      -
      +
      <?php
      +
      +return function ($context) {
      +    return $context->res->send(getenv('MY_VAR'), 200);
      +};
    6. Python

      -
      os.environ['MY_VAR']
      +
      def main(context):
      +    return context.res.send(os.environ['MY_VAR'], 200)
    7. Ruby

      -
      ENV['MY_VAR']
      +
      def main(context)
      +    return context.res.send(ENV['MY_VAR'], 200)
      +end
    8. Deno

      -
      Deno.env.get('MY_VAR')
      +
      export default async ({ req, res, log }) => {
      +    return res.send(Deno.env.get('MY_VAR'), 200);
      +}
    9. Dart

      -
      Platform.environment['MY_VAR']
      +
      import 'dart:async';
      +
      +Future<dynamic> main(final context) async {
      +    return context.res.send(Platform.environment['MY_VAR'], 200);
      +}
    10. Swift

      -
      ProcessInfo.processInfo.environment["MY_VAR"]
      +
      import Foundation
      +
      +func main(context: RuntimeContext) async throws -> RuntimeOutput {
      +    return try await context.send(ProcessInfo.processInfo.environment["MY_VAR"], 200)
      +}
    11. .NET

      -
      Environment.GetEnvironmentVariable("MY_VAR")
      +
      namespace DotNetRuntime;
      +
      +public class Handler {
      +    public async Task<RuntimeOutput> Main(RuntimeContext Context) 
      +    {
      +        return await Context.Send(Environment.GetEnvironmentVariable("MY_VAR"), 200);
      +    }
      +}
    12. Kotlin

      -
      System.getenv("MY_VAR")
      +
      package io.openruntimes.kotlin.src
      +
      +import io.openruntimes.kotlin.RuntimeContext
      +import io.openruntimes.kotlin.RuntimeOutput
      +
      +class Main {
      +    fun main(context: RuntimeContext): RuntimeOutput {
      +        return context.send(System.getenv("MY_VAR"), 200)
      +    }
      +}
    13. Java

      -
      System.getenv("MY_VAR")
      +
      package io.openruntimes.java.src;
      +
      +import io.openruntimes.java.RuntimeContext;
      +import io.openruntimes.java.RuntimeOutput;
      +
      +public class Main {
      +    public RuntimeOutput main(RuntimeContext context) throws Exception {
      +        return context.send(System.getenv("MY_VAR"), 200);
      +    }
      +}
    14. C++

      -
      std::getenv("MY_VAR")
      +
      #include "../RuntimeResponse.h"
      +#include "../RuntimeRequest.h"
      +#include "../RuntimeOutput.h"
      +#include "../RuntimeContext.h"
      +
      +namespace runtime {
      +  class Handler {
      +    public:
      +      static RuntimeOutput main(RuntimeContext &context) {
      +
      +      return context.send(std::getenv("MY_VAR"), 200);
      +  };
      +}
    15. @@ -2206,7 +2324,6 @@ return function ($context) { return $context->res->send(add(1, 2)); };
      -
    16. Python

      @@ -2362,8 +2479,7 @@ class Main {
    17. Java

      -
      
      -// src/Utils.java
      +            
      // src/Utils.java
       
       package io.openruntimes.java.src;
       
      
      From 0f51947fb0562267bc42bb1655aefc56a23b3778 Mon Sep 17 00:00:00 2001
      From: "Vincent (Wen Yu) Ge" 
      Date: Wed, 23 Aug 2023 16:47:30 +0000
      Subject: [PATCH 57/80] Add Domain name steps in deploy
      
      ---
       app/views/docs/functions-deploy.phtml | 33 ++++++++++++++++++++++++++-
       1 file changed, 32 insertions(+), 1 deletion(-)
      
      diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml
      index 0244a4e5f..7be004807 100644
      --- a/app/views/docs/functions-deploy.phtml
      +++ b/app/views/docs/functions-deploy.phtml
      @@ -164,4 +164,35 @@
       
       

      Domains

      -[TODO: @matej steps to add a custom domain thanks] +

      + Each deployed function can have it's own domain. + By default, one is generated for each of your functions. + You can find the generated domain for your function like this. +

      + +
        +
      1. Navigate to the Appwrite Console's Functions page.
      2. +
      3. Navigate to the Domains tab.
      4. +
      5. In the table, you'll find a link formatted similar to https://64d4d22db370ae41a32e.appwrite.global. This is your preview.
      6. +
      + +

      + You can also add a custom domain, which allows you to build custom REST APIs using nothing but Appwrite Functions. + To do this, you need to first buy and register a domain. + After obtaining a domain, follow these steps to add the domain to Appwrite. +

      + +
        +
      1. Navigate to the Appwrite Console's Functions page.
      2. +
      3. Navigate to the Domains tab.
      4. +
      5. Click on Create domain.
      6. +
      7. Input your domain in the Domain input field and click Next.
      8. +
      9. Copy the CNAME record provided to you, and add it to your domain registar.
      10. +
      11. Click Go to console and wait for the domain name to be verified and certificate to generate.
      12. +
      + +

      + DNS records can take up to 48 hours to propagate after they're added. + Please retry verification over the next 48 hours. + If the domain verification still fails and you have confirmed DNS records are added correctly, please contact support. +

      \ No newline at end of file From ab0cab25181919551aee5283618bda7a71ccc47c Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 23 Aug 2023 18:04:13 +0000 Subject: [PATCH 58/80] Fix some wrong links --- app/views/docs/functions.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 0dc52264d..f45f4415b 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -48,7 +48,7 @@ $image = new View(__DIR__.'/../general/image.phtml'); Explore how Appwrite Functions can be invoked.

      -Learn more about executing functions +Learn more about executing functions

      @@ -64,5 +64,5 @@ $image = new View(__DIR__.'/../general/image.phtml'); Here's a curated list of examples that showcase Appwrite Function's capabilies.

      -Learn more about using function examples +Learn more about using function examples

      \ No newline at end of file From 4d5419288713c0143bd60c529c7ab725dfe2975a Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Wed, 23 Aug 2023 20:54:10 +0000 Subject: [PATCH 59/80] deno cache command fixed --- app/views/docs/functions-develop.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 89f5a3d2a..c251a31ae 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -1602,7 +1602,7 @@ namespace runtime { Deno deno - deno cache + deno cache [ENTRYPOINT_FILE] Dart From 86d262a673eaef85043def166f989218639e7a19 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:11:38 +0100 Subject: [PATCH 60/80] feat: better functions error handling --- app/views/docs/functions-develop.phtml | 1810 ++++++++++++------------ 1 file changed, 915 insertions(+), 895 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index c251a31ae..4817eb1aa 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -10,10 +10,10 @@

        -
      1. The function is invoked.
      2. -
      3. Appwrite passes in request information like headers and environment variables through the context.req object.
      4. -
      5. The runtime executes the code you defined, you can log through the context.log or context.error methods.
      6. -
      7. Function terminates when you return results using context.res.
      8. +
      9. The function is invoked.
      10. +
      11. Appwrite passes in request information like headers and environment variables through the context.req object.
      12. +
      13. The runtime executes the code you defined, you can log through the context.log or context.error methods.
      14. +
      15. Function terminates when you return results using context.res.

      You'll find all of these steps in a simple function like this.

      @@ -452,29 +452,29 @@ public class Main {

      You'll find these properties in the context object.

      - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + +
      PropertyDescription
      PropertyDescription
      reqContains request information like method, body, and headers. See full examples here.
      resContains methods to build a response and return information. See full examples here.
      logLogs information to the Appwrite Console, end users will not be able to see these logs. See full examples here.
      errorLogs errors to the Appwrite Console, end users will not be able to see these errors. See full examples here.
      reqContains request information like method, body, and headers. See full examples here.
      resContains methods to build a response and return information. See full examples here.
      logLogs information to the Appwrite Console, end users will not be able to see these logs. See full examples here.
      errorLogs errors to the Appwrite Console, end users will not be able to see these errors. See full examples here.
      @@ -505,7 +505,7 @@ public class Main {

      Request

      - If you pass data into an Appwrite function, it'll be found in the request object. + If you pass data into an Appwrite function, it'll be found in the request object. This includes all invocation methods, such as data from Appwrite SDKs, HTTP calls, Appwrite events, and browsers visiting the configured domain. Explore the request object with the following function, which logs all request params to the Appwrite Console.

      @@ -750,80 +750,80 @@ public class Main {

      Headers

      -

      - Appwrite Functions will always receive a set of headers that provide meta data about the function execution. - These are provided along side any custom headers sent to the function. -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      VariableDescription
      x-appwrite-trigger - Describes how the function execution was invoked. - Possible values are http, schedule or event. -
      x-appwrite-event - If the function execution was triggered by an event, describes the triggering event. -
      x-appwrite-user-id - If the function execution was invoked by an authenticated user, display the user ID. - This doesn't apply to Appwrite Console users or API keys. -
      x-appwrite-user-jwt - JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. - Learn more about JWT tokens. -
      x-appwrite-country-code - Displays the country code of the configured locale. -
      x-appwrite-continent-code - Displays the continent code of the configured locale. -
      x-appwrite-continent-eu - Describes if the configured local is within the EU. -
      - - -

      Response

      -

      - If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. - The response information will not be logged to the Appwrite Console. - There are several possible ways to send a response, explore them in the following Appwrite Function. -

      - -
        -
      • -

        Node.js

        -
        -
        export default async ({ req, res, log }) => {
        +    

        + Appwrite Functions will always receive a set of headers that provide meta data about the function execution. + These are provided along side any custom headers sent to the function. +

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        VariableDescription
        x-appwrite-trigger + Describes how the function execution was invoked. + Possible values are http, schedule or event. +
        x-appwrite-event + If the function execution was triggered by an event, describes the triggering event. +
        x-appwrite-user-id + If the function execution was invoked by an authenticated user, display the user ID. + This doesn't apply to Appwrite Console users or API keys. +
        x-appwrite-user-jwt + JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. + Learn more about JWT tokens. +
        x-appwrite-country-code + Displays the country code of the configured locale. +
        x-appwrite-continent-code + Displays the continent code of the configured locale. +
        x-appwrite-continent-eu + Describes if the configured local is within the EU. +
        + + +

        Response

        +

        + If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. + The response information will not be logged to the Appwrite Console. + There are several possible ways to send a response, explore them in the following Appwrite Function. +

        + +
          +
        • +

          Node.js

          +
          +
          export default async ({ req, res, log }) => {
           
               switch (req.query.type) {
                   case 'text':
          @@ -841,12 +841,12 @@ public class Main {
                       return res.empty();
               }
           }
          -
          -
        • -
        • -

          PHP

          -
          -
          <?php
          +            
          +
        • +
        • +

          PHP

          +
          +
          <?php
           
           return function ($context) {
               switch ($context->req->query['type']) {
          @@ -864,12 +864,12 @@ return function ($context) {
                       return $context->res->empty();
               }
           };
          -
          -
        • -
        • -

          Python

          -
          -
          def main(context):
          +            
          +
        • +
        • +

          Python

          +
          +
          def main(context):
               switch context.req.query['type']:
                   case 'text':
                       return context.res.send("This is a text response", 200)
          @@ -883,12 +883,12 @@ return function ($context) {
                       })
                   default:
                       return context.res.empty()
          -
          -
        • -
        • -

          Ruby

          -
          -
          def main(context)
          +            
          +
        • +
        • +

          Ruby

          +
          +
          def main(context)
               case context.req.query['type'] 
                   when 'text'
                       return context.res.send("This is a text response", 200)
          @@ -904,12 +904,12 @@ return function ($context) {
                       return context.res.empty()
               end
           end
          -
          -
        • -
        • -

          Deno

          -
          -
          export default async ({ req, res, log }) => {
          +            
          +
        • +
        • +

          Deno

          +
          +
          export default async ({ req, res, log }) => {
           
               switch (req.query.type) {
                   case 'text':
          @@ -927,12 +927,12 @@ end
          return res.empty(); } }
        -
        -
      • -
      • -

        Dart

        -
        -
        import 'dart:async';
        +            
        +
      • +
      • +

        Dart

        +
        +
        import 'dart:async';
         
         Future<dynamic> main(final context) async {
             switch (context.req.query['type']) {
        @@ -955,12 +955,12 @@ Future<dynamic> main(final context) async {
                         .empty();
             }
         }
        -
        -
      • -
      • -

        Swift

        -
        -
        import Foundation
        +            
        +
      • +
      • +

        Swift

        +
        +
        import Foundation
         
         func main(context: RuntimeContext) async throws -> RuntimeOutput {
             switch context.req.query["type"] {
        @@ -978,12 +978,12 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
                     return try await context.empty()
             }
         }
        -
        -
      • -
      • -

        .NET

        -
        -
        namespace DotNetRuntime;
        +            
        +
      • +
      • +

        .NET

        +
        +
        namespace DotNetRuntime;
         
         public class Handler {
             public async Task<RuntimeOutput> Main(RuntimeContext Context) 
        @@ -1005,12 +1005,12 @@ public class Handler {
                 }
             }
         }
        -
        -
      • -
      • -

        Kotlin

        -
        -
        package io.openruntimes.kotlin.src
        +            
        +
      • +
      • +

        Kotlin

        +
        +
        package io.openruntimes.kotlin.src
         
         import io.openruntimes.kotlin.RuntimeContext
         import io.openruntimes.kotlin.RuntimeOutput
        @@ -1026,12 +1026,12 @@ class Main {
                 }
             }
         }
        -
        -
      • -
      • -

        Java

        -
        -
        package io.openruntimes.java.src;
        +            
        +
      • +
      • +

        Java

        +
        +
        package io.openruntimes.java.src;
         
         import io.openruntimes.java.RuntimeContext;
         import io.openruntimes.java.RuntimeOutput;
        @@ -1056,12 +1056,12 @@ public class Main {
                 }
             }
         }
        -
        -
      • -
      • -

        C++

        -
        -
        #include "../RuntimeResponse.h"
        +            
        +
      • +
      • +

        C++

        +
        +
        #include "../RuntimeResponse.h"
         #include "../RuntimeRequest.h"
         #include "../RuntimeOutput.h"
         #include "../RuntimeContext.h"
        @@ -1090,35 +1090,35 @@ namespace runtime {
               }
           };
         }
        -
        -
      • -
      - -

      Logging

      -

      - To protect user privacy, the request and response objects are not logged to the Appwrite Console by default. - This means, to see logs or debug function executions you need to use the log() and error() methods. - These logs are only visible to developers with access to the Appwrite Console. -

      - -

      Here's an example of using logs and errors.

      -
        -
      • -

        Node.js

        -
        -
        export default async ({ res, log, error }) => {
        +            
        +
      • +
      + +

      Logging

      +

      + To protect user privacy, the request and response objects are not logged to the Appwrite Console by default. + This means, to see logs or debug function executions you need to use the log() and error() methods. + These logs are only visible to developers with access to the Appwrite Console. +

      + +

      Here's an example of using logs and errors.

      +
        +
      • +

        Node.js

        +
        +
        export default async ({ res, log, error }) => {
             log("This is a log, use for logging information to console");
             log(`This function was called with ${context.req.method} method`);
             error("This is an error, use for logging errors to console");
         
             return res.send("Check the Appwrite Console to see logs and errors!");
         };
        -
        -
      • -
      • -

        PHP

        -
        -
        <?php
        +            
        +
      • +
      • +

        PHP

        +
        +
        <?php
         
         return function ($context) {
             $context->log("This is a log, use for logging information to console");
        @@ -1127,47 +1127,47 @@ return function ($context) {
         
             return $context->send("Check the Appwrite Console to see logs and errors!");
         };
        -
        -
      • -
      • -

        Python

        -
        -
        def main(context):
        +            
        +
      • +
      • +

        Python

        +
        +
        def main(context):
             context.log("This is a log, use for logging information to console")
             context.log(f"This function was called with {context.req.method} method")
             context.error("This is an error, use for logging errors to console")
         
             return context.send("Check the Appwrite Console to see logs and errors!")
        -
        -
      • -
      • -

        Ruby

        -
        -
        def main(context)
        +            
        +
      • +
      • +

        Ruby

        +
        +
        def main(context)
             context.log("This is a log, use for logging information to console")
             context.log("This function was called with #{context.req.method} method")
             context.error("This is an error, use for logging errors to console")
         
             return context.send("Check the Appwrite Console to see logs and errors!")
         end
        -
        -
      • -
      • -

        Deno

        -
        -
        export default async ({ res, log, error }: any) => {
        +            
        +
      • +
      • +

        Deno

        +
        +
        export default async ({ res, log, error }: any) => {
             log("This is a log, use for logging information to console");
             log(`This function was called with ${context.req.method} method`);
             error("This is an error, use for logging errors to console");
         
             return res.send("Check the Appwrite Console to see logs and errors!");
         };
        -
        -
      • -
      • -

        Dart

        -
        -
        import 'dart:async';
        +            
        +
      • +
      • +

        Dart

        +
        +
        import 'dart:async';
         
         Future<dynamic> main(final context) async {
             context.log("This is a log, use for logging information to console");
        @@ -1176,12 +1176,12 @@ Future<dynamic> main(final context) async {
         
             return context.send("Check the Appwrite Console to see logs and errors!");
         }
        -
        -
      • -
      • -

        Swift

        -
        -
        import Foundation
        +            
        +
      • +
      • +

        Swift

        +
        +
        import Foundation
         
         func main(context: RuntimeContext) async throws -> RuntimeOutput {
             context.log("This is a log, use for logging information to console")
        @@ -1190,12 +1190,12 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
         
             return try context.send("Check the Appwrite Console to see logs and errors!")
         }
        -
        -
      • -
      • -

        .NET

        -
        -
        namespace DotNetRuntime;
        +            
        +
      • +
      • +

        .NET

        +
        +
        namespace DotNetRuntime;
         
         public class Handler {
             public async Task<RuntimeOutput> Main(RuntimeContext Context) 
        @@ -1207,12 +1207,12 @@ public class Handler {
                 return await Context.Send("Check the Appwrite Console to see logs and errors!");
             }
         }
        -
        -
      • -
      • -

        Kotlin

        -
        -
        package io.openruntimes.kotlin.src
        +            
        +
      • +
      • +

        Kotlin

        +
        +
        package io.openruntimes.kotlin.src
         
         import io.openruntimes.kotlin.RuntimeContext
         import io.openruntimes.kotlin.RuntimeOutput
        @@ -1226,12 +1226,12 @@ class Main {
                 return context.send("Check the Appwrite Console to see logs and errors!")
             }
         }
        -
        -
      • -
      • -

        Java

        -
        -
        package io.openruntimes.java.src;
        +            
        +
      • +
      • +

        Java

        +
        +
        package io.openruntimes.java.src;
         
         import io.openruntimes.java.RuntimeContext;
         import io.openruntimes.java.RuntimeOutput;
        @@ -1245,12 +1245,12 @@ public class Main {
                 return context.send("Check the Appwrite Console to see logs and errors!");
             }
         }
        -
        -
      • -
      • -

        C++

        -
        -
        #include "../RuntimeResponse.h"
        +            
        +
      • +
      • +

        C++

        +
        +
        #include "../RuntimeResponse.h"
         #include "../RuntimeRequest.h"
         #include "../RuntimeOutput.h"
         #include "../RuntimeContext.h"
        @@ -1267,394 +1267,394 @@ namespace runtime {
               }
           };
         }
        -
        -
      • -
      -

      You can access these logs through the following steps.

      -
        -
      1. In Appwrite Console, navigate to Functions.
      2. -
      3. Click to open a function you wish to inspect.
      4. -
      5. Under the Executions tab, click on an execution.
      6. -
      7. In the Response section, you'll be able to view logs under the Logs and Errors tabs.
      8. -
      +
      +
    18. + +

      You can access these logs through the following steps.

      +
        +
      1. In Appwrite Console, navigate to Functions.
      2. +
      3. Click to open a function you wish to inspect.
      4. +
      5. Under the Executions tab, click on an execution.
      6. +
      7. In the Response section, you'll be able to view logs under the Logs and Errors tabs.
      8. +
      + +

      Accessing Environment Variables

      +

      + If you need to pass constants or secrets to Appwrite Functions, you can use environment variables. + Environmental variables can be global, or function specific. +

      + +

      Default Environment Variables

      +

      + Appwrite runtimes passes in some environment variables by default. + These are always accesible for every function at runtime. +

      + +
      +

      Appwrite API keys

      +

      + If your function is using an Appwrite SDK with an API key, this API key needs to be generated and passed in manually. + API keys are not passed by default for security reasons. +

      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      VariableDescription
      APPWRITE_FUNCTION_ID + The ID of the running function. +
      APPWRITE_FUNCTION_NAME + The Name of the running function. +
      APPWRITE_FUNCTION_DEPLOYMENT + The deployment ID of the running function. +
      APPWRITE_FUNCTION_PROJECT_ID + The project ID of the running function. +
      APPWRITE_FUNCTION_RUNTIME_NAME + The runtime of the running function. +
      APPWRITE_FUNCTION_RUNTIME_VERSION + The runtime version of the running function. +
      + +

      Local Environment Variables

      +

      + Local environment variables will only be accessible in the function they belong to. + Local environment variables will override global environment variables when they have conflicting names. +

      +
        +
      1. In Appwrite Console, navigate to Functions.
      2. +
      3. Click to open a function you wish to add variables to.
      4. +
      5. Under the Settings tab, navigate to Environment variables.
      6. +
      7. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
      8. +
      + +

      Global Environment Variables

      +

      + Global environment variables are accessible to all Appwrite Functions. + Local environment variables will override global environment variables when they have conflicting names. +

      +
        +
      1. In Appwrite Console, navigate to your project's Settings page.
      2. +
      3. Navigate to Global variables section.
      4. +
      5. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
      6. +
      + +

      + You can access the environment variables through the systems library of each language. +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      VariableDescription
      x-appwrite-trigger + Describes how the function execution was invoked. + Possible values are http, schedule or event. +
      x-appwrite-event + If the function execution was triggered by an event, describes the triggering event. +
      x-appwrite-user-id + If the function execution was invoked by an authenticated user, display the user ID. + This doesn't apply to Appwrite Console users or API keys. +
      x-appwrite-user-jwt + JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. + Learn more about JWT tokens. +
      x-appwrite-country-code + Displays the country code of the configured locale. +
      x-appwrite-continent-code + Displays the continent code of the configured locale. +
      x-appwrite-continent-eu + Describes if the configured local is within the EU. +
      + + +

      Response

      +

      + If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. + The response information will not be logged to the Appwrite Console. + There are several possible ways to send a response, explore them in the following Appwrite Function. +

      + +
        +
      • +

        Node.js

        +
        +
        export default async ({ req, res, log }) => {
        +    return res.send(process.env.MY_VAR, 200);
        +}
        +
        +
      • +
      • +

        PHP

        +
        +
        <?php
         
        -

        Accessing Environment Variables

        -

        - If you need to pass constants or secrets to Appwrite Functions, you can use environment variables. - Environmental variables can be global, or function specific. -

        +return function ($context) { + return $context->res->send(getenv('MY_VAR'), 200); +};
        +
        +
      • +
      • +

        Python

        +
        +
        def main(context):
        +    return context.res.send(os.environ['MY_VAR'], 200)
        +
        +
      • +
      • +

        Ruby

        +
        +
        def main(context)
        +    return context.res.send(ENV['MY_VAR'], 200)
        +end
        +
        +
      • +
      • +

        Deno

        +
        +
        export default async ({ req, res, log }) => {
        +    return res.send(Deno.env.get('MY_VAR'), 200);
        +}
        +
        +
      • +
      • +

        Dart

        +
        +
        import 'dart:async';
         
        -

        Default Environment Variables

        -

        - Appwrite runtimes passes in some environment variables by default. - These are always accesible for every function at runtime. -

        +Future<dynamic> main(final context) async { + return context.res.send(Platform.environment['MY_VAR'], 200); +}
        +
        +
      • +
      • +

        Swift

        +
        +
        import Foundation
         
        -
        -

        Appwrite API keys

        -

        - If your function is using an Appwrite SDK with an API key, this API key needs to be generated and passed in manually. - API keys are not passed by default for security reasons. -

        -
        +func main(context: RuntimeContext) async throws -> RuntimeOutput { + return try await context.send(ProcessInfo.processInfo.environment["MY_VAR"], 200) +}
        +
        +
      • +
      • +

        .NET

        +
        +
        namespace DotNetRuntime;
         
        -
        -    
        -        
        -            
        -            
        -        
        -    
        -    
        -        
        -            
        -            
        -        
        -        
        -            
        -            
        -        
        -        
        -            
        -            
        -        
        +public class Handler {
        +    public async Task<RuntimeOutput> Main(RuntimeContext Context) 
        +    {
        +        return await Context.Send(Environment.GetEnvironmentVariable("MY_VAR"), 200);
        +    }
        +}
        +            
        +        
        +        
      • +

        Kotlin

        +
        +
        package io.openruntimes.kotlin.src
        +
        +import io.openruntimes.kotlin.RuntimeContext
        +import io.openruntimes.kotlin.RuntimeOutput
        +
        +class Main {
        +    fun main(context: RuntimeContext): RuntimeOutput {
        +        return context.send(System.getenv("MY_VAR"), 200)
        +    }
        +}
        +
        +
      • +
      • +

        Java

        +
        +
        package io.openruntimes.java.src;
        +
        +import io.openruntimes.java.RuntimeContext;
        +import io.openruntimes.java.RuntimeOutput;
        +
        +public class Main {
        +    public RuntimeOutput main(RuntimeContext context) throws Exception {
        +        return context.send(System.getenv("MY_VAR"), 200);
        +    }
        +}
        +
        +
      • +
      • +

        C++

        +
        +
        #include "../RuntimeResponse.h"
        +#include "../RuntimeRequest.h"
        +#include "../RuntimeOutput.h"
        +#include "../RuntimeContext.h"
        +
        +namespace runtime {
        +  class Handler {
        +    public:
        +      static RuntimeOutput main(RuntimeContext &context) {
        +
        +      return context.send(std::getenv("MY_VAR"), 200);
        +  };
        +}
        +
        +
      • + + +

        Dependencies

        + +

        + Your function's dependencies should be managed by the package manager of each language. By default, we include the following package managers in each runtime: +

        + +

        + To install your dependencies before your function is built, you should add the relevant install command to the top your functions Build Commands script. +

        + +
        VariableDescription
        APPWRITE_FUNCTION_ID - The ID of the running function. -
        APPWRITE_FUNCTION_NAME - The Name of the running function. -
        APPWRITE_FUNCTION_DEPLOYMENT - The deployment ID of the running function. -
        + + + + + + + - - + + + - - + + + - - + + + - -
        LanguagePackage ManagerCommands
        APPWRITE_FUNCTION_PROJECT_ID - The project ID of the running function. - Node.jsnpmnpm install
        APPWRITE_FUNCTION_RUNTIME_NAME - The runtime of the running function. - PHPComposercomposer install
        APPWRITE_FUNCTION_RUNTIME_VERSION - The runtime version of the running function. - Pythonpippip install -r requirements.txt
        - -

        Local Environment Variables

        -

        - Local environment variables will only be accessible in the function they belong to. - Local environment variables will override global environment variables when they have conflicting names. -

        -
          -
        1. In Appwrite Console, navigate to Functions.
        2. -
        3. Click to open a function you wish to add variables to.
        4. -
        5. Under the Settings tab, navigate to Environment variables.
        6. -
        7. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
        8. -
        - -

        Global Environment Variables

        -

        - Global environment variables are accessible to all Appwrite Functions. - Local environment variables will override global environment variables when they have conflicting names. -

        -
          -
        1. In Appwrite Console, navigate to your project's Settings page.
        2. -
        3. Navigate to Global variables section.
        4. -
        5. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
        6. -
        - -

        - You can access the environment variables through the systems library of each language. -

        - - - - - + + + - - - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - -
        VariableDescriptionRubyBundlerbundle install
        x-appwrite-trigger - Describes how the function execution was invoked. - Possible values are http, schedule or event. - Denodenodeno cache [ENTRYPOINT_FILE]
        x-appwrite-event - If the function execution was triggered by an event, describes the triggering event. - Dartpubpub get
        x-appwrite-user-id - If the function execution was invoked by an authenticated user, display the user ID. - This doesn't apply to Appwrite Console users or API keys. - SwiftSwift Package ManagerN/A
        x-appwrite-user-jwt - JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. - Learn more about JWT tokens. - .NETNuGetN/A
        x-appwrite-country-code - Displays the country code of the configured locale. - KotlinGradleN/A
        x-appwrite-continent-code - Displays the continent code of the configured locale. - JavaGradleN/A
        x-appwrite-continent-eu - Describes if the configured local is within the EU. - C++NoneN/A
        + +

        Using Appwrite in a Function

        -

        Response

        -

        - If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. - The response information will not be logged to the Appwrite Console. - There are several possible ways to send a response, explore them in the following Appwrite Function. -

        - -
          -
        • -

          Node.js

          -
          -
          export default async ({ req, res, log }) => {
          -    return res.send(process.env.MY_VAR, 200);
          -}
          -
          -
        • -
        • -

          PHP

          -
          -
          <?php
          -
          -return function ($context) {
          -    return $context->res->send(getenv('MY_VAR'), 200);
          -};
          -
          -
        • -
        • -

          Python

          -
          -
          def main(context):
          -    return context.res.send(os.environ['MY_VAR'], 200)
          -
          -
        • -
        • -

          Ruby

          -
          -
          def main(context)
          -    return context.res.send(ENV['MY_VAR'], 200)
          -end
          -
          -
        • -
        • -

          Deno

          -
          -
          export default async ({ req, res, log }) => {
          -    return res.send(Deno.env.get('MY_VAR'), 200);
          -}
          -
          -
        • -
        • -

          Dart

          -
          -
          import 'dart:async';
          +    

          Appwrite can be used in your functions by adding the relevant SDK to your function's dependencies

          +

          Authenticating with Appwrite is done via an API key or a JWT token. You can read more about authentication in the Server Authentication section of the docs.

          -Future<dynamic> main(final context) async { - return context.res.send(Platform.environment['MY_VAR'], 200); -}
          -
          -
        • -
        • -

          Swift

          -
          -
          import Foundation
          -
          -func main(context: RuntimeContext) async throws -> RuntimeOutput {
          -    return try await context.send(ProcessInfo.processInfo.environment["MY_VAR"], 200)
          -}
          -
          -
        • -
        • -

          .NET

          -
          -
          namespace DotNetRuntime;
          -
          -public class Handler {
          -    public async Task<RuntimeOutput> Main(RuntimeContext Context) 
          -    {
          -        return await Context.Send(Environment.GetEnvironmentVariable("MY_VAR"), 200);
          -    }
          -}
          -
          -
        • -
        • -

          Kotlin

          -
          -
          package io.openruntimes.kotlin.src
          -
          -import io.openruntimes.kotlin.RuntimeContext
          -import io.openruntimes.kotlin.RuntimeOutput
           
          -class Main {
          -    fun main(context: RuntimeContext): RuntimeOutput {
          -        return context.send(System.getenv("MY_VAR"), 200)
          -    }
          -}
          -
          -
        • -
        • -

          Java

          -
          -
          package io.openruntimes.java.src;
          +    

          Using with API Key

          +

          + API keys have defined scopes when you create them. + They ignore permissions and operate without a sessions. + Use API keys if the function should act as an admin type role, instead of acting on behalf of a user. +

          +
            +
          • +

            Node.js

            +
            +
            import { Client, Databases, ID } from 'node-appwrite';
             
            -import io.openruntimes.java.RuntimeContext;
            -import io.openruntimes.java.RuntimeOutput;
            -
            -public class Main {
            -    public RuntimeOutput main(RuntimeContext context) throws Exception {
            -        return context.send(System.getenv("MY_VAR"), 200);
            -    }
            -}
            -
            -
          • -
          • -

            C++

            -
            -
            #include "../RuntimeResponse.h"
            -#include "../RuntimeRequest.h"
            -#include "../RuntimeOutput.h"
            -#include "../RuntimeContext.h"
            -
            -namespace runtime {
            -  class Handler {
            -    public:
            -      static RuntimeOutput main(RuntimeContext &context) {
            -
            -      return context.send(std::getenv("MY_VAR"), 200);
            -  };
            -}
            -
            -
          • -
          - -

          Dependencies

          - -

          - Your function's dependencies should be managed by the package manager of each language. By default, we include the following package managers in each runtime: -

          - -

          - To install your dependencies before your function is built, you should add the relevant install command to the top your functions Build Commands script. -

          - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          LanguagePackage ManagerCommands
          Node.jsnpmnpm install
          PHPComposercomposer install
          Pythonpippip install -r requirements.txt
          RubyBundlerbundle install
          Denodenodeno cache [ENTRYPOINT_FILE]
          Dartpubpub get
          SwiftSwift Package ManagerN/A
          .NETNuGetN/A
          KotlinGradleN/A
          JavaGradleN/A
          C++NoneN/A
          - -

          Using Appwrite in a Function

          - -

          Appwrite can be used in your functions by adding the relevant SDK to your function's dependencies

          -

          Authenticating with Appwrite is done via an API key or a JWT token. You can read more about authentication in the Server Authentication section of the docs.

          - - -

          Using with API Key

          -

          - API keys have defined scopes when you create them. - They ignore permissions and operate without a sessions. - Use API keys if the function should act as an admin type role, instead of acting on behalf of a user. -

          -
            -
          • -

            Node.js

            -
            -
            import { Client, Databases, ID } from 'node-appwrite';
            -
            -export default async ({ req, res, log }) => {
            +export default async ({ req, res, log, error }) => {
             
               const client = new Client()
                 .setEndpoint('https://cloud.appwrite.io/v1')
            @@ -1667,17 +1667,18 @@ export default async ({ req, res, log }) => {
               try {
                 databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {})
               } catch (e) {
            -    return res.send("We messed up, document not created")
            +    error("Failed to create document: " + e.message)
            +    return res.send("Failed to create document")
               }
             
               return res.send("Document created")
             }
            -
            -
          • -
          • -

            PHP

            -
            -
            <?php
            +            
            +
          • +
          • +

            PHP

            +
            +
            <?php
             
             require(__DIR__ . '/../vendor/autoload.php');
             
            @@ -1699,17 +1700,18 @@ return function ($context) {
                 try {
                     $databases->createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID::unique(), []);
                 } catch (Exception $e) {
            -        return $context->res->send("We messed up, document not created");
            +        $context->error("Failed to create document: " . $e->getMessage());
            +        return $context->res->send("Failed to create document");
                 }
             
                 return $context->res->send("Document created");
             };
            -
            -
          • -
          • -

            Python

            -
            -
            from appwrite.client import Client
            +            
            +
          • +
          • +

            Python

            +
            +
            from appwrite.client import Client
             from appwrite.services.databases import Databases
             from appwrite.id import ID
             
            @@ -1729,15 +1731,16 @@ def main(context):
                 try:
                     databases.create_document("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), {})
                 except Exception as e:
            -        return context.response.send("We messed up, document not created")
            +        context.error("Failed to create document: " + e.message)
            +        return context.response.send("Failed to create document")
             
                 return context.response.send("Document created")
            -
            -
          • -
          • -

            Ruby

            -
            -
            require "appwrite"
            +            
            +
          • +
          • +

            Ruby

            +
            +
            require "appwrite"
             
             def main(context)
               client = Appwrite::Client.new
            @@ -1752,19 +1755,20 @@ def main(context)
                 begin
                     databases.create_document('[DATABASE_ID]', '[COLLECTION_ID]', Appwrite::ID.unique(), {})
                 rescue Appwrite::Exception => e
            -        return context.response.send("We messed up, document not created")
            +        context.error("Failed to create document: " + e.message)
            +        return context.response.send("Failed to create document")
                 end
             
                 return context.response.send("Document created")
             end
            -
            -
          • -
          • -

            Deno

            -
            -
            import { Client, Databases, ID } from "https://deno.land/x/appwrite/mod.ts";
            +            
            +
          • +
          • +

            Deno

            +
            +
            import { Client, Databases, ID } from "https://deno.land/x/appwrite/mod.ts";
                             
            -export default function ({req, res}: any){
            +export default function ({req, res, error}: any){
                 const client = new Client();
                 client
                     .setEndpoint("https://cloud.appwrite.io/v1")
            @@ -1777,17 +1781,18 @@ export default function ({req, res}: any){
                 try {
                     databases.createDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), {});
                 } catch (e) {
            -        return res.send("We messed up, document not created");
            +        error("Failed to create document: " + e.message);
            +        return res.send("Failed to create document");
                 }
                 
                 return res.send("Document created");
            -}
            -
            -
          • -
          • -

            Dart

            -
            -
            import 'dart:async';
            +}
            +
            +
          • +
          • +

            Dart

            +
            +
            import 'dart:async';
             import 'package:dart_appwrite/dart_appwrite.dart';
             
             
            @@ -1803,17 +1808,18 @@ Future<dynamic> main(final context) async {
                 try {
                     await databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {});
                 } catch (e) {
            -        return context.res.send("We messed up, document not created");
            +        context.error("Failed to create document: " + e.message);
            +        return context.res.send("Failed to create document");
                 }
             
                 return context.res.send("Document created");
             }
            -
            -
          • -
          • -

            Swift

            -
            -
            import Appwrite
            +            
            +
          • +
          • +

            Swift

            +
            +
            import Appwrite
             import AppwriteModels
             import Foundation
             
            @@ -1829,17 +1835,18 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
                 do {
                     try await databases.createDocument(databaseId: "[DATABASE_ID]", collectionId: "[COLLECTION_ID]", data: [:])
                 } catch {
            -        return context.res.send("We messed up, document not created")
            +        context.error("Failed to create document: \(error.localizedDescription)")
            +        return try await context.res.send("Failed to create document")
                 }
             
                 return context.res.send("Document created")
             }
            -
            -
          • -
          • -

            .NET

            -
            -
            namespace DotNetRuntime;
            +            
            +
          • +
          • +

            .NET

            +
            +
            namespace DotNetRuntime;
             
             using Appwrite;
             using Appwrite.Services;
            @@ -1860,18 +1867,19 @@ public class Handler {
                     try {
                         await databases.CreateDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.Unique(), new Dictionary<string, object>());
                     } catch (Exception e) {
            -            return Context.Response.Send("We messed up, document not created");
            +            Context.Error("Failed to create document: " + e.Message);
            +            return Context.Response.Send("Failed to create document");
                     }
             
                     return Context.Response.Send("Document created");
                 }
             }
            -
            -
          • -
          • -

            Kotlin

            -
            -
            package io.openruntimes.kotlin.src
            +            
            +
          • +
          • +

            Kotlin

            +
            +
            package io.openruntimes.kotlin.src
             
             import io.openruntimes.kotlin.RuntimeContext
             import io.openruntimes.kotlin.RuntimeOutput
            @@ -1894,18 +1902,19 @@ class Main {
                     try {
                         databases.createDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), HashMap())
                     } catch (e: Exception) {
            -            return context.res.send("We messed up, document not created")
            +            context.error("Failed to create document: " + e.message)
            +            return context.res.send("Failed to create document")
                     }
             
                     return context.res.send("Document created")
                 }
             }
            -
            -
          • -
          • -

            Java

            -
            -
            package io.openruntimes.java.src;
            +            
            +
          • +
          • +

            Java

            +
            +
            package io.openruntimes.java.src;
             
             import io.openruntimes.java.RuntimeContext;
             import io.openruntimes.java.RuntimeOutput;
            @@ -1926,34 +1935,35 @@ public class Main {
                     try {
                         databases.createDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), new HashMap<>());
                     } catch (Exception e) {
            -            return context.res.send("We messed up, document not created");
            +            context.error("Failed to create document: " + e.getMessage());
            +            return context.res.send("Failed to create document");
                     }
             
                     return context.res.send("Document created");
             
                 }
             }
            -
            -
          • -
          +
          +
        • +
        -

        Using JWT

        -

        - JWTs allow you to act on behalf of an user in your Appwrite Function. - When using JWTs, you will be able to access and change only the resources with the same permissions as the user account that signed the JWT. - This preserves the permissions you configured on each resource. -

        +

        Using JWT

        +

        + JWTs allow you to act on behalf of an user in your Appwrite Function. + When using JWTs, you will be able to access and change only the resources with the same permissions as the user account that signed the JWT. + This preserves the permissions you configured on each resource. +

        -

        - If the Appwrite Function is invoked by an authenticated user, the x-appwrite-user-jwt header is automatically passed in. -

        +

        + If the Appwrite Function is invoked by an authenticated user, the x-appwrite-user-jwt header is automatically passed in. +

        -
          -
        • -

          Node.js

          -
          -
          import { Client, Databases, ID } from 'node-appwrite';
          +    
            +
          • +

            Node.js

            +
            +
            import { Client, Databases, ID } from 'node-appwrite';
             
             export default async ({ req, res, log }) => {
             
            @@ -1972,17 +1982,18 @@ export default async ({ req, res, log }) => {
               try {
                 databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {})
               } catch (e) {
            -    return res.send("We messed up, document not created")
            +    log("Failed to create document: " + e.message)
            +    return res.send("Failed to create document")
               }
             
               return res.send("Document created")
             }
            -
            -
          • -
          • -

            PHP

            -
            -
            <?php
            +            
            +
          • +
          • +

            PHP

            +
            +
            <?php
             
             require(__DIR__ . '/../vendor/autoload.php');
             
            @@ -2008,17 +2019,18 @@ return function ($context) {
                 try {
                     $databases->createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID::unique(), []);
                 } catch (Exception $e) {
            -        return $context->res->send("We messed up, document not created");
            +        $context->error("Failed to create document: " . $e->getMessage());
            +        return $context->res->send("Failed to create document");
                 }
             
                 return $context->res->send("Document created");
             };
            -
            -
          • -
          • -

            Python

            -
            -
            from appwrite.client import Client
            +            
            +
          • +
          • +

            Python

            +
            +
            from appwrite.client import Client
             from appwrite.services.databases import Databases
             from appwrite.id import ID
             
            @@ -2042,15 +2054,16 @@ def main(context):
                 try:
                     databases.create_document("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), {})
                 except Exception as e:
            -        return context.response.send("We messed up, document not created")
            +        context.error("Failed to create document: " + e.message)
            +        return context.response.send("Failed to create document")
             
                 return context.response.send("Document created")
            -
            -
          • -
          • -

            Ruby

            -
            -
            require "appwrite"
            +            
            +
          • +
          • +

            Ruby

            +
            +
            require "appwrite"
             
             def main(context)
               client = Appwrite::Client.new
            @@ -2069,19 +2082,20 @@ def main(context)
                 begin
                     databases.create_document('[DATABASE_ID]', '[COLLECTION_ID]', Appwrite::ID.unique(), {})
                 rescue Appwrite::Exception => e
            -        return context.response.send("We messed up, document not created")
            +        context.error("Failed to create document: " + e.message)
            +        return context.response.send("Failed to create document")
                 end
             
                 return context.response.send("Document created")
             end
            -
            -
          • -
          • -

            Deno

            -
            -
            import { Client, Databases, ID } from "https://deno.land/x/appwrite/mod.ts";
            +            
            +
          • +
          • +

            Deno

            +
            +
            import { Client, Databases, ID } from "https://deno.land/x/appwrite/mod.ts";
                             
            -export default function ({req, res}: any){
            +export default function ({req, res, error}: any){
                 const client = new Client();
                 client
                     .setEndpoint("https://cloud.appwrite.io/v1")
            @@ -2098,17 +2112,18 @@ export default function ({req, res}: any){
                 try {
                     databases.createDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), {});
                 } catch (e) {
            -        return res.send("We messed up, document not created");
            +        error("Failed to create document: " + e.message)
            +        return res.send("Failed to create document");
                 }
                 
                 return res.send("Document created");
            -}
            -
            -
          • -
          • -

            Dart

            -
            -
            import 'dart:async';
            +}
            +
            +
          • +
          • +

            Dart

            +
            +
            import 'dart:async';
             import 'package:dart_appwrite/dart_appwrite.dart';
             
             
            @@ -2128,17 +2143,18 @@ Future<dynamic> main(final context) async {
                 try {
                     await databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {});
                 } catch (e) {
            -        return context.res.send("We messed up, document not created");
            +        context.error("Failed to create document: " + e.message);
            +        return context.res.send("Failed to create document");
                 }
             
                 return context.res.send("Document created");
             }
            -
            -
          • -
          • -

            Swift

            -
            -
            import Appwrite
            +            
            +
          • +
          • +

            Swift

            +
            +
            import Appwrite
             import AppwriteModels
             import Foundation
             
            @@ -2158,17 +2174,18 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
                 do {
                     try await databases.createDocument(databaseId: "[DATABASE_ID]", collectionId: "[COLLECTION_ID]", data: [:])
                 } catch {
            -        return context.res.send("We messed up, document not created")
            +        context.error("Failed to create document: \(error.localizedDescription)")
            +        return context.res.send("Failed to create document")
                 }
             
                 return context.res.send("Document created")
             }
            -
            -
          • -
          • -

            .NET

            -
            -
            namespace DotNetRuntime;
            +            
            +
          • +
          • +

            .NET

            +
            +
            namespace DotNetRuntime;
             
             using Appwrite;
             using Appwrite.Services;
            @@ -2193,18 +2210,19 @@ public class Handler {
                     try {
                         await databases.CreateDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.Unique(), new Dictionary<string, object>());
                     } catch (Exception e) {
            -            return Context.Res.Send("We messed up, document not created");
            +            Context.Error("Failed to create document: " + e.Message);
            +            return Context.Res.Send("Failed to create document");
                     }
             
                     return Context.Res.Send("Document created");
                 }
             }
            -
            -
          • -
          • -

            Kotlin

            -
            -
            package io.openruntimes.kotlin.src
            +            
            +
          • +
          • +

            Kotlin

            +
            +
            package io.openruntimes.kotlin.src
             
             import io.openruntimes.kotlin.RuntimeContext
             import io.openruntimes.kotlin.RuntimeOutput
            @@ -2231,18 +2249,19 @@ class Main {
                     try {
                         databases.createDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), HashMap())
                     } catch (e: Exception) {
            -            return context.res.send("We messed up, document not created")
            +            context.error("Failed to create document: " + e.message)
            +            return context.res.send("Failed to create document")
                     }
             
                     return context.res.send("Document created")
                 }
             }
            -
            -
          • -
          • -

            Java

            -
            -
            package io.openruntimes.java.src;
            +            
            +
          • +
          • +

            Java

            +
            +
            package io.openruntimes.java.src;
             
             import io.openruntimes.java.RuntimeContext;
             import io.openruntimes.java.RuntimeOutput;
            @@ -2267,162 +2286,163 @@ public class Main {
                     try {
                         databases.createDocument("[DATABASE_ID]", "[COLLECTION_ID]", ID.unique(), new HashMap<>());
                     } catch (Exception e) {
            -            return context.res.send("We messed up, document not created");
            +            context.error("Failed to create document: " + e.getMessage());
            +            return context.res.send("Failed to create document");
                     }
             
                     return context.res.send("Document created");
             
                 }
             }
            -
            -
          • -
          - - -

          Code Splitting

          -

          - As your functions grow, you may find yourself needing to split your code into multiple files. - This helps you keep your codebase maintainable and easy to read. - Here's how you can accomplish code splitting. -

          -
            -
          • -

            Node.js

            -
            -
            // src/utils.js
            +            
            +
          • +
          + + +

          Code Splitting

          +

          + As your functions grow, you may find yourself needing to split your code into multiple files. + This helps you keep your codebase maintainable and easy to read. + Here's how you can accomplish code splitting. +

          +
            +
          • +

            Node.js

            +
            +
            // src/utils.js
             
             export function add(a, b) {
                 return a + b;
             }
                         
            -
            -
            -
            // src/main.js
            +            
            +
            +
            // src/main.js
             
             import { add } from './utils.js';
             
             export defaut function ({res}) {
                 return res.send(add(1, 2));
             }
            -
            -
          • -
          • -

            PHP

            -
            -
            // src/utils.php
            +            
            +
          • +
          • +

            PHP

            +
            +
            // src/utils.php
             
             function add($a, $b) {
                 return $a + $b;
             }
            -
            -
            -
            // src/main.php
            +            
            +
            +
            // src/main.php
             
             include 'utils.php';
             
             return function ($context) {
                 return $context->res->send(add(1, 2));
             };
            -
            -
          • -
          • -

            Python

            -
            -
            // src/utils.py
            +            
            +
          • +
          • +

            Python

            +
            +
            // src/utils.py
             
             def add(a, b):
                 return a + b;
                     
            -
            -
            -
            // src/main.py
            +            
            +
            +
            // src/main.py
             
             import utils
             
             def main(context):
                 return context.res.send(utils.add(1, 2))
            -
            -
          • -
          • -

            Ruby

            -
            -
            # lib/utils.rb
            +            
            +
          • +
          • +

            Ruby

            +
            +
            # lib/utils.rb
             
             def add(a, b)
                 return a + b
             end
            -
            -
            -
            # lib/main.rb
            +            
            +
            +
            # lib/main.rb
             
             require_relative 'utils'
             
             def main(context)
                 return context.res.send(add(1, 2))
             end
            -
            -
          • -
          • -

            Deno

            -
            -
            // src/utils.ts
            +            
            +
          • +
          • +

            Deno

            +
            +
            // src/utils.ts
             
             export function add(a: number, b: number): number {
                 return a + b;
            -}
            -
            -
            -
            // src/main.ts
            +}
            +
            +
            +
            // src/main.ts
             
             import { add } from './utils.ts';
             
             export default function ({res}: {res: any}) {
                 return res.send(add(1, 2));
             }
            -
            -
          • -
          • -

            Dart

            -
            -
            // lib/utils.dart
            +            
            +
          • +
          • +

            Dart

            +
            +
            // lib/utils.dart
             
             int add(int a, int b) {
                 return a + b;
             }
            -
            -
            -
            // lib/main.dart
            +            
            +
            +
            // lib/main.dart
             import 'dart:async';
             
             Future<dynamic> main(final context) async {
                 return context.res.send(add(1, 2));
             }
            -
            -
          • -
          • -

            Swift

            -
            -
            // Sources/utils.swift
            +            
            +
          • +
          • +

            Swift

            +
            +
            // Sources/utils.swift
             
             func add(_ a: Int, _ b: Int) -> Int {
                 return a + b
             }
                         
            -
            -
            -
            // Sources/index.swift
            +            
            +
            +
            // Sources/index.swift
             
             import Foundation
             
             func main(context: RuntimeContext) async throws -> RuntimeOutput {
                 return context.res.send(add(1, 2))
             }
            -
            -
          • -
          • -

            .NET

            -
            -
            // src/Utils.cs
            +            
            +
          • +
          • +

            .NET

            +
            +
            // src/Utils.cs
             
             namespace DotNetRuntime
             
            @@ -2433,9 +2453,9 @@ public static class Utils
                     return a + b;
                 }
             }
            -
            -
            -
            // src/Index.cs
            +            
            +
            +
            // src/Index.cs
             
             namespace DotNetRuntime
             
            @@ -2445,12 +2465,12 @@ public class Handler {
                     return Context.Res.Send(Utils.Add(1, 2));
                 }
             }
            -
            -
          • -
          • -

            Kotlin

            -
            -
            // src/Utils.kt
            +            
            +
          • +
          • +

            Kotlin

            +
            +
            // src/Utils.kt
             
             package io.openruntimes.kotlin.src
             
            @@ -2459,9 +2479,9 @@ class Utils {
                     return a + b
                 }
             }
            -
            -
            -
            // src/Main.kt
            +            
            +
            +
            // src/Main.kt
             
             package io.openruntimes.kotlin.src
             
            @@ -2474,12 +2494,12 @@ class Main {
                     return context.res.send(Utils.add(1, 2))
                 }
             }
            -
            -
          • -
          • -

            Java

            -
            -
            // src/Utils.java
            +            
            +
          • +
          • +

            Java

            +
            +
            // src/Utils.java
             
             package io.openruntimes.java.src;
             
            @@ -2488,9 +2508,9 @@ class Utils {
                     return a + b;
                 }
             }
            -
            -
            -
            package io.openruntimes.java.src;
            +            
            +
            +
            package io.openruntimes.java.src;
             
             import io.openruntimes.java.RuntimeContext;
             import io.openruntimes.java.RuntimeOutput;
            @@ -2501,54 +2521,54 @@ public class Main {
                     return context.res.send(Utils.add(1, 2));
                 }
             }
            -
            -
          • -
          - - -

          Upgrade

          -

          - Appwrite Functions received major updates in Appwrite version 1.4. - If you still have functions from previous versions, they will be read-only in Appwrite 1.4. - You will have to migrate your old functions to follow new runtime syntax. -

          - -

          - Here's a checklist of things you need to know. -

          - -
            -
          1. - The parameter passed into functions has changed. - req and res has been replaced by context, which contains new logger methods. - Learn about context. -
          2. -
          3. - To improve privacy and logging reliability, we provide new context.log() and context.error() functions. - You can no longer use native logging methods. - Learn about logging. -
          4. -
          5. - The old way of req.variables has been deprecated. - You can now access variables passed into each function as environment variables. - Learn about environment variables. -
          6. -
          7. - The req object has been updated to use terminology consistent with typical HTTP concepts. - You'll now find familiar concepts like headers, body, HTTP methods, and others. - Learn about request. -
          8. -
          9. - The response object has been updated. - You can now specify headers, as well as use new methods like return redirects or empty responses. - Learn about response. -
          10. -
          11. - Now, you must return a response such as return context.res.send(""). - This prevents confusing errors when functions are terminated prematurely before a response is sent. - Learn about response. -
          12. -
          13. - Some variables about how a function was triggered are now found in the context.req object as headers. -
          14. -
          \ No newline at end of file +
          +
        • +
        + + +

        Upgrade

        +

        + Appwrite Functions received major updates in Appwrite version 1.4. + If you still have functions from previous versions, they will be read-only in Appwrite 1.4. + You will have to migrate your old functions to follow new runtime syntax. +

        + +

        + Here's a checklist of things you need to know. +

        + +
          +
        1. + The parameter passed into functions has changed. + req and res has been replaced by context, which contains new logger methods. + Learn about context. +
        2. +
        3. + To improve privacy and logging reliability, we provide new context.log() and context.error() functions. + You can no longer use native logging methods. + Learn about logging. +
        4. +
        5. + The old way of req.variables has been deprecated. + You can now access variables passed into each function as environment variables. + Learn about environment variables. +
        6. +
        7. + The req object has been updated to use terminology consistent with typical HTTP concepts. + You'll now find familiar concepts like headers, body, HTTP methods, and others. + Learn about request. +
        8. +
        9. + The response object has been updated. + You can now specify headers, as well as use new methods like return redirects or empty responses. + Learn about response. +
        10. +
        11. + Now, you must return a response such as return context.res.send(""). + This prevents confusing errors when functions are terminated prematurely before a response is sent. + Learn about response. +
        12. +
        13. + Some variables about how a function was triggered are now found in the context.req object as headers. +
        14. +
        \ No newline at end of file From 4548a4d2a0e1c7132e6aa408e9e893dace0f97c4 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Aug 2023 22:13:31 +0100 Subject: [PATCH 61/80] fix: await document create --- app/views/docs/functions-develop.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 4817eb1aa..aaab1175b 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -1665,7 +1665,7 @@ export default async ({ req, res, log, error }) => { const databases = new Databases(client); try { - databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {}) + await databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {}) } catch (e) { error("Failed to create document: " + e.message) return res.send("Failed to create document") @@ -1980,7 +1980,7 @@ export default async ({ req, res, log }) => { const databases = new Databases(client); try { - databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {}) + await databases.createDocument('[DATABASE_ID]', '[COLLECTION_ID]', ID.unique(), {}) } catch (e) { log("Failed to create document: " + e.message) return res.send("Failed to create document") From 3f57c8df1b8be4cbaf9634334f12cf040d28b1db Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 24 Aug 2023 12:12:00 -0400 Subject: [PATCH 62/80] Update app/views/docs/functions-deploy.phtml Co-authored-by: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> --- app/views/docs/functions-deploy.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 7be004807..5d8eed8fb 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -53,7 +53,7 @@

        Deploy

        1. - Checkout your prodction branch in Git. + Checkout your production branch in Git.
        2. Create a new commit. From c0e4011fc3be15bbcfc529f9ec56015cbc86da3b Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 24 Aug 2023 16:19:17 +0000 Subject: [PATCH 63/80] Add entry file and html tags got deno --- app/views/docs/functions-develop.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index c251a31ae..3febbd757 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -1602,7 +1602,7 @@ namespace runtime { Deno deno - deno cache [ENTRYPOINT_FILE] + deno cache <ENTRYPOINT_FILE> Dart From c6e548cf6a61b4b2a8932a4792bd6f5f105f8222 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 24 Aug 2023 19:56:05 +0000 Subject: [PATCH 64/80] add debug and redeploy --- app/views/docs/functions-deploy.phtml | 40 +++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 5d8eed8fb..0405d7445 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -16,7 +16,7 @@

          Create Funcion

          -

          Before deploying your function with Git, create a new function attached to your Git repo.

          +

          Before deploying your function with VCS, create a new function attached to your VCS repo.

          1. Navigate to Functions from the side bar of the Appwrite Console. @@ -43,7 +43,8 @@ Name your function, select a runtime that matches your function, and enter an entry point path, relative to the root directory from the previous step.
          2. - If you have build steps, like installing dependencies, input the commands into the Build settings heading's Command field. + If you have build steps, like installing dependencies, input the commands into the Build settings heading's Command field. + You can combine multiple commands using &&, such as npm install && npm build.
          3. Finally, configure the execute permissions of the function. For security, only provide execute permissions to the necessary roles. @@ -65,14 +66,6 @@ A new deployment will be automatically created. Deployments will be automatically activated when new commits are added to the production branch.
          -

          Debugging Builds

          -

          After deploying a function, you can find the status of the deployment and build logs in the Appwrite Console.

          -
            -
          1. In Appwrite Console, navigate to Functions.
          2. -
          3. Click to open a function you wish to inspect.
          4. -
          5. Under the Deployments tab, you'll find the status of the current active deployment and previous inactive deployments.
          6. -
          7. You can access build logs for the active deployment by clicking the Build logs button. You can click on an inactive function's three dots button to find their build logs.
          8. -

          CLI

          @@ -163,7 +156,6 @@

        Domains

        -

        Each deployed function can have it's own domain. By default, one is generated for each of your functions. @@ -195,4 +187,30 @@ DNS records can take up to 48 hours to propagate after they're added. Please retry verification over the next 48 hours. If the domain verification still fails and you have confirmed DNS records are added correctly, please contact support. +

        + +

        Debugging Build

        +

        After deploying a function, you can find the status of the deployment and build logs in the Appwrite Console.

        +
          +
        1. In Appwrite Console, navigate to Functions.
        2. +
        3. Click to open a function you wish to inspect.
        4. +
        5. Under the Deployments tab, you'll find the status of the current active deployment and previous inactive deployments.
        6. +
        7. You can access build logs for the active deployment by clicking the Build logs button. You can click on an inactive function's three dots button to find their build logs.
        8. +
        + +

        Redeploy Builds

        +

        + After updateing the configuration of your Appwrite Function, you need to redeploy your function for the changes to take effect. + You can also redeploy builds to retry failed builds. +

        +
          +
        1. In Appwrite Console, navigate to Functions.
        2. +
        3. Click to open a function you wish to inspect.
        4. +
        5. Under the Deployments tab, you'll find the status of the current active deployment.
        6. +
        7. You can redeploy by clicking the Redeploy button.
        8. +
        +

        + The redeployment behavior varies depending on how the initial deployment is created. + For VCS deployments, redeploy uses the same commit hash but updated function settings. + For manual and CLI deployments, redeploy uses previously updated code but updated function settings.

        \ No newline at end of file From 8337b4ecd8183cba949ffd43436ef837d4225258 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Thu, 24 Aug 2023 20:04:27 +0000 Subject: [PATCH 65/80] Fix modal text --- app/views/docs/functions-execute.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index e66d845da..685756b8f 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -434,8 +434,8 @@ $image = new View(__DIR__.'/../general/image.phtml'); echo $image ->setParam('srcLight', '/images-ee/docs/functions-execute-light.png') ->setParam('srcDark', '/images-ee/docs/functions-execute-dark.png') - ->setParam('alt', '"Create Function" page.') - ->setParam('description', '"Create Function" page.') + ->setParam('alt', '"Execute Function" modal.') + ->setParam('description', '"Execute Function" modal.') ->render(); ?> From 28cd6fe65bfcea30e128c7170d01d4d073c2a465 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 24 Aug 2023 21:50:58 +0100 Subject: [PATCH 66/80] feat: simplify function examples --- app/views/docs/functions-examples.phtml | 413 +++++------------------- 1 file changed, 83 insertions(+), 330 deletions(-) diff --git a/app/views/docs/functions-examples.phtml b/app/views/docs/functions-examples.phtml index e125d902e..d7971d657 100644 --- a/app/views/docs/functions-examples.phtml +++ b/app/views/docs/functions-examples.phtml @@ -1,176 +1,16 @@

        Appwrite Functions is all about flexibility. - Behind the simple workflow hides some useful recipes that can help you accomplish your goals faster. + Behind the simple workflow hides some useful examples that can help you accomplish your goals faster. Take a look at the following.

        -[TODO: @matej @luke -> Let's have some simple recipes here for common actions]

        Currency Conversion API

        -

        Getting Started

        - -

        - In this recipe, we'll build a simple function for currency conversion. - Let's start with a static converstion rate. We'll show you how to integrate an API later in this example. -

        - -
          -
        • -

          Node.js

          -
          -

          Create a new file, index.js.

          -

          Add the following code to index.js.

          -
          -
          export default async function ({ res }) {
          -  return res.send('1.13');
          -};
          -
          -
          -
        • -
        • -

          PHP

          -
          -

          Create a new file, index.php.

          -

          Add the following code to index.php.

          -
          -
          <?php
          -return function ($context) {
          -  return $context->res->send('1.13');
          -};
          -
          -
          -
        • -
        • -

          Python

          -
          -

          Create a new file, index.py.

          -

          Add the following code to index.py.

          -
          -
          def main(context):
          -  return context.res.send('1.13')
          -
          -
          -
        • -
        • -

          Dart

          -
          -

          Create a new file, index.dart.

          -

          Add the following code to index.dart.

          -
          -
          import 'dart:async';
          -
          -Future<dynamic> main(final context) async {
          -  return context.res.send('1.13');
          -}
          -
          -
          -
        • -
        • -

          Ruby

          -
          -

          Create a new file, index.rb.

          -

          Add the following code to index.rb.

          -
          -
          def main(context)
          -  return context.res.send('1.13')
          -end
          -
          -
          -
        • -
        - -

        This code will return 1.13 when the function is called, because 1€ equals approximately 1.13$.

        -

        Now, create a function in the Appwrite console, adding your Git repository as the remote source and using the path file you created as the entry point.

        -

        Finally, execute the function and visit the URL (like ghrfu9ewji.functions.appwrite.app) to see the response.

        - -

        Adding the Business Logic

        - -

        Now, let's update the function to use the request payload.

        -

        You can use a query string to pass data to your function. For example, ghrfu9ewji.functions.appwrite.app?amount=5 will pass 5 as the amount parameter.

        - -
          -
        • -

          Node.js

          -
          -

          Update index.js to use req.query.amount to access the amount parameter, and return the conversion result.

          -
          -
          export default async function ({ req, res }) {
          -  const amountInEuros = Number(req.query.amount);
          -  const amountInDollars = amountInEuros * 1.13;
          -  return res.send(amountInDollars.toString());
          -};
          -
          -
          -
        • -
        • -

          PHP

          -
          -

          Update index.php to use $context->req->query['amount'] to access the amount parameter, and return the conversion result.

          -
          -
          <?php
          -return function ($context) {
          -  $amountInEuros = $context->req->query['amount'];
          -  $amountInDollars = $amountInEuros * 1.13;
          -  return $context->res->send($amountInDollars);
          -};
          -
          -
          -
        • -
        • -

          Python

          -
          -

          Update index.py to use context.req.query['amount'] to access the amount parameter, and return the conversion result.

          -
          -
          def main(context):
          -  amountInEuros = context.req.query['amount']
          -  amountInDollars = amountInEuros * 1.13
          -  return context.res.send(amountInDollars)
          -
          -
          -
        • -
        • -

          Dart

          -
          -

          Update index.dart to use context.req.query['amount'] to access the amount parameter, and return the conversion result.

          -
          -
          import 'dart:async';
          -
          -Future<dynamic> main(final context) async {
          -  final amountInEuros = context.req.query['amount'];
          -  final amountInDollars = amountInEuros * 1.13;
          -  return context.res.send(amountInDollars);
          -}
          -
          -
          -
        • -
        • -

          Ruby

          -
          -

          Update index.rb to use context.req.query['amount'] to access the amount parameter, and return the conversion result.

          -
          -
          def main(context)
          -  amountInEuros = context.req.query['amount']
          -  amountInDollars = amountInEuros * 1.13
          -  return context.res.send(amountInDollars)
          -end
          -
          -
          -
        • -
        - -

        Commit your changes and push them to your Git repository.

        - - -

        Testing our Function

        - -

        - Execute the function and visit the URL (like ghrfu9ewji.functions.appwrite.app?amount=5) to see the response. -

        - You should see the result of the conversion, like 5.65. + Here's a currency conversion API that converts from Euros and Indian Rupees to US Dollars. We'll use an external API to get the latest exchange rates, and query it using an dependency specific to each runtime.

        -

        Adding Dependencies

        +

        Prerequisites

        • @@ -190,7 +30,6 @@ end
      • PHP

        -

        You can use Composer to manage your PHP project's dependencies. Install it from getcomposer.org/download.

        Run the following bash command to create a composer.json file. This file is used to manage your PHP project's dependencies.

        composer init -y
        @@ -236,9 +75,7 @@ environment: Install the http library. This library includes a get function that you can use to make HTTP requests.

        -
        
        -          pub install http
        -        
        +
        pub install http

        Finally, add pub get to your function's build commands in the Appwrite console. @@ -254,8 +91,7 @@ environment: Create a Gemfile file with the following contents. This file is used to manage your Ruby project's dependencies.

        -
        source 'https://rubygems.org'
        -        
        +
        source 'https://rubygems.org'

        Install the httparty library. This library includes a get function that you can use to make HTTP requests. @@ -271,106 +107,7 @@ bundle install

      -

      Using Dependencies

      - -
        -
      • -

        Node.js

        -
        -

        Use fetch from undici to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

        -
        -
        import { fetch } from 'undici';
        -
        -export default async function ({ req, res }) {
        -  const amountInEuros = Number(req.query.amount);
        -  const response = await fetch('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
        -  const data = await response.json();
        -  const amountInDollars = amountInEuros * data.rates.USD;
        -  return res.send(amountInDollars.toString());
        -};
        -
        -
        -
      • -
      • -

        PHP

        -
        -

        -

        -
        <?php
        -
        -require(__DIR__ . '/../vendor/autoload.php');
        -
        -use GuzzleHttp\Client;
        -
        -return function ($context) {
        -  $amountInEuros = $context->getRequest()->getQuery('amount');
        -  $client = new Client();
        -  $response = $client->get('https://api.exchangerate.host/latest?base=EUR&symbols=USD');
        -  $data = json_decode($response->getBody(), true);
        -  $amountInDollars = $amountInEuros * $data['rates']['USD'];
        -  return $context->res->send(strval($amountInDollars));
        -};
        -
        -
        -
      • -
      • -

        Python

        -
        -

        Use get from requests to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

        -
        -
        import requests
        -
        -def main(context):
        -  amount_in_euros = float(context.req.query['amount'])
        -  response = requests.get('https://api.exchangerate.host/latest?base=EUR&symbols=USD')
        -  data = response.json()
        -  amount_in_dollars = amount_in_euros * data['rates']['USD']
        -  return context.res.send(str(amount_in_dollars))
        -
        -
        -
      • -
      • -

        Dart

        -
        -

        Use get from http to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

        -
        -
        import 'dart:async';
        -import 'package:http/http.dart' as http;
        -
        -Future<dynamic> main(final context) async {
        -  final amountInEuros = double.parse(context.req.query['amount'])
        -  final response = await http.get(Uri.parse('https://api.exchangerate.host/latest?base=EUR&symbols=USD'));
        -  final data = json.decode(response.body);
        -  final amountInDollars = amountInEuros * data['rates']['USD'];
        -  return context.res.send(amountInDollars.toString());
        -}
        -
        -
        -
      • -
      • -

        Ruby

        -
        -

        Use get from httparty to get the current conversion rate. This API call will return the current conversion rate between Euros and Dollars.

        -
        -
        require 'httparty'
        -
        -def main(context)
        -  amount_in_euros = context.req.query['amount'].to_f
        -  response = HTTParty.get('https://api.exchangerate.host/latest?base=EUR&symbols=USD')
        -  data = JSON.parse(response.body)
        -  amount_in_dollars = amount_in_euros * data['rates']['USD']
        -  return context.res.send(amount_in_dollars.to_s)
        -end
        -
        -
        -
      • -
      - -

      After your function has updated, you can test it by visiting the URL and providing different amounts to convert in the query string. The conversion rate should now be more precise because we're using the current conversion rate.

      - -

      Adding Multiple Routes

      - -

      Let's add support multiple paths like /eur and /inr. Each path will convert from that currency to dollars.

      +

      Code

      • @@ -490,44 +227,78 @@ end
      -

      After your function has updated, you can try out the new paths. For example, ghrfu9ewji.functions.appwrite.app/eur?amount=5 should convert Euros to Dollars, while ghrfu9ewji.functions.appwrite.app/inr?amount=100 should convert Indian Rupees to Dollars.

      -

      Congratulations! You've built a currency conversion API using Appwrite functions!

      - -

      Voting System Using Appwrite

      - -

      Getting Started

      -

      - In this recipe, you will build a simple voting system that allows users to vote on various topics. Appwrite functions and the server SDK will be used to enforce voting rules and prevent multiple votes from the same user for a single topic. + Use the function by navigating to function URL in the browser. The path should contain the currency and amount parameter. + For example, [YOUR_FUNCTION_URL]/eur?amount=5 should convert Euros to Dollars.

      -

      Setting Up

      -
        -
      • -

        Appwrite Project

        -
        -

        1. Create a new Appwrite project.

        -

        2. Add your development domain to your Appwrite project's trusted domains.

        -
        -
      • -
      • -

        Create Collections

        -
        -

        Create two collections named "topics" and "votes."

        -

        For "topics" collection, have fields: title and description.

        -

        For "votes" collection, have fields: topicId, userId, and vote.

        -
        -
      • -
      +

      Voting System Using Appwrite

      +

      + Here's a simple voting system that allows users to vote on various topics. Appwrite Functions and the server SDK are used to enforce voting rules and prevent multiple votes from the same user for a single topic. +

      -

      Building the Voting Function

      +

      Prerequisites

      + +

      Create a Topics collection with the following attributes:

      + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      titlestringThe name of the topic
      descriptionstringLong form description of the topic
      + +

      Create a Votes collection with the following attributes:

      + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      userIdstringThe ID of the user who cast the vote
      topicIdstringThe ID of the topic that was voted on
      votestringThe vote cast by the user. Must be either "yes" or "no"
      + +

      Code

      • Node.js

        -

        Create a new located at functions/vote/src/main.js.

        import { Client, Databases, Query } from 'node-appwrite';
         
        @@ -539,7 +310,7 @@ export default async function ({ req, res }) {
           };
         
           if (vote.vote !== 'yes' && vote.vote !== 'no') {
        -    return res.json({ ok: false, message: 'You must vote yes or no.' });
        +    return res.json({ ok: false, message: 'You must vote yes or no.' }, 400);
           }
         
           const client = new Client();
        @@ -556,7 +327,7 @@ export default async function ({ req, res }) {
           ]);
         
           if (existingVotes.total > 0) {
        -    return res.json({ message: 'You have already voted on this topic.' });
        +    return res.json({ ok: false, message: 'You have already voted on this topic.' }, 400);
           }
         
           const voteDocument = await database.createDocument('[VOTES_COLLECTION_ID]', {
        @@ -567,7 +338,7 @@ export default async function ({ req, res }) {
         
           return res.json({ ok: true, message: 'Vote cast.', vote: voteDocument });
         }
        -
        +
      • @@ -588,7 +359,7 @@ def main(context): } if vote['vote'] != 'yes' and vote['vote'] != 'no': - return context.res.json({'ok': False, 'message': 'You must vote yes or no.'}) + return context.res.json({'ok': False, 'message': 'You must vote yes or no.'}, 400) client = Client() client.set_endpoint('https://cloud.appwrite.io/v1') @@ -606,7 +377,7 @@ def main(context): return context.res.json({ 'ok': False, 'message': 'You have already voted on this topic.' - }) + }, 400) vote_document = database.create_document('[VOTES_COLLECTION_ID]', { 'userId': vote['userId'], @@ -640,7 +411,7 @@ return function ($context) { ]; if ($vote['vote'] !== 'yes' && $vote['vote'] !== 'no') { - return $context->res->json(['ok' => false, 'message' => 'You must vote yes or no.']); + return $context->res->json(['ok' => false, 'message' => 'You must vote yes or no.'], 400); } $client = new Client(); @@ -660,7 +431,7 @@ return function ($context) { return $context->res->json([ 'ok' => false, 'message' => 'You have already voted on this topic.' - ]); + ], 400); } $voteDocument = $database->createDocument('[VOTES_COLLECTION_ID]', [ @@ -686,7 +457,6 @@ return function ($context) {
        require "appwrite"
         
         def main(context)
        -
             vote = {
                 'userId' => context.req.query['userId'],
                 'topicId' => context.req.query['topicId'],
        @@ -694,7 +464,7 @@ def main(context)
             }
         
             if vote['vote'] != 'yes' and vote['vote'] != 'no'
        -        return context.res.json({'ok': false, 'message': 'You must vote yes or no.'})
        +        return context.res.json({'ok': false, 'message': 'You must vote yes or no.'}, 400)
             end
         
             client = Appwrite::Client.new()
        @@ -714,7 +484,7 @@ def main(context)
                 return context.res.json({
                   'ok': false, 
                   'message': 'You have already voted on this topic.'
        -        })
        +        }, 400)
             end
         
             vote_document = database.create_document('[VOTES_COLLECTION_ID]', {
        @@ -748,7 +518,7 @@ Future main(final context) async {
             };
         
             if (vote['vote'] != 'yes' && vote['vote'] != 'no') {
        -        return context.res.json({'ok': false, 'message': 'You must vote yes or no.'});
        +        return context.res.json({'ok': false, 'message': 'You must vote yes or no.'}, 400);
             }
         
             final client = Client()
        @@ -767,7 +537,7 @@ Future main(final context) async {
                 return context.res.json({
                   'ok': false, 
                   'message': 'You have already voted on this topic.'
        -        });
        +        }, 400);
             }
         
             final voteDocument = await database.createDocument('[VOTES_COLLECTION_ID]', {
        @@ -804,7 +574,7 @@ def main(context):
             }
         
             if vote['vote'] != 'yes' and vote['vote'] != 'no':
        -        return context.res.json({'ok': False, 'message': 'You must vote yes or no.'})
        +        return context.res.json({'ok': False, 'message': 'You must vote yes or no.'}, 400)
         
             client = Client()
             client.set_endpoint('https://cloud.appwrite.io/v1')
        @@ -822,7 +592,7 @@ def main(context):
                 return context.res.json({
                   'ok': False, 
                   'message': 'You have already voted on this topic.'
        -        })
        +        }, 400)
         
             vote_document = database.create_document('[VOTES_COLLECTION_ID]', vote)
         
        @@ -836,28 +606,11 @@ def main(context):
           
      -

      In the Appwrite console, create a function and use the file you created as the entry point.

      - -

      Testing the Function

      -

      - Execute the function and visit the URL, passing the required parameters [YOUR_FUNCTION_URL]?userId=[USER_ID]&topicId=[TOPIC_ID];&vote=yes to cast a vote. You should see a response confirming the vote. Trying to vote again with the same user ID and topic ID should result in a message indicating that the user has already voted on this topic. + Use the function by navigating to the function URL in the browser. The URL should contain the required parameters. + For example, [YOUR_FUNCTION_URL]?userId=[USER_ID]&topicId=[TOPIC_ID];&vote=yes to cast a vote.

      -

      Conclusion

      - -

      - To further improve this function, try adding the following checks: -

        -
      • Check that the user ID and topic ID exist.
      • -
      • Replace the user ID query parameter with the x-appwrite-user-id header, so votes may only be cast by logged in users.
      • -
      • Convert the function to POST requests with a JSON body to cast votes, so that the vote data is not visible in the URL.
      • -
      -

      - -

      - Congratulations! You've built a voting system that leverages Appwrite functions and the Appwrite server SDK to enforce business logic and ensure that users can only vote once per topic. -

      [TODO: @luke -> Example with JWT, show both client and server code] From 9673f0446197b9c0e9c9b5e4d9b175a0af11c880 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 24 Aug 2023 22:37:31 +0100 Subject: [PATCH 67/80] feat: html form function example --- app/views/docs/functions-examples.phtml | 331 +++++++++++++++++++++++- 1 file changed, 328 insertions(+), 3 deletions(-) diff --git a/app/views/docs/functions-examples.phtml b/app/views/docs/functions-examples.phtml index d7971d657..a8dd01968 100644 --- a/app/views/docs/functions-examples.phtml +++ b/app/views/docs/functions-examples.phtml @@ -611,6 +611,334 @@ def main(context): For example, [YOUR_FUNCTION_URL]?userId=[USER_ID]&topicId=[TOPIC_ID];&vote=yes to cast a vote.

      +

      HTML Contact Form

      + +

      + Here's a simple form page that can be used to store a user's message in a collection. + The form is submitted to the function using the POST method and the form data is sent as a URL-encoded string in the request body. +

      + +

      Prerequisites

      + +

      Create a Messages collection with the following attributes:

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      namestringThe name of the message author
      emailstringThe email of the message author
      contentstringThe content of the message
      + +

      Code

      + +
        +
      • +

        Node.js

        +
        +
        +
        import { Client, Databases, Query, ID } from 'node-appwrite';
        +import querystring from 'node:querystring';
        +
        +const html = `<!doctype html>
        +<html lang="en">
        +  <head>
        +    <meta charset="utf-8">
        +    <title>Contact Form</title>
        +  </head>
        +  <body>
        +    <form action="/" method="POST">
        +      <input type="text" id="name" name="name" placeholder="Name" required>
        +      <input type="email" id="email" name="email" placeholder="Email" required>
        +      <textarea id="content" name="content" placeholder="Message" required></textarea>
        +      <button type="submit">Submit</button>
        +    </form>
        +  </body>
        +</html>`
        +
        +export default async function ({ req, res }) {
        +  if (req.method === 'GET') {
        +    return res.send(html, 200, {'content-type': 'text/html'});
        +  }
        +
        +  if (req.method === 'POST' && req.headers['content-type'] === 'application/x-www-form-urlencoded') {
        +    const formData = querystring.parse(req.body);
        +
        +    const message = {
        +      name: formData.name,
        +      email: formData.email,
        +      content: formData.content
        +    };
        +
        +    const client = new Client();
        +    client
        +      .setEndpoint('https://cloud.appwrite.io/v1')
        +      .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        +      .setKey(process.env.APPWRITE_API_KEY);
        +
        +    const databases = new Databases(client);
        +    const document = await databases.createDocument('[DATABASE_ID]', '[MESSAGES_COLLECTION_ID]', ID.unique(), message);
        +
        +    return res.send("Message sent", 200);
        +  }
        +
        +  return res.send('Not found', 404);
        +}
        +
        +
        +
      • +
      • +

        Python

        +
        +

        +

        +
        from appwrite.client import Client
        +from appwrite.services.databases import Databases
        +from appwrite.query import Query
        +from urllib.parse import parse_qs
        +import os
        +
        +html = '''<!doctype html>
        +<html lang="en">
        +  <head>
        +    <meta charset="utf-8">
        +    <title>Contact Form</title>
        +  </head>
        +  <body>
        +    <form action="/" method="POST">
        +      <input type="text" id="name" name="name" placeholder="Name" required>
        +      <input type="email" id="email" name="email" placeholder="Email" required>
        +      <textarea id="content" name="content" placeholder="Message" required></textarea>
        +      <button type="submit">Submit</button>
        +    </form>
        +  </body>
        +</html>
        +'''
        +
        +def main(context):
        +    if context.req.method == 'GET':
        +        return context.res.send(html, 200, {'content-type': 'text/html'})
        +
        +    if context.req.method == 'POST' and context.req.headers['content-type'] == 'application/x-www-form-urlencoded':
        +        formData = parse_qs(context.req.body)
        +
        +        message = {
        +            'name': formData['name'][0],
        +            'email': formData['email'][0],
        +            'content': formData['content'][0]
        +        }
        +
        +        client = (
        +          Client()
        +            .set_endpoint('https://cloud.appwrite.io/v1')
        +            .set_project(os.environ['APPWRITE_FUNCTION_PROJECT_ID'])
        +            .set_key(os.environ['APPWRITE_API_KEY'])
        +        )
        +
        +        databases = Databases(client)
        +        document = databases.create_document('[DATABASE_ID]', '[MESSAGES_COLLECTION_ID]', ID.unique(), message)
        +
        +        return context.res.send("Message sent", 200)
        +
        +    return context.res.send('Not found', 404)
        +
        +
        +
      • +
      • +

        PHP

        +
        +

        +

        +
        <?php
        +
        +require(__DIR__ . '/../vendor/autoload.php');
        +
        +use Appwrite\Client;
        +use Appwrite\Exception;
        +use Appwrite\Services\Databases;
        +
        +$html = '<!doctype html>
        +<html lang="en">
        +  <head>
        +    <meta charset="utf-8">
        +    <title>Contact Form</title>
        +  </head>
        +  <body>
        +    <form action="/" method="POST">
        +      <input type="text" id="name" name="name" placeholder="Name" required>
        +      <input type="email" id="email" name="email" placeholder="Email" required>
        +      <textarea id="content" name="content" placeholder="Message" required></textarea>
        +      <button type="submit">Submit</button>
        +    </form>
        +  </body>
        +</html>';
        +
        +return function ($context) {
        +  global $html;
        +
        +  if ($context->req->method === 'GET') {
        +    return $context->res->send($html, 200, ['content-type' => 'text/html']);
        +  }
        +
        +  if ($context->req->method === 'POST' && $context->req->headers['content-type'] === 'application/x-www-form-urlencoded') {
        +    \parse_str($context->req->body, $formData);
        +    
        +    $message = [
        +      'name' => $formData['name'],
        +      'email' => $formData['email'],
        +      'content' => $formData['content']
        +    ];
        +
        +    $client = new Client();
        +    $client
        +      ->setEndpoint('https://cloud.appwrite.io/v1')
        +      ->setProject(getenv('APPWRITE_FUNCTION_PROJECT_ID'))
        +      ->setKey(getenv('APPWRITE_API_KEY'));
        +
        +    $databases = new Databases($client);
        +    $document = $databases->createDocument('[DATABASE_ID]', '[MESSAGES_COLLECTION_ID]', ID::unique(), $message);
        +
        +    return $context->res->send("Message sent", 200);
        +  }
        +
        +  return $context->res->send('Not found', 404);
        +};
        +
        +
        +
      • +
      • +

        Ruby

        +
        +

        +

        +
        require "appwrite"
        +
        +html = '''<!doctype html>
        +<html lang="en">
        +  <head>
        +    <meta charset="utf-8">
        +    <title>Contact Form</title>
        +  </head>
        +  <body>
        +    <form action="/" method="POST">
        +      <input type="text" id="name" name="name" placeholder="Name" required>
        +      <input type="email" id="email" name="email" placeholder="Email" required>
        +      <textarea id="content" name="content" placeholder="Message" required></textarea>
        +      <button type="submit">Submit</button>
        +    </form>
        +  </body>
        +</html>
        +'''
        +
        +def main(context)
        +    if context.req.method == 'GET'
        +        return context.res.send(html, 200, {'content-type': 'text/html'})
        +    end
        +
        +    if context.req.method == 'POST' and context.req.headers['content-type'] == 'application/x-www-form-urlencoded'
        +        formData = URI.decode_www_form(context.req.body).to_h
        +
        +        message = {
        +            'name' => formData['name'],
        +            'email' => formData['email'],
        +            'content' => formData['content']
        +        }
        +
        +        client = Appwrite::Client.new()
        +        client
        +            .set_endpoint('https://cloud.appwrite.io/v1')
        +            .set_project(ENV['APPWRITE_FUNCTION_PROJECT_ID'])
        +            .set_key(ENV['APPWRITE_API_KEY'])
        +
        +        databases = Appwrite::Database.new(client)
        +        document = databases.create_document('[DATABASE_ID]', '[MESSAGES_COLLECTION_ID]', ID.unique(), message)
        +
        +        return context.res.send("Message sent", 200)
        +    end
        +
        +    return context.res.send('Not found', 404)
        +end
        +
        +
        +
      • +
      • +

        Dart

        +
        +

        +

        +
        import 'dart:async';
        +import 'package:dart_appwrite/dart_appwrite.dart';
        +
        +Future main(final context) async {
        +    final html = '''<!doctype html>
        +<html lang="en">
        +  <head>
        +    <meta charset="utf-8">
        +    <title>Contact Form</title>
        +  </head>
        +  <body>
        +    <form action="/" method="POST">
        +      <input type="text" id="name" name="name" placeholder="Name" required>
        +      <input type="email" id="email" name="email" placeholder="Email" required>
        +      <textarea id="content" name="content" placeholder="Message" required></textarea>
        +      <button type="submit">Submit</button>
        +    </form>
        +  </body>
        +</html>
        +''';
        +
        +    if (context.req.method == 'GET') {
        +        return context.res.send(html, 200, {'content-type': 'text/html'});
        +    }
        +
        +    if (context.req.method == 'POST' && context.req.headers['content-type'] == 'application/x-www-form-urlencoded') {
        +        final formData = Uri.splitQueryString(context.req.body);
        +
        +        final message = {
        +            'name': formData['name'],
        +            'email': formData['email'],
        +            'content': formData['content']
        +        };
        +
        +        final client = Client()
        +            .setEndpoint('https://cloud.appwrite.io/v1')
        +            .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
        +            .setKey(process.env.APPWRITE_API_KEY);
        +
        +        final databases = Database(client);
        +        final document = await databases.createDocument('[DATABASE_ID]', '[MESSAGES_COLLECTION_ID]', ID.unique(), message);
        +
        +        return context.res.send("Message sent", 200);
        +    }
        +
        +    return context.res.send('Not found', 404);
        +}
        +
        +
        +
      • +
      + +

      + Use the function by navigating to the function URL in the browser. Submit the form to store the message in the collection. +

      [TODO: @luke -> Example with JWT, show both client and server code] @@ -618,15 +946,12 @@ def main(context): \ No newline at end of file From 8872899bf9a2c9b672d99039cbdd633ac4161644 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sat, 26 Aug 2023 20:37:18 +0000 Subject: [PATCH 68/80] Runtimes merged on one line --- app/views/docs/functions-runtimes.phtml | 56 +++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 96119f04d..928071a3f 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -4,12 +4,26 @@ use Appwrite\Utopia\View; $events = $this->getParam('events', []); $runtimes = $this->getParam('runtimes', []); -$runtimes['node-16.0']["cloud"] = true; -$runtimes['node-18.0']["cloud"] = true; -$runtimes['php-8.0']["cloud"] = true; -$runtimes['ruby-3.0']["cloud"] = true; -$runtimes['python-3.9']["cloud"] = true; -$runtimes['dart-2.17']["cloud"] = true; + +$sorted_runtimes = []; + +foreach ($runtimes as $key => $item) { + $name = $item['name']; + + if (!isset($sorted_runtimes[$name])) { + $sorted_runtimes[$name] = []; + } + + $item['version'] = $key; + + $sorted_runtimes[$name]['versions'][] = $item; +} + +$sorted_runtimes['Node.js']["cloud"] = true; +$sorted_runtimes['PHP']["cloud"] = true; +$sorted_runtimes['Ruby']["cloud"] = true; +$sorted_runtimes['Python']["cloud"] = true; +$sorted_runtimes['Dart']["cloud"] = true; ?> @@ -26,24 +40,32 @@ $runtimes['dart-2.17']["cloud"] = true; - Name + Name Image - Architectures - Platforms + Architectures + Platforms - $runtime): ?> + $runtime): ?> - Function Env. - escape($key); ?> - escape($runtime['image'] ?? ''); ?> - escape(implode(' / ', $runtime['supports'] ?? [])); ?> - + Function Env. + + + escape($key); ?> + + + $version): ?> + escape($version['image'] ?? ''); ?> + + + escape(implode(' / ', $runtime['versions'][0]['supports'] ?? [])); ?> + + Self-hosted + Cloud - - Self-hosted + From 77337c1edf209eb235fe60155e818c3d447bdff1 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sat, 26 Aug 2023 20:50:20 +0000 Subject: [PATCH 69/80] Fix response code examples --- app/views/docs/functions-develop.phtml | 338 ++++++++++++++++++++----- 1 file changed, 279 insertions(+), 59 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index a780b61e7..8f5c4cd8a 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -1301,7 +1301,7 @@ namespace runtime { - + @@ -1372,64 +1372,7 @@ namespace runtime { You can access the environment variables through the systems library of each language.

      -
      VariableVariable Description
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      VariableDescription
      x-appwrite-trigger - Describes how the function execution was invoked. - Possible values are http, schedule or event. -
      x-appwrite-event - If the function execution was triggered by an event, describes the triggering event. -
      x-appwrite-user-id - If the function execution was invoked by an authenticated user, display the user ID. - This doesn't apply to Appwrite Console users or API keys. -
      x-appwrite-user-jwt - JWT token generated from the invoking user's session. Used to authenticate Server SDKs to respect access permissions. - Learn more about JWT tokens. -
      x-appwrite-country-code - Displays the country code of the configured locale. -
      x-appwrite-continent-code - Displays the continent code of the configured locale. -
      x-appwrite-continent-eu - Describes if the configured local is within the EU. -
      - - -

      Response

      +

      If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. The response information will not be logged to the Appwrite Console. @@ -1561,6 +1504,283 @@ namespace runtime { +

      Response

      + +
        +
      • +

        Node.js

        +
        +
        export default async ({ req, res, log }) => {
        +
        +    switch (req.query.type) {
        +        case 'text':
        +            return res.send("This is a text response", 200);
        +        case 'json':
        +            return res.json({"type": "This is a JSON response"}, 200);
        +        case 'redirect':
        +            return res.redirect("https://appwrite.io", 301);
        +        case 'html':
        +            return res.send(
        +                "<h1>This is an HTML response</h1>", 200, {
        +                    "content-type": "text/html"
        +                });
        +        default:
        +            return res.empty();
        +    }
        +}
        +
        +
      • +
      • +

        PHP

        +
        +
        <?php
        +
        +return function ($context) {
        +    switch ($context->req->query['type']) {
        +        case 'text':
        +            return $context->res->send("This is a text response", 200);
        +        case 'json':
        +            return $context->res->json(["type" => "This is a JSON response"], 200);
        +        case 'redirect':
        +            return $context->res->redirect("https://appwrite.io", 301);
        +        case 'html':
        +            return $context->res->send("<h1>This is an HTML response</h1>", 200, [
        +                "content-type" => "text/html"
        +                ]);
        +        default:
        +            return $context->res->empty();
        +    }
        +};
        +
        +
      • +
      • +

        Python

        +
        +
        def main(context):
        +    switch context.req.query['type']:
        +        case 'text':
        +            return context.res.send("This is a text response", 200)
        +        case 'json':
        +            return context.res.json({"type": "This is a JSON response"}, 200)
        +        case 'redirect':
        +            return context.res.redirect("https://appwrite.io", 301)
        +        case 'html':
        +            return context.res.send("<h1>This is an HTML response</h1>", 200, {
        +                "content-type": "text/html"
        +            })
        +        default:
        +            return context.res.empty()
        +
        +
      • +
      • +

        Ruby

        +
        +
        def main(context)
        +    case context.req.query['type'] 
        +        when 'text'
        +            return context.res.send("This is a text response", 200)
        +        when 'json'
        +            return context.res.json({"type": "This is a JSON response"}, 200)
        +        when 'redirect'
        +            return context.res.redirect("https://appwrite.io", 301)
        +        when 'html'
        +            return context.res.send("<h1>This is an HTML response</h1>", 200, {
        +                "content-type": "text/html"
        +            })
        +        else
        +            return context.res.empty()
        +    end
        +end
        +
        +
      • +
      • +

        Deno

        +
        +
        export default async ({ req, res, log }) => {
        +
        +    switch (req.query.type) {
        +        case 'text':
        +            return res.send("This is a text response", 200);
        +        case 'json':
        +            return res.json({type": "This is a JSON response"}, 200);
        +        case 'redirect':
        +            return res.redirect("https://appwrite.io", 301);
        +        case 'html':
        +            return res.send(
        +                "<h1>This is an HTML response</h1>", 200, {
        +                    "content-type": "text/html"
        +                });
        +        default:
        +            return res.empty();
        +    }
        +}
        +
        +
      • +
      • +

        Dart

        +
        +
        import 'dart:async';
        +
        +Future<dynamic> main(final context) async {
        +    switch (context.req.query['type']) {
        +        case 'text':
        +            return context.res
        +                .send('This is a text response', 200);
        +        case 'json':
        +            return context.res
        +                .json({'type': 'This is a JSON response'});
        +        case 'redirect':
        +            return context.res
        +                .redirect('https://appwrite.io', 301);
        +        case 'html':
        +            return context.res
        +                .send('<h1>This is an HTML response</h1>', 200, {
        +                    'content-type': 'text/html'
        +                });
        +        default:
        +            return context.res
        +                .empty();
        +    }
        +}
        +
        +
      • +
      • +

        Swift

        +
        +
        import Foundation
        +
        +func main(context: RuntimeContext) async throws -> RuntimeOutput {
        +    switch context.req.query["type"] {
        +        case "text":
        +            return try await context.send("This is a text response", 200)
        +        case "json":
        +            return try await context.send(["type": "This is a JSON response"], 200)
        +        case "redirect":
        +            return try await context.redirect("https://appwrite.io", 301)
        +        case "html":
        +            return try await context.send("<h1>This is an HTML response</h1>", 200, [
        +                "content-type": "text/html"
        +                ])
        +        default:
        +            return try await context.empty()
        +    }
        +}
        +
        +
      • +
      • +

        .NET

        +
        +
        namespace DotNetRuntime;
        +
        +public class Handler {
        +    public async Task<RuntimeOutput> Main(RuntimeContext Context) 
        +    {
        +        switch (Context.Request.Query["type"])
        +        {
        +            case "text":
        +                return await Context.Send("This is a text response", 200);
        +            case "json":
        +                return await Context.Send(new Dictionary<string, object>() { { "type", "This is a JSON response" } }, 200);
        +            case "redirect":
        +                return await Context.Redirect("https://appwrite.io", 301);
        +            case "html":
        +                return await Context.Send("<h1>This is an HTML response</h1>", 200, new Dictionary<string, string>() {
        +                    { "content-type", "text/html" } 
        +                });
        +            default:    
        +                return await Context.Empty();
        +        }
        +    }
        +}
        +
        +
      • +
      • +

        Kotlin

        +
        +
        package io.openruntimes.kotlin.src
        +
        +import io.openruntimes.kotlin.RuntimeContext
        +import io.openruntimes.kotlin.RuntimeOutput
        +
        +class Main {
        +    fun main(context: RuntimeContext): RuntimeOutput {
        +        when (context.req.query["type"]) {
        +            "text" -> return context.send("This is a text response", 200)
        +            "json" -> return context.send(mapOf("type" to "This is a JSON response"), 200)
        +            "redirect" -> return context.redirect("https://appwrite.io", 301)
        +            "html" -> return context.send("<h1>This is an HTML response</h1>", 200, mapOf("content-type" to "text/html"))
        +            else -> return context.empty()
        +        }
        +    }
        +}
        +
        +
      • +
      • +

        Java

        +
        +
        package io.openruntimes.java.src;
        +
        +import io.openruntimes.java.RuntimeContext;
        +import io.openruntimes.java.RuntimeOutput;
        +import java.util.Map;
        +import java.util.HashMap;
        +
        +public class Main {
        +    public RuntimeOutput main(RuntimeContext context) throws Exception {
        +        switch (context.getReq().getQuery()["type"]) {
        +            case "text":
        +                return context.send("This is a text response", 200);
        +            case "json":
        +                HashMap<String, Object> data = new HashMap<>();
        +                data.put("type", "This is a JSON response");
        +                return context.send(data, 200);
        +            case "redirect":
        +                return context.redirect("https://appwrite.io", 301);
        +            case "html":
        +                return context.send("<h1>This is an HTML response</h1>", 200, Map.of("content-type", "text/html"));
        +            default:
        +                return context.empty();
        +        }
        +    }
        +}
        +
        +
      • +
      • +

        C++

        +
        +
        #include "../RuntimeResponse.h"
        +#include "../RuntimeRequest.h"
        +#include "../RuntimeOutput.h"
        +#include "../RuntimeContext.h"
        +
        +namespace runtime {
        +  class Handler {
        +    public:
        +      static RuntimeOutput main(RuntimeContext &context) {
        +        std::string type = context.req.query["type"];
        +
        +        if (type == "text") {
        +          return context.send("This is a text response", 200);
        +        } else if (type == "json") {
        +          Json::Value data;
        +          data["type"] = "This is a JSON response";
        +          return context.send(data, 200);
        +        } else if (type == "redirect") {
        +          return context.redirect("https://appwrite.io", 301);
        +        } else if (type == "html") {
        +          Json::Value headers;
        +          headers["content-type"] = "text/html";
        +          return context.send("<h1>This is an HTML response</h1>", 200, headers);
        +        } else {
        +          return context.empty();
        +        }
        +      }
        +  };
        +}
        +
        +
      • +
      +

      Dependencies

      From 7228e69949d250284603ecd10341e36a2ae0700f Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sat, 26 Aug 2023 21:16:52 +0000 Subject: [PATCH 70/80] Add link to Destructuring Assignment --- app/views/docs/functions-develop.phtml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 8f5c4cd8a..ae7ea3d84 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -3,7 +3,7 @@ Each function is handled following a request and response pattern.

      -

      Function Flow

      +

      Function Flow

      There is a clear flow for all Appwrite Functions, from beginning to end. Here's everything that happens during a function execution. @@ -478,8 +478,11 @@ public class Main { -

      Destructuring Assignment

      -

      Some languages, namely JavaScript, support destructuring. You'll see us use destructing in examples, which has the following syntax.

      +

      Destructuring Assignment

      +

      + Some languages, namely JavaScript, support destructuring. You'll see us use destructing in examples, which has the following syntax. + Learn more about destructuring assignment. +

      • Node.js

        From 7a24d8eb3e2e6d113f651b8b05f63ae113b007b9 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sat, 26 Aug 2023 22:02:27 +0000 Subject: [PATCH 71/80] ChatGPT reviewed grammar and spelling: --- app/views/docs/functions-deploy.phtml | 10 +++++----- app/views/docs/functions-develop.phtml | 8 ++++---- app/views/docs/functions-execute.phtml | 26 +++++++++++++------------- app/views/docs/functions.phtml | 14 +++++++------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 0405d7445..9071a415f 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -1,5 +1,5 @@

        - Appwrite Functions are mini application in Appwrite with their own endpoint. + Appwrite Functions are mini-applications in Appwrite with their own endpoints. Each function can have many deployments, which can be thought of as versions of the mini-application.

        @@ -15,7 +15,7 @@ This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.

        -

        Create Funcion

        +

        Create Function

        Before deploying your function with VCS, create a new function attached to your VCS repo.

        1. @@ -157,7 +157,7 @@

          Domains

          - Each deployed function can have it's own domain. + Each deployed function can have its own domain. By default, one is generated for each of your functions. You can find the generated domain for your function like this.

          @@ -179,7 +179,7 @@
        2. Navigate to the Domains tab.
        3. Click on Create domain.
        4. Input your domain in the Domain input field and click Next.
        5. -
        6. Copy the CNAME record provided to you, and add it to your domain registar.
        7. +
        8. Copy the CNAME record provided to you, and add it to your domain registrar.
        9. Click Go to console and wait for the domain name to be verified and certificate to generate.
        @@ -200,7 +200,7 @@

        Redeploy Builds

        - After updateing the configuration of your Appwrite Function, you need to redeploy your function for the changes to take effect. + After updating the configuration of your Appwrite Function, you need to redeploy your function for the changes to take effect. You can also redeploy builds to retry failed builds.

          diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index ae7ea3d84..f3afdaee5 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -446,7 +446,7 @@ public class Main {

          The Context Object

          Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite console. - All input, output, anddlogging must be handled through the context object passed in. + All input, output, and logging must be handled through the context object passed in.

          You'll find these properties in the context object.

          @@ -1284,13 +1284,13 @@ namespace runtime {

          Accessing Environment Variables

          If you need to pass constants or secrets to Appwrite Functions, you can use environment variables. - Environmental variables can be global, or function specific. + Environmental variables can be global, or function-specific.

          Default Environment Variables

          Appwrite runtimes passes in some environment variables by default. - These are always accesible for every function at runtime. + These are always accessible for every function at runtime.

          @@ -2544,7 +2544,7 @@ export function add(a, b) { import { add } from './utils.js'; -export defaut function ({res}) { +export default function ({res}) { return res.send(add(1, 2)); }
          diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 685756b8f..4fc4dc929 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -4,8 +4,8 @@ use Appwrite\Utopia\View; ?>

          - Appwrite Functions executions can be invoked in several ways. - Functions can be invoked through the Appwrite SDK and visting its REST endpoint. Functions can also be triggered by events and scheduled executions. + Appwrite Functions can be executed in several ways. + Executions can be invoked through the Appwrite SDK and visiting its REST endpoint. Functions can also be triggered by events and scheduled executions. Here are all the different ways to consume your new Appwrite Functions.

          @@ -73,7 +73,7 @@ try { }) console.log(data) } catch (err) { - console.err(err.message) + console.error(err.message) }
          @@ -175,7 +175,7 @@ try { }) console.log(data) } catch (err) { - console.err(err.message) + console.error(err.message) }
          @@ -274,7 +274,7 @@ try { }) console.log(data) } catch (err) { - console.err(err.message) + console.error(err.message) }
          @@ -425,8 +425,9 @@ public static void main(String[] args) throws Exception {

          Console

          - Another easy way to test a function is directly in the Appwrite console. - You test a function by hitting the Execute now button and + Another easy way to test a function is directly in the Appwrite Console. + You test a function by hitting the Execute now button, which will display with modal below. + You'll be able to mock executions by configuring the path, method, headers, and body.

          1. In Appwrite Console, navigate to Functions.
          2. -
          3. Click to open a function you wish to add variables to.
          4. +
          5. Click to open a function you wish to configure.
          6. Under the Settings tab, navigate to Events.
          7. Add one or multiple events as triggers for the function.
          8. Be careful to avoid selecting events that can be caused by the function itself. - This can cause the function to trigger it's own execution, resulting in infinite recursions. + This can cause the function to trigger its own execution, resulting in infinite recursions.
          @@ -488,7 +489,6 @@ $image = new View(__DIR__.'/../general/image.phtml'); -

          Permissions

          Permissions

          @@ -497,15 +497,15 @@ $image = new View(__DIR__.'/../general/image.phtml'); Server SDKs require an API key with the correct scopes.

          - If your function with a generated or custom domain, permissions are disabled for this function. - Anyone visiting this domain will be able to execute the function. + If your function has a generated or custom domain, execute permissions are ignored for this function. + Anyone visiting the configured domains will be able to execute the function. If you need to enforce permissions for functions with a domain, use authentication methods like JWT.

          Logs and results

          You can view the logs your function executions in the Appwrite Console. - Navigate to Functions and click on a function to view it's executions. + Navigate to Functions and click on a function to view its executions.

          For security reasons, Appwrite does not store the response of function executions. diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index f45f4415b..9fb059feb 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -6,14 +6,14 @@ use Appwrite\Utopia\View;

          Appwrite Functions unlock limitless potential for developers to extend Appwrite with code snippets. - Appwrite Functions are user defined functions that can start small and scale big, deploying automatically from source control. - These Functions can be triggered by HTTP requests, SDK methods, server events, webhooks, scheduled executions. - Each function will have their own URL, execute in their own isolated container, and have their own configurable environment variables and permissions. + Appwrite Functions are user-defined functions that can start small and scale big, deploying automatically from source control. + These Functions can be triggered by HTTP requests, SDK methods, server events, webhooks, and scheduled executions. + Each function will have its own URL, execute in its own isolated container, and have its own configurable environment variables and permissions.

          Getting Started

          - Appwrite Functions let you build anything you can imagine, but this flexibility makes is difficult to know where to start. + Appwrite Functions let you build anything you can imagine, but this flexibility makes it difficult to know where to start. Start exploring by cloning one of the quick start templates or using a template with pre-built integration to quickly implement features.

          @@ -29,7 +29,7 @@ $image = new View(__DIR__.'/../general/image.phtml');

          Explore Features

          - Appwrite Functions use familiar HTTP concepts, so you can learn quickly and grain transferable skills. + Appwrite Functions use familiar HTTP concepts, so you can learn quickly and gain transferable skills.

          Learn more about developing functions @@ -44,7 +44,7 @@ $image = new View(__DIR__.'/../general/image.phtml');

          - Appwrite Functions can be triggered by HTTP requests, SDK methods, server events, webhooks, scheduled executions. + Appwrite Functions can be triggered by HTTP requests, SDK methods, server events, webhooks, and scheduled executions. Explore how Appwrite Functions can be invoked.

          @@ -61,7 +61,7 @@ $image = new View(__DIR__.'/../general/image.phtml');

          Like to learn from examples? - Here's a curated list of examples that showcase Appwrite Function's capabilies. + Here's a curated list of examples that showcase Appwrite Functions' capabilities.

          Learn more about using function examples From e3683ed4a1bdd1ff3497af9b1afc79794868777c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 27 Aug 2023 13:37:11 +0000 Subject: [PATCH 72/80] PR review changes --- app/views/docs/functions-develop.phtml | 316 ++---------------------- app/views/docs/functions-examples.phtml | 28 +-- 2 files changed, 22 insertions(+), 322 deletions(-) diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index f3afdaee5..369b8148d 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -11,9 +11,9 @@

          1. The function is invoked.
          2. -
          3. Appwrite passes in request information like headers and environment variables through the context.req object.
          4. -
          5. The runtime executes the code you defined, you can log through the context.log or context.error methods.
          6. -
          7. Function terminates when you return results using context.res.
          8. +
          9. Appwrite passes in request information like headers, body or path through the context.req object.
          10. +
          11. The runtime executes the code you defined, you can log through the context.log() or context.error() methods.
          12. +
          13. Function terminates when you return results using return context.res.send(), return context.res.json() or similar.

          You'll find all of these steps in a simple function like this.

          @@ -445,7 +445,7 @@ public class Main {

          The Context Object

          - Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite console. + Context is an object passed into every function to handle communication to both the end users, and logging to the Appwrite Console. All input, output, and logging must be handled through the context object passed in.

          @@ -467,12 +467,12 @@ public class Main { Contains methods to build a response and return information. See full examples here. - log - Logs information to the Appwrite Console, end users will not be able to see these logs. See full examples here. + log() + Method to log information to the Appwrite Console, end users will not be able to see these logs. See full examples here. - error - Logs errors to the Appwrite Console, end users will not be able to see these errors. See full examples here. + error() + Methoc to log errors to the Appwrite Console, end users will not be able to see these errors. See full examples here. @@ -508,8 +508,8 @@ public class Main {

          Request

          - If you pass data into an Appwrite function, it'll be found in the request object. - This includes all invocation methods, such as data from Appwrite SDKs, HTTP calls, Appwrite events, and browsers visiting the configured domain. + If you pass data into an Appwrite Function, it'll be found in the request object. + This includes all invocation inputs from Appwrite SDKs, HTTP calls, Appwrite events, or browsers visiting the configured domain. Explore the request object with the following function, which logs all request params to the Appwrite Console.

          @@ -528,7 +528,7 @@ public class Main { log(req.port); // Port from the host header, for example 8000 log(req.path); // Path part of URL, for example /v1/hooks log(req.queryString); // Raw query params string. For example "limit=12&offset=50" - log(req.query); // Parsed query params. For example, req.query.limit + log(JSON.stringify(req.query)); // Parsed query params. For example, req.query.limit return res.send("All the request parameters are logged to the Appwrite Console."); };
          @@ -612,7 +612,7 @@ end
          log(req.port); // Port from the host header, for example 8000 log(req.path); // Path part of URL, for example /v1/hooks log(req.queryString); // Raw query params string. For example "limit=12&offset=50" - log(req.query); // Parsed query params. For example, req.query.limit + log(JSON.stringify(req.query)); // Parsed query params. For example, req.query.limit return res.send("All the request parameters are logged to the Appwrite Console.");
          @@ -1348,10 +1348,10 @@ namespace runtime { -

          Local Environment Variables

          +

          Function Environment Variables

          - Local environment variables will only be accessible in the function they belong to. - Local environment variables will override global environment variables when they have conflicting names. + Function environment variables will only be accessible in the function they belong to. + Function environment variables will override global environment variables when they have conflicting names.

          1. In Appwrite Console, navigate to Functions.
          2. @@ -1362,7 +1362,7 @@ namespace runtime {

            Global Environment Variables

            - Global environment variables are accessible to all Appwrite Functions. + Global environment variables are accessible to all Appwrite Functions in your project. Local environment variables will override global environment variables when they have conflicting names.

              @@ -1375,13 +1375,6 @@ namespace runtime { You can access the environment variables through the systems library of each language.

              - -

              - If you need to send a response to the invoker of the function, such as a user, client app, or an integration, use the response object. - The response information will not be logged to the Appwrite Console. - There are several possible ways to send a response, explore them in the following Appwrite Function. -

              -
              • Node.js

                @@ -1507,283 +1500,6 @@ namespace runtime {
              -

              Response

              - -
                -
              • -

                Node.js

                -
                -
                export default async ({ req, res, log }) => {
                -
                -    switch (req.query.type) {
                -        case 'text':
                -            return res.send("This is a text response", 200);
                -        case 'json':
                -            return res.json({"type": "This is a JSON response"}, 200);
                -        case 'redirect':
                -            return res.redirect("https://appwrite.io", 301);
                -        case 'html':
                -            return res.send(
                -                "<h1>This is an HTML response</h1>", 200, {
                -                    "content-type": "text/html"
                -                });
                -        default:
                -            return res.empty();
                -    }
                -}
                -
                -
              • -
              • -

                PHP

                -
                -
                <?php
                -
                -return function ($context) {
                -    switch ($context->req->query['type']) {
                -        case 'text':
                -            return $context->res->send("This is a text response", 200);
                -        case 'json':
                -            return $context->res->json(["type" => "This is a JSON response"], 200);
                -        case 'redirect':
                -            return $context->res->redirect("https://appwrite.io", 301);
                -        case 'html':
                -            return $context->res->send("<h1>This is an HTML response</h1>", 200, [
                -                "content-type" => "text/html"
                -                ]);
                -        default:
                -            return $context->res->empty();
                -    }
                -};
                -
                -
              • -
              • -

                Python

                -
                -
                def main(context):
                -    switch context.req.query['type']:
                -        case 'text':
                -            return context.res.send("This is a text response", 200)
                -        case 'json':
                -            return context.res.json({"type": "This is a JSON response"}, 200)
                -        case 'redirect':
                -            return context.res.redirect("https://appwrite.io", 301)
                -        case 'html':
                -            return context.res.send("<h1>This is an HTML response</h1>", 200, {
                -                "content-type": "text/html"
                -            })
                -        default:
                -            return context.res.empty()
                -
                -
              • -
              • -

                Ruby

                -
                -
                def main(context)
                -    case context.req.query['type'] 
                -        when 'text'
                -            return context.res.send("This is a text response", 200)
                -        when 'json'
                -            return context.res.json({"type": "This is a JSON response"}, 200)
                -        when 'redirect'
                -            return context.res.redirect("https://appwrite.io", 301)
                -        when 'html'
                -            return context.res.send("<h1>This is an HTML response</h1>", 200, {
                -                "content-type": "text/html"
                -            })
                -        else
                -            return context.res.empty()
                -    end
                -end
                -
                -
              • -
              • -

                Deno

                -
                -
                export default async ({ req, res, log }) => {
                -
                -    switch (req.query.type) {
                -        case 'text':
                -            return res.send("This is a text response", 200);
                -        case 'json':
                -            return res.json({type": "This is a JSON response"}, 200);
                -        case 'redirect':
                -            return res.redirect("https://appwrite.io", 301);
                -        case 'html':
                -            return res.send(
                -                "<h1>This is an HTML response</h1>", 200, {
                -                    "content-type": "text/html"
                -                });
                -        default:
                -            return res.empty();
                -    }
                -}
                -
                -
              • -
              • -

                Dart

                -
                -
                import 'dart:async';
                -
                -Future<dynamic> main(final context) async {
                -    switch (context.req.query['type']) {
                -        case 'text':
                -            return context.res
                -                .send('This is a text response', 200);
                -        case 'json':
                -            return context.res
                -                .json({'type': 'This is a JSON response'});
                -        case 'redirect':
                -            return context.res
                -                .redirect('https://appwrite.io', 301);
                -        case 'html':
                -            return context.res
                -                .send('<h1>This is an HTML response</h1>', 200, {
                -                    'content-type': 'text/html'
                -                });
                -        default:
                -            return context.res
                -                .empty();
                -    }
                -}
                -
                -
              • -
              • -

                Swift

                -
                -
                import Foundation
                -
                -func main(context: RuntimeContext) async throws -> RuntimeOutput {
                -    switch context.req.query["type"] {
                -        case "text":
                -            return try await context.send("This is a text response", 200)
                -        case "json":
                -            return try await context.send(["type": "This is a JSON response"], 200)
                -        case "redirect":
                -            return try await context.redirect("https://appwrite.io", 301)
                -        case "html":
                -            return try await context.send("<h1>This is an HTML response</h1>", 200, [
                -                "content-type": "text/html"
                -                ])
                -        default:
                -            return try await context.empty()
                -    }
                -}
                -
                -
              • -
              • -

                .NET

                -
                -
                namespace DotNetRuntime;
                -
                -public class Handler {
                -    public async Task<RuntimeOutput> Main(RuntimeContext Context) 
                -    {
                -        switch (Context.Request.Query["type"])
                -        {
                -            case "text":
                -                return await Context.Send("This is a text response", 200);
                -            case "json":
                -                return await Context.Send(new Dictionary<string, object>() { { "type", "This is a JSON response" } }, 200);
                -            case "redirect":
                -                return await Context.Redirect("https://appwrite.io", 301);
                -            case "html":
                -                return await Context.Send("<h1>This is an HTML response</h1>", 200, new Dictionary<string, string>() {
                -                    { "content-type", "text/html" } 
                -                });
                -            default:    
                -                return await Context.Empty();
                -        }
                -    }
                -}
                -
                -
              • -
              • -

                Kotlin

                -
                -
                package io.openruntimes.kotlin.src
                -
                -import io.openruntimes.kotlin.RuntimeContext
                -import io.openruntimes.kotlin.RuntimeOutput
                -
                -class Main {
                -    fun main(context: RuntimeContext): RuntimeOutput {
                -        when (context.req.query["type"]) {
                -            "text" -> return context.send("This is a text response", 200)
                -            "json" -> return context.send(mapOf("type" to "This is a JSON response"), 200)
                -            "redirect" -> return context.redirect("https://appwrite.io", 301)
                -            "html" -> return context.send("<h1>This is an HTML response</h1>", 200, mapOf("content-type" to "text/html"))
                -            else -> return context.empty()
                -        }
                -    }
                -}
                -
                -
              • -
              • -

                Java

                -
                -
                package io.openruntimes.java.src;
                -
                -import io.openruntimes.java.RuntimeContext;
                -import io.openruntimes.java.RuntimeOutput;
                -import java.util.Map;
                -import java.util.HashMap;
                -
                -public class Main {
                -    public RuntimeOutput main(RuntimeContext context) throws Exception {
                -        switch (context.getReq().getQuery()["type"]) {
                -            case "text":
                -                return context.send("This is a text response", 200);
                -            case "json":
                -                HashMap<String, Object> data = new HashMap<>();
                -                data.put("type", "This is a JSON response");
                -                return context.send(data, 200);
                -            case "redirect":
                -                return context.redirect("https://appwrite.io", 301);
                -            case "html":
                -                return context.send("<h1>This is an HTML response</h1>", 200, Map.of("content-type", "text/html"));
                -            default:
                -                return context.empty();
                -        }
                -    }
                -}
                -
                -
              • -
              • -

                C++

                -
                -
                #include "../RuntimeResponse.h"
                -#include "../RuntimeRequest.h"
                -#include "../RuntimeOutput.h"
                -#include "../RuntimeContext.h"
                -
                -namespace runtime {
                -  class Handler {
                -    public:
                -      static RuntimeOutput main(RuntimeContext &context) {
                -        std::string type = context.req.query["type"];
                -
                -        if (type == "text") {
                -          return context.send("This is a text response", 200);
                -        } else if (type == "json") {
                -          Json::Value data;
                -          data["type"] = "This is a JSON response";
                -          return context.send(data, 200);
                -        } else if (type == "redirect") {
                -          return context.redirect("https://appwrite.io", 301);
                -        } else if (type == "html") {
                -          Json::Value headers;
                -          headers["content-type"] = "text/html";
                -          return context.send("<h1>This is an HTML response</h1>", 200, headers);
                -        } else {
                -          return context.empty();
                -        }
                -      }
                -  };
                -}
                -
                -
              • -
              -

              Dependencies

              diff --git a/app/views/docs/functions-examples.phtml b/app/views/docs/functions-examples.phtml index a8dd01968..d30c0b827 100644 --- a/app/views/docs/functions-examples.phtml +++ b/app/views/docs/functions-examples.phtml @@ -24,7 +24,7 @@

              npm install undici
              -

              Finally, add npm install to your function's build commands in the Appwrite console.

              +

              Finally, add npm install to your function's build commands in the Appwrite Console.

            1. @@ -38,7 +38,7 @@
              composer require guzzlehttp/guzzle
              -

              Finally, add composer install to your function's build commands in the Appwrite console.

              +

              Finally, add composer install to your function's build commands in the Appwrite Console.

            2. @@ -53,7 +53,7 @@
              echo "requests" >> requirements.txt
               pip install -r requirements.txt
              -

              Finally, add pip install -r requirements.txt to your function's build commands in the Appwrite console.

              +

              Finally, add pip install -r requirements.txt to your function's build commands in the Appwrite Console.

            3. @@ -78,7 +78,7 @@ environment:
              pub install http

              - Finally, add pub get to your function's build commands in the Appwrite console. + Finally, add pub get to your function's build commands in the Appwrite Console.

            4. @@ -101,7 +101,7 @@ environment: bundle install

              - Finally, add bundle install to your function's build commands in the Appwrite console. + Finally, add bundle install to your function's build commands in the Appwrite Console.

              @@ -608,7 +608,7 @@ def main(context):

              Use the function by navigating to the function URL in the browser. The URL should contain the required parameters. - For example, [YOUR_FUNCTION_URL]?userId=[USER_ID]&topicId=[TOPIC_ID];&vote=yes to cast a vote. + For example, [YOUR_FUNCTION_URL]/?userId=[USER_ID]&topicId=[TOPIC_ID]&vote=yes to cast a vote.

              HTML Contact Form

              @@ -939,19 +939,3 @@ Future main(final context) async {

              Use the function by navigating to the function URL in the browser. Submit the form to store the message in the collection.

              - - -[TODO: @luke -> Example with JWT, show both client and server code] - - - - \ No newline at end of file From 67f2c434162c53a2530230f4c777865611f6f635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 27 Aug 2023 13:37:31 +0000 Subject: [PATCH 73/80] Fix console name spelling --- app/views/docs/authentication-security.phtml | 4 ++-- app/views/docs/authentication.phtml | 4 ++-- app/views/docs/custom-domains.phtml | 2 +- app/views/docs/email-delivery.phtml | 2 +- app/views/docs/functions-deploy.phtml | 6 +++--- app/views/docs/getting-started-for-android.phtml | 4 ++-- app/views/docs/getting-started-for-apple.phtml | 2 +- app/views/docs/getting-started-for-flutter.phtml | 4 ++-- app/views/docs/getting-started-for-server.phtml | 2 +- app/views/docs/getting-started-for-web.phtml | 2 +- app/views/docs/keys.phtml | 2 +- app/views/docs/permissions-old.phtml | 2 +- app/views/docs/permissions.phtml | 2 +- app/views/docs/production.phtml | 2 +- app/views/docs/self-hosting.phtml | 2 +- 15 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/views/docs/authentication-security.phtml b/app/views/docs/authentication-security.phtml index 241e6893e..fe26070e9 100644 --- a/app/views/docs/authentication-security.phtml +++ b/app/views/docs/authentication-security.phtml @@ -87,10 +87,10 @@

              Password History

              Password history prevents users from reusing recent passwords. This protects user accounts from security risks by enforcing a new password everytime it's changed.

              -

              Password history can be enabled in the Auth service's Security tab on the Appwrite console. You can choose how many previous passwords to remember up to a maximum of 20 and block users from reusing them.

              +

              Password history can be enabled in the Auth service's Security tab on the Appwrite Console. You can choose how many previous passwords to remember up to a maximum of 20 and block users from reusing them.

              Password Dictionary

              Password dictionary protects users from using bad passwords. It compares the user's password to the 10,000 most common passwords and throws an error if there's a match. Together with rate limits, password dictionary will significantly reduce the chance of a malicious actor from guessing user passwords.

              -

              Password dictionary can be enabled in the Auth service's Security tab on the Appwrite console.

              \ No newline at end of file +

              Password dictionary can be enabled in the Auth service's Security tab on the Appwrite Console.

              \ No newline at end of file diff --git a/app/views/docs/authentication.phtml b/app/views/docs/authentication.phtml index 9910bc286..065b96683 100644 --- a/app/views/docs/authentication.phtml +++ b/app/views/docs/authentication.phtml @@ -513,7 +513,7 @@ mutation {

              - Only redirect URLs to domains added as a platform on your Appwrite console will be accepted. URLs not added as a platform are rejected to protect against redirect attacks. + Only redirect URLs to domains added as a platform on your Appwrite Console will be accepted. URLs not added as a platform are rejected to protect against redirect attacks.

              @@ -898,7 +898,7 @@ let session = try await account.get()

              - Only redirect URLs to domains added as a platform on your Appwrite console will be accepted. URLs not added as a platform are rejected to protect against redirect attacks. + Only redirect URLs to domains added as a platform on your Appwrite Console will be accepted. URLs not added as a platform are rejected to protect against redirect attacks.

                diff --git a/app/views/docs/custom-domains.phtml b/app/views/docs/custom-domains.phtml index 74c087140..5c178c322 100644 --- a/app/views/docs/custom-domains.phtml +++ b/app/views/docs/custom-domains.phtml @@ -356,7 +356,7 @@ $dns = [

                Confirm and Verify Your Domain

                -

                Once you added your new CNAME record to your DNS settings, you will need to verify your new domain name from your Appwrite console. Enter your custom domains tab from your project settings, click the DNS Settings link and click on the 'Confirm and Verify" button. If everything went well, Appwrite will approve your domain and generate a new SSL certificate for it in the background.

                +

                Once you added your new CNAME record to your DNS settings, you will need to verify your new domain name from your Appwrite Console. Enter your custom domains tab from your project settings, click the DNS Settings link and click on the 'Confirm and Verify" button. If everything went well, Appwrite will approve your domain and generate a new SSL certificate for it in the background.

                Enjoy your Free SSL Certificate

                diff --git a/app/views/docs/email-delivery.phtml b/app/views/docs/email-delivery.phtml index ad8000572..1ad88c091 100644 --- a/app/views/docs/email-delivery.phtml +++ b/app/views/docs/email-delivery.phtml @@ -66,4 +66,4 @@ The next possible source of error is the configuration in your .env file. Make s
                docker compose up -d --build --force-recreate
                -

                Now you can head over to your Appwrite console, logout from your account and try to recover your password or send invites to other team members from your Appwrite console using your newly configured SMTP provider.

                +

                Now you can head over to your Appwrite Console, logout from your account and try to recover your password or send invites to other team members from your Appwrite Console using your newly configured SMTP provider.

                diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 9071a415f..100d4e95a 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -101,14 +101,14 @@

                Overwrite Warning

                - If you made changes in the Appwrite console that is different from your appwrite.json, + If you made changes in the Appwrite Console that is different from your appwrite.json, using the CLI deploy command will overwrite your console changes, such as execution schedule or permissions. Update your appwrite.json manually before deploying to avoid overwriting changes.

                Manual Deployment

                -

                You can also upload your functions to be deployed using the Appwrite console. The example below shows a simple Node.JS function, but the same idea applies to any other language.

                +

                You can also upload your functions to be deployed using the Appwrite Console. The example below shows a simple Node.JS function, but the same idea applies to any other language.

                .
                @@ -143,7 +143,7 @@
                     
                 
              -

              Next, navigate to your Appwrite console and upload the function.

              +

              Next, navigate to your Appwrite Console and upload the function.

              1. Navigate to the function you want to deploy.
              2. diff --git a/app/views/docs/getting-started-for-android.phtml b/app/views/docs/getting-started-for-android.phtml index 74d35cbf0..207aed6af 100644 --- a/app/views/docs/getting-started-for-android.phtml +++ b/app/views/docs/getting-started-for-android.phtml @@ -19,7 +19,7 @@ $androidVersion = (isset($versions['android'])) ? $versions['android'] : '';

                Add your Android Platform

                -

                To init your SDK and start interacting with Appwrite services, you need to add a new Android platform to your project. To add a new platform, go to your Appwrite console, choose the project you created in the step before, and click the 'Add Platform' button. Only API requests initiated from platforms added to your Appwrite project will be accepted. This prevents unauthorized apps from accessing your Appwrite project.

                +

                To init your SDK and start interacting with Appwrite services, you need to add a new Android platform to your project. To add a new platform, go to your Appwrite Console, choose the project you created in the step before, and click the 'Add Platform' button. Only API requests initiated from platforms added to your Appwrite project will be accepted. This prevents unauthorized apps from accessing your Appwrite project.

                From the options, choose to add a new Android platform and add add your app name and package name, your package name is generally the applicationId in your app-level build.gradle file. By registering your new app platform, you are allowing your app to communicate with the Appwrite API.

                @@ -40,7 +40,7 @@ $androidVersion = (isset($versions['android'])) ? $versions['android'] : '';

                OAuth Callback

                -

                In order to capture the Appwrite OAuth callback url, the following activity needs to be added inside the `<application>` tag, along side the existing `<activity>` tags in your AndroidManifest.xml. Be sure to replace the [PROJECT_ID] string with your actual Appwrite project ID. You can find your Appwrite project ID in your project settings screen in your Appwrite console.

                +

                In order to capture the Appwrite OAuth callback url, the following activity needs to be added inside the `<application>` tag, along side the existing `<activity>` tags in your AndroidManifest.xml. Be sure to replace the [PROJECT_ID] string with your actual Appwrite project ID. You can find your Appwrite project ID in your project settings screen in your Appwrite Console.

                escape('
                diff --git a/app/views/docs/getting-started-for-apple.phtml b/app/views/docs/getting-started-for-apple.phtml
                index fc6ce3038..fd431809c 100644
                --- a/app/views/docs/getting-started-for-apple.phtml
                +++ b/app/views/docs/getting-started-for-apple.phtml
                @@ -19,7 +19,7 @@ $appleVersion = $versions['apple'] ?? '';
                 
                 

                Add your Apple Platform

                -

                To init your SDK and start interacting with Appwrite services, you need to add a new Apple platform to your project. To add a new platform, go to your Appwrite console, choose the project you created in the step before, and click the 'Add Platform' button. Only API requests initiated from platforms added to your Appwrite project will be accepted. This prevents unauthorized apps from accessing your Appwrite project.

                +

                To init your SDK and start interacting with Appwrite services, you need to add a new Apple platform to your project. To add a new platform, go to your Appwrite Console, choose the project you created in the step before, and click the 'Add Platform' button. Only API requests initiated from platforms added to your Appwrite project will be accepted. This prevents unauthorized apps from accessing your Appwrite project.

                From the options, choose to add a new Apple platform, select the iOS, macOS, watchOS or tvOS tab and add your app name and bundle identifier, Your bundle identifier can be found at the top of the General tab in your project settings, or in your Info.plist file. By registering your new app platform, you are allowing your app to communicate with the Appwrite API.

                diff --git a/app/views/docs/getting-started-for-flutter.phtml b/app/views/docs/getting-started-for-flutter.phtml index 6bd614873..47355fa81 100644 --- a/app/views/docs/getting-started-for-flutter.phtml +++ b/app/views/docs/getting-started-for-flutter.phtml @@ -19,7 +19,7 @@ $version = (isset($versions['flutter'])) ? $versions['flutter'] : '';

                Add your Flutter Platform

                -

                To init your SDK and start interacting with Appwrite services, you need to add a new Flutter platform to your project. To add a new platform, go to your Appwrite console, choose the project you created in the step before, and click the 'Add Platform' button. Only API requests initiated from platforms added to your Appwrite project will be accepted. This prevents unauthorized apps from accessing your Appwrite project.

                +

                To init your SDK and start interacting with Appwrite services, you need to add a new Flutter platform to your project. To add a new platform, go to your Appwrite Console, choose the project you created in the step before, and click the 'Add Platform' button. Only API requests initiated from platforms added to your Appwrite project will be accepted. This prevents unauthorized apps from accessing your Appwrite project.

                From the options, choose to add a new Flutter platform and add your app credentials. Appwrite Flutter SDK currently supports building apps for Android, iOS, Linux, Mac OS, Web and Windows.

                @@ -29,7 +29,7 @@ $version = (isset($versions['flutter'])) ? $versions['flutter'] : '';

                For Android first add your app name and package name, Your package name is generally the applicationId in your app-level build.gradle file. By registering your new app platform, you are allowing your app to communicate with the Appwrite API.

                -

                In order to capture the Appwrite OAuth callback url, the following activity needs to be added inside the `<application>` tag, along side the existing `<activity>` tags in your AndroidManifest.xml. Be sure to replace the [PROJECT_ID] string with your actual Appwrite project ID. You can find your Appwrite project ID in you project settings screen in your Appwrite console.

                +

                In order to capture the Appwrite OAuth callback url, the following activity needs to be added inside the `<application>` tag, along side the existing `<activity>` tags in your AndroidManifest.xml. Be sure to replace the [PROJECT_ID] string with your actual Appwrite project ID. You can find your Appwrite project ID in you project settings screen in your Appwrite Console.

                escape('
                diff --git a/app/views/docs/getting-started-for-server.phtml b/app/views/docs/getting-started-for-server.phtml
                index e5ede12fe..b05dd86e3 100644
                --- a/app/views/docs/getting-started-for-server.phtml
                +++ b/app/views/docs/getting-started-for-server.phtml
                @@ -109,7 +109,7 @@ $swiftVersion = $versions['swift'] ?? '';
                 
                 

                Create Your First Appwrite Project

                -

                Go to your new Appwrite console and click the icon in the top navigation header or on the 'Create Project' button on your console homepage. Choose a name for your project and click create to get started.

                +

                Go to your new Appwrite Console and click the icon in the top navigation header or on the 'Create Project' button on your console homepage. Choose a name for your project and click create to get started.

                Authentication

                diff --git a/app/views/docs/getting-started-for-web.phtml b/app/views/docs/getting-started-for-web.phtml index d7ec5bc2a..979053852 100644 --- a/app/views/docs/getting-started-for-web.phtml +++ b/app/views/docs/getting-started-for-web.phtml @@ -21,7 +21,7 @@ $demos = $platform['demos'] ?? [];

                Add Your Web Platform

                -

                To init your SDK and interact with Appwrite services, you need to add a web platform to your project. To add a new platform, go to your Appwrite console, choose the project you created in the step before and click the 'Add Platform' button.

                +

                To init your SDK and interact with Appwrite services, you need to add a web platform to your project. To add a new platform, go to your Appwrite Console, choose the project you created in the step before and click the 'Add Platform' button.

                From the options, choose to add a web platform and add your client app hostname. By adding your hostname to your project platform, you are allowing cross-domain communication between your project and the Appwrite API. Only web apps hosted on domains specified in a web platform will be able to make requests to your Appwrite instance, preventing unwanted access from malicious actors.

                diff --git a/app/views/docs/keys.phtml b/app/views/docs/keys.phtml index cfff198a4..a63fff508 100644 --- a/app/views/docs/keys.phtml +++ b/app/views/docs/keys.phtml @@ -2,7 +2,7 @@ $scopes = $this->getParam('scopes', ); ?> -

                Using your API Keys, you can access Appwrite services using the SDK of your choice. To create a new API key, go to your API keys tab in your project setting using your Appwrite console and click the 'Add API Key' button.

                +

                Using your API Keys, you can access Appwrite services using the SDK of your choice. To create a new API key, go to your API keys tab in your project setting using your Appwrite Console and click the 'Add API Key' button.

                When adding a new API Key, you can choose which scope to grant your application. If you need to replace your API Key, create a new key, update your app credentials and, once ready, delete your old key.

                diff --git a/app/views/docs/permissions-old.phtml b/app/views/docs/permissions-old.phtml index ac8cb8ecb..a1d7ff4c5 100644 --- a/app/views/docs/permissions-old.phtml +++ b/app/views/docs/permissions-old.phtml @@ -10,7 +10,7 @@

                For example, only users with a guest role can access authentication endpoints while access to member users is denied.

                -

                You can change your project members' roles from your project settings in the Appwrite console.

                +

                You can change your project members' roles from your project settings in the Appwrite Console.

                diff --git a/app/views/docs/permissions.phtml b/app/views/docs/permissions.phtml index adb1bbd3d..1c10fc523 100644 --- a/app/views/docs/permissions.phtml +++ b/app/views/docs/permissions.phtml @@ -18,7 +18,7 @@

                Default Values

                -

                If you create a resource using a Server SDK or the Appwrite console without explicit permissions, no one can access it by default because the permissions will be empty. If you create a resource using a Client SDK without explicit permissions, the creator will be granted read, update, and delete permissions on that resource by default.

                +

                If you create a resource using a Server SDK or the Appwrite Console without explicit permissions, no one can access it by default because the permissions will be empty. If you create a resource using a Client SDK without explicit permissions, the creator will be granted read, update, and delete permissions on that resource by default.

                Server Integration

                diff --git a/app/views/docs/production.phtml b/app/views/docs/production.phtml index 3133817d1..460e73f91 100644 --- a/app/views/docs/production.phtml +++ b/app/views/docs/production.phtml @@ -15,7 +15,7 @@

                Limit Console Access

                -

                Appwrite provides three different methods to limit access to your Appwrite console.

                +

                Appwrite provides three different methods to limit access to your Appwrite Console.

                1. Whitelist a group of developers by IP using the _APP_CONSOLE_WHITELIST_IPS environment variable.
                2. diff --git a/app/views/docs/self-hosting.phtml b/app/views/docs/self-hosting.phtml index 2aafd2f6f..2f6c39fd9 100644 --- a/app/views/docs/self-hosting.phtml +++ b/app/views/docs/self-hosting.phtml @@ -136,7 +136,7 @@
                  docker compose up -d --remove-orphans
                  -

                  Once the Docker installation completes, go to your machine's hostname or IP address on your browser to access the Appwrite console. Please note that on hosts that are not Linux-native, the server might take a few minutes to start after installation completes.

                  +

                  Once the Docker installation completes, go to your machine's hostname or IP address on your browser to access the Appwrite Console. Please note that on hosts that are not Linux-native, the server might take a few minutes to start after installation completes.

                  Stop

                  From 870726e6a2011b60b90ab45d88228b1698c200e5 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 27 Aug 2023 09:50:26 -0400 Subject: [PATCH 74/80] Update app/views/docs/functions.phtml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matej Bačo --- app/views/docs/functions.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/docs/functions.phtml b/app/views/docs/functions.phtml index 9fb059feb..0d1f9e026 100644 --- a/app/views/docs/functions.phtml +++ b/app/views/docs/functions.phtml @@ -56,7 +56,7 @@ $image = new View(__DIR__.'/../general/image.phtml'); Avoid adding additional complexity to your codebase by coding in languages you already use and love.

                  -Learn more about using function runtimes +Learn more about using function runtimes

                  From a84c5650b4becdd3b5043c1cf67ed4e7e75ce3ef Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 27 Aug 2023 09:53:37 -0400 Subject: [PATCH 75/80] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matej Bačo --- app/views/docs/functions-deploy.phtml | 19 ++++++++++--------- app/views/docs/functions-execute.phtml | 16 +++++++++------- app/views/docs/functions-runtimes.phtml | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 100d4e95a..0a6c2b8e0 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -31,20 +31,21 @@ Search for the Git repository that hold your function and click connect.

                3. - Select a production branch. New commits made to the production branch will be automatically deployed and activated. + Select a production branch. New commits pushed to the production branch will be automatically activated. Commits to any other branch will still be deployed, but not be activated.
                4. - Input the root directory of the function inside the repository. + Input the root directory of the function inside the repository. If you have only one function in your repository, you can leave this empty. If you have multiple, root directory should point to the folder of your function. This should be the directory in which your custom build commands can run successfully. It also improves efficiency because only what's necessary is cloned.
                5. - If you don't want deploy comments to be made on your PRs, select Silent mode. + If you don't want deploy comments to be made on your pull requests or commits, select Silent mode.
                6. - Name your function, select a runtime that matches your function, and enter an entry point path, relative to the root directory from the previous step. + Name your function, select a runtime that matches your function, and enter entrypoint, relative to the root directory from the previous step. Entrypoint is path to the main file of your function, which exports the function to be run on every execution.
                7. If you have build steps, like installing dependencies, input the commands into the Build settings heading's Command field. You can combine multiple commands using &&, such as npm install && npm build. + For compiled languages you don't need to worry about installing dependencies, as that's done automatically during compilation step.
                8. Finally, configure the execute permissions of the function. For security, only provide execute permissions to the necessary roles. @@ -54,7 +55,7 @@

                  Deploy

                  1. - Checkout your production branch in Git. + Using Git, checkout the branch you configured as production branch when creating the Appwrite Function.
                  2. Create a new commit. @@ -63,18 +64,18 @@ Push your new commit.
                  3. - A new deployment will be automatically created. Deployments will be automatically activated when new commits are added to the production branch. + A new deployment will be automatically created, built and activated.

                  CLI

                  CLI Setup

                  -

                  Before you can deploy with the Appwrite CLI, make sure you've installed and initialized the CLI

                  +

                  Before you can deploy with the Appwrite CLI, make sure you've installed and initialized the CLI.

                  To deploy with the Appwrite CLI, your function must be added to appwrite.json that tells the CLI where each function is stored. - To ensure the folder structure is setup correctly and appwrite.json is configured correctly, use the appwrite init function method to create a shell function, then paste in your function code. + To ensure the folder structure is setup correctly and appwrite.json is configured correctly, use the appwrite init function method to create a starter function, then paste in your function code.

                  @@ -151,7 +152,7 @@

                9. Select the Manual tab.
                10. Input the entry point of your function under Entrypoint. For the example above, it would be index.js.
                11. Upload code.tar.gz.
                12. -
                13. Select Activate deployment after build to use your new function.
                14. +
                15. Select Activate deployment after build to use your new deployment.
                16. Click Create to deploy your function.
                diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 4fc4dc929..c017e601b 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -6,7 +6,7 @@ use Appwrite\Utopia\View;

                Appwrite Functions can be executed in several ways. Executions can be invoked through the Appwrite SDK and visiting its REST endpoint. Functions can also be triggered by events and scheduled executions. - Here are all the different ways to consume your new Appwrite Functions. + Here are all the different ways to consume your Appwrite Functions.

                Domains

                @@ -23,14 +23,14 @@ use Appwrite\Utopia\View;

                - Alternatively you can add a custom domain to your Appwrite project. + Alternatively you can add a custom domain to your Appwrite Function.

                REST API

                When requests are made to this domain, whether through a browser or through an HTTP requests, - the request information like request headers and request body will be passed to the function. - This unlocks interesting ways to integrate other apps and backends to your Appwrite project. + the request information like request URL, request headers, and request body will be passed to the function. + This unlocks ability for Appwrite Function to become a full-blown API server on its own. It also allows accepting incoming webhooks for handling online payments, hosting social platform bots, and much more.

                @@ -68,8 +68,10 @@ client const functions = new Functions(client) try { - const data = await functions.createExecution('[FUNCTION_ID]', { + const data = await functions.createExecution('[FUNCTION_ID]', JSON.stringify({ 'foo': 'bar' + }), '/', 'GET', { + 'X-Custom-Header': '123' }) console.log(data) } catch (err) { @@ -497,8 +499,8 @@ $image = new View(__DIR__.'/../general/image.phtml'); Server SDKs require an API key with the correct scopes.

                - If your function has a generated or custom domain, execute permissions are ignored for this function. - Anyone visiting the configured domains will be able to execute the function. + If your function has a generated or custom domain, executions are not authenticated. + Anyone visiting the configured domains will be considered a guest, so make sure to give `Any` execute permission in order for domain executions to work. If you need to enforce permissions for functions with a domain, use authentication methods like JWT.

                diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 928071a3f..e1fe3be9c 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -29,7 +29,7 @@ $sorted_runtimes['Dart']["cloud"] = true;

                Appwrite Functions supports an extensive list of runtimes to meet your unique tech preferences. - Not all runtimes are available on Appwrite Cloud, check for the Cloud label in each listed runtime to know which ones are available. + Not all runtimes are available on Appwrite Cloud yet. Check for the Cloud label in each listed runtime to know which ones are available.

                Supported Runtimes

                From f70b7671ebe7f25c1ef58427f75785ad93e58de9 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 27 Aug 2023 14:22:32 +0000 Subject: [PATCH 76/80] Address matej's review comments --- app/views/docs/functions-deploy.phtml | 13 +++++------ app/views/docs/functions-execute.phtml | 2 +- app/views/docs/functions-runtimes.phtml | 30 +++++++++++++------------ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 0a6c2b8e0..62ee5219f 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -9,14 +9,14 @@ Here's everything you need to know to deploy your first Appwrite Function.

                -

                VCS (Version Control System)

                +

                Git

                The recommended way to manage your Appwrite Function deployments is to use a version control system, like Git. This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.

                Create Function

                -

                Before deploying your function with VCS, create a new function attached to your VCS repo.

                +

                Before deploying your function with Git, create a new function attached to your Git repo.

                1. Navigate to Functions from the side bar of the Appwrite Console. @@ -109,8 +109,7 @@

                Manual Deployment

                -

                You can also upload your functions to be deployed using the Appwrite Console. The example below shows a simple Node.JS function, but the same idea applies to any other language.

                - +

                You can upload your functions to be deployed using the Appwrite Console. The example below shows a simple Node.js function, but the same idea applies to any other language.

                .
                 ├── package.json
                @@ -166,7 +165,7 @@
                 
                1. Navigate to the Appwrite Console's Functions page.
                2. Navigate to the Domains tab.
                3. -
                4. In the table, you'll find a link formatted similar to https://64d4d22db370ae41a32e.appwrite.global. This is your preview.
                5. +
                6. In the table, you'll find a link formatted similar to https://64d4d22db370ae41a32e.appwrite.global. This is your generated domain.

                @@ -208,10 +207,10 @@

              3. In Appwrite Console, navigate to Functions.
              4. Click to open a function you wish to inspect.
              5. Under the Deployments tab, you'll find the status of the current active deployment.
              6. -
              7. You can redeploy by clicking the Redeploy button.
              8. +
              9. You can redeploy by clicking the triple-dots beside an execution, and hitting the Redeploy button.
              10. The redeployment behavior varies depending on how the initial deployment is created. - For VCS deployments, redeploy uses the same commit hash but updated function settings. + For Git deployments, redeploy uses the same commit hash but updated function settings. For manual and CLI deployments, redeploy uses previously updated code but updated function settings.

                \ No newline at end of file diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index c017e601b..2df0337d3 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -47,7 +47,7 @@ use Appwrite\Utopia\View;

                -Learn more about using the Appwrite SDKs +Learn more about using the Appwrite SDKs

                Client SDKs

                diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index e1fe3be9c..69a946b74 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -4,6 +4,12 @@ use Appwrite\Utopia\View; $events = $this->getParam('events', []); $runtimes = $this->getParam('runtimes', []); +$runtimes['node-16.0']["cloud"] = true; +$runtimes['node-18.0']["cloud"] = true; +$runtimes['php-8.0']["cloud"] = true; +$runtimes['ruby-3.0']["cloud"] = true; +$runtimes['python-3.9']["cloud"] = true; +$runtimes['dart-2.17']["cloud"] = true; $sorted_runtimes = []; @@ -18,12 +24,6 @@ foreach ($runtimes as $key => $item) { $sorted_runtimes[$name]['versions'][] = $item; } - -$sorted_runtimes['Node.js']["cloud"] = true; -$sorted_runtimes['PHP']["cloud"] = true; -$sorted_runtimes['Ruby']["cloud"] = true; -$sorted_runtimes['Python']["cloud"] = true; -$sorted_runtimes['Dart']["cloud"] = true; ?> @@ -41,9 +41,9 @@ $sorted_runtimes['Dart']["cloud"] = true;
                + - @@ -53,7 +53,15 @@ $sorted_runtimes['Dart']["cloud"] = true; Function Env. + - From 7a307fb3a95fc98737d3cbab536226ea21c67660 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 27 Aug 2023 14:23:35 +0000 Subject: [PATCH 77/80] Fix runtimes by showing only cloud tags beside relevant items + return the copy button --- app/views/docs/functions-runtimes.phtml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/views/docs/functions-runtimes.phtml b/app/views/docs/functions-runtimes.phtml index 69a946b74..663d4eaf0 100644 --- a/app/views/docs/functions-runtimes.phtml +++ b/app/views/docs/functions-runtimes.phtml @@ -40,10 +40,11 @@ foreach ($runtimes as $key => $item) { - - + + + - + @@ -53,17 +54,21 @@ foreach ($runtimes as $key => $item) { Function Env. + $version): ?> + escape($version['version']); ?>
                +
                + - + - + - + - + @@ -487,20 +487,32 @@ public class Main {
              11. Node.js

                -
                export default async function ({ req, res, log, error }) {
                -    log('This is a log!');
                -    error('This is an error!');
                -    return res.send(`This function was called with ${req.method} method!`)
                +            
                // before destructuring
                +export default async function (context) {
                +    context.log("This is a log!");
                +    // ... more code
                +}
                +
                +// after destructuring
                +export default async function ({ req, res, log, error }) {
                +    log("This is a log!");
                +    // ... more code
                 }
              12. Deno

                -
                export default async function ({ req, res, log, error }: any) {
                -    log('This is a log!');
                -    error('This is an error!');
                -    return res.send(`This function was called with ${req.method} method!`)
                +            
                // before destructuring
                +export default async function (context: any) {
                +    context.log("This is a log!");
                +    // ... more code
                +}
                +   
                +// after destructuring
                +export default async function ({ req, res, log, error }: any) {
                +    context.log("This is a log!");
                +    // ... more code
                 }
              13. @@ -829,7 +841,7 @@ public class Main {
                export default async ({ req, res, log }) => {
                 
                     switch (req.query.type) {
                -        case 'text':
                +        case 'text': 
                             return res.send("This is a text response", 200);
                         case 'json':
                             return res.json({"type": "This is a JSON response"}, 200);
                @@ -1097,6 +1109,47 @@ namespace runtime {
                         
                     
                 
                +    

                + To get the different response types, set one of the following query parameters in the generated domain of your function. +

                + +
                Nameversion Image ArchitecturesPlatforms
                - escape($key); ?> + escape($key); ?> + + + Self-hosted + + Cloud + + $version): ?> @@ -61,12 +69,6 @@ $sorted_runtimes['Dart']["cloud"] = true; escape(implode(' / ', $runtime['versions'][0]['supports'] ?? [])); ?> - Self-hosted - - Cloud - -
                NameversionNameVersion ImageArchitecturesArchitectures
                - escape($key); ?> + escape($key); ?> - - Self-hosted - - Cloud - - + $version): ?> + + Cloud + + + $version): ?> escape($version['image'] ?? ''); ?> From e9fb6375ccdebba92a5ba0295107ea2467e5993d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 27 Aug 2023 14:44:47 +0000 Subject: [PATCH 78/80] Improve SDK function execute code examples --- app/views/docs/functions-execute.phtml | 99 ++++++++++++++++++++------ 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/app/views/docs/functions-execute.phtml b/app/views/docs/functions-execute.phtml index 2df0337d3..a6e52cfcb 100644 --- a/app/views/docs/functions-execute.phtml +++ b/app/views/docs/functions-execute.phtml @@ -85,6 +85,7 @@ try {
                import 'package:appwrite/appwrite.dart';
                +import 'dart:convert';
                 
                 final client = Client();
                 client
                @@ -94,8 +95,10 @@ client
                 final functions = Functions(client);
                 
                 try {
                -    final response = await functions.createExecution('[FUNCTION_ID]', {
                +    final response = await functions.createExecution('[FUNCTION_ID]', json.encode({
                         'foo': 'bar'
                +    }), '/', 'GET', {
                +        'X-Custom-Header': '123'
                     });
                     print(response.data);
                 } catch (e) {
                @@ -110,6 +113,7 @@ try {
                         
                import io.appwrite.Client;
                 import io.appwrite.services.Functions;
                +import com.google.gson.Gson;
                 
                 val client = new Client();
                 client
                @@ -119,8 +123,10 @@ client
                 val functions = new Functions(client);
                 
                 try {
                -    val response = await functions.createExecution('[FUNCTION_ID]', {
                +    val response = await functions.createExecution('[FUNCTION_ID]', gson.toString({
                         'foo': 'bar'
                +    }), '/', 'GET', {
                +        'X-Custom-Header': '123'
                     });
                     print(response.data);
                 } catch (e) {
                @@ -134,6 +140,7 @@ try {
                         
                import Appwrite
                +import Foundation
                 
                 let client = Client()
                 client
                @@ -143,7 +150,15 @@ client
                 let functions = Functions(client: client)
                 
                 do {
                -    let response = try functions.createExecution(functionId: "[FUNCTION_ID]", data: ["foo": "bar"])
                +    let response = try functions.createExecution(
                +        functionId: "[FUNCTION_ID]",
                +        data: NSJSONSerialization.jsonObject(with: ["foo": "bar"], options: [])!,
                +        xpath: "/",
                +        method: "GET",
                +        headers: [
                +            "X-Custom-Header": "123"
                +        ]
                +    )
                     print(response)
                 } catch let error {
                     print(error)
                @@ -172,8 +187,10 @@ client
                 const functions = new Functions(client)
                 
                 try {
                -    const data = await functions.createExecution('[FUNCTION_ID]', {
                +    const data = await functions.createExecution('[FUNCTION_ID]', JSON.stringify({
                         'foo': 'bar'
                +    }), '/', 'GET', {
                +        'X-Custom-Header': '123'
                     })
                     console.log(data)
                 } catch (err) {
                @@ -203,7 +220,11 @@ $client
                 
                 $functions = new Functions($client);
                 
                -$result = $functions->createExecution('[FUNCTION_ID]');
                +$result = $functions->createExecution('[FUNCTION_ID]', json_encode([ + 'foo' => 'bar' +], '/', 'GET', [ + 'X-Custom-Header': '123' +]);
                @@ -213,6 +234,7 @@ $result = $functions->createExecution('[FUNCTION_ID]');
                from appwrite.client import Client
                 from appwrite.services.functions import Functions
                +import json
                 
                 client = Client()
                 
                @@ -224,11 +246,11 @@ client = Client()
                 
                 functions = Functions(client)
                 
                -result = functions.create_execution('[FUNCTION_ID]',
                -    {
                -        'foo': 'bar'
                -    }
                -)
                +result = functions.create_execution('[FUNCTION_ID]', json.dumps({ + 'foo': 'bar' +}, '/', 'GET', { + 'X-Custom-Header': '123' +})
                @@ -237,6 +259,7 @@ result = functions.create_execution('[FUNCTION_ID]',
                require 'Appwrite'
                +require 'json'
                 
                 include Appwrite
                 
                @@ -247,8 +270,10 @@ client = Client.new
                 
                 functions = Functions.new(client)
                 
                -response = functions.create_execution(function_id: '[FUNCTION_ID]', data: {
                +response = functions.create_execution(function_id: '[FUNCTION_ID]', data: JSON.generate({
                     'foo': 'bar'
                +}), '/', 'GET', {
                +    'X-Custom-Header': '123'
                 })
                 
                 puts response.inspect
                @@ -271,8 +296,10 @@ client const functions = new Functions(client) try { - const data = await functions.createExecution('[FUNCTION_ID]', { + const data = await functions.createExecution('[FUNCTION_ID]', JSON.stringify({ 'foo': 'bar' + }), '/', 'GET', { + 'X-Custom-Header': '123' }) console.log(data) } catch (err) { @@ -286,6 +313,7 @@ try {
                import 'package:dart_appwrite/dart_appwrite.dart';
                +import 'dart:convert';
                 
                 void main() {
                   Client client = Client();
                @@ -299,8 +327,13 @@ void main() {
                 
                   Future result = functions.createExecution(
                     functionId: '[FUNCTION_ID]',
                -    data: {
                +    data: json.encode({
                       'foo': 'bar'
                +    }),
                +    xpath: '/',
                +    method: 'GET',
                +    headers: {
                +        'X-Custom-Header': '123'
                     }
                   );
                 
                @@ -319,6 +352,7 @@ void main() {
                         
                import Appwrite
                +import Foundation
                 
                 let client = Client()
                     .setEndpoint("https://cloud.appwrite.io/v1")
                @@ -329,10 +363,14 @@ let functions = Functions(client)
                 
                 let execution = try await functions.createExecution(
                     functionId: "[FUNCTION_ID]",
                -    data: [
                +    data: NSJSONSerialization.jsonObject(with: [
                         "foo": "bar"
                -    ]
                -)
                + ], options: [])!), + xpath: '/', + method: 'GET', + headers: [ + "X-Custom-Header": "123" +])
                @@ -343,6 +381,7 @@ let execution = try await functions.createExecution(
                using Appwrite;
                 using Appwrite.Services;
                 using Appwrite.Models;
                +using System.Text.Json;
                 
                 var client = new Client()
                     .SetEndPoint("https://cloud.appwrite.io/v1")
                @@ -353,10 +392,14 @@ var functions = new Functions(client);
                 
                 Execution result = await functions.CreateExecution(
                     functionId: "[FUNCTION_ID]",
                -    data: new Dictionary<string, object> {
                +    data: JsonSerializer.Serialize<object>(new Dictionary<string, object> {
                         { "foo", "bar" }
                -    }
                -);
                + }), + xpath: "/", + method: "GET", + headers: new Dictionary<string, object> { + { "X-Custom-Header", "123" } +});
                @@ -366,6 +409,7 @@ Execution result = await functions.CreateExecution(
                import io.appwrite.Client
                 import io.appwrite.services.Functions
                +import com.google.gson.Gson
                 
                 fun main(args: Array<String>) {
                     val client = Client(context)
                @@ -377,8 +421,13 @@ fun main(args: Array<String>) {
                 
                     val response = functions.createExecution(
                         functionId = "[FUNCTION_ID]",
                -        data = mapOf(
                +        data = gson.toString(mapOf(
                             "foo" to "bar"
                +        )),
                +        xpath = "/",
                +        method = "GET",
                +        headers = mapOf(
                +            "X-Custom-Header" to "123"
                         )
                     )
                 }
                @@ -395,6 +444,7 @@ fun main(args: Array<String>) {
                             
                import io.appwrite.Client;
                 import io.appwrite.services.Functions;
                 import java.util.HashMap;
                +import com.google.gson.Gson;
                 
                 public static void main(String[] args) throws Exception {
                     Client client = new Client()
                @@ -414,9 +464,14 @@ public static void main(String[] args) throws Exception {
                 
                             System.out.println(result);
                         }),
                -        new HashMap() {{
                +        gson.toString(new HashMap() {{
                             put("foo", "bar");
                -        }}
                +        }}),
                +        "/",
                +        "GET",
                +        new HashMap() {{
                +            put("X-Custom-Header", "123");
                +        }},
                     );
                 }
                From e22eee160e3d5a9559f23ffebe0c20bc81ff50c3 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 27 Aug 2023 15:10:46 +0000 Subject: [PATCH 79/80] Address Eldad's comments --- app/views/docs/functions-deploy.phtml | 2 +- app/views/docs/functions-develop.phtml | 153 +++++++++++++++++++----- app/views/docs/functions-examples.phtml | 9 +- 3 files changed, 128 insertions(+), 36 deletions(-) diff --git a/app/views/docs/functions-deploy.phtml b/app/views/docs/functions-deploy.phtml index 62ee5219f..934166ed4 100644 --- a/app/views/docs/functions-deploy.phtml +++ b/app/views/docs/functions-deploy.phtml @@ -69,7 +69,7 @@

                CLI

                -
                +

                CLI Setup

                Before you can deploy with the Appwrite CLI, make sure you've installed and initialized the CLI.

                diff --git a/app/views/docs/functions-develop.phtml b/app/views/docs/functions-develop.phtml index 369b8148d..b2b7bad38 100644 --- a/app/views/docs/functions-develop.phtml +++ b/app/views/docs/functions-develop.phtml @@ -3,9 +3,9 @@ Each function is handled following a request and response pattern.

                -

                Function Flow

                +

                Lifecycle

                - There is a clear flow for all Appwrite Functions, from beginning to end. + There is a clear lifecycle for all Appwrite Functions, from beginning to end. Here's everything that happens during a function execution.

                @@ -460,19 +460,19 @@ public class Main {
                reqContains request information like method, body, and headers. See full examples here.Contains request information like method, body, and headers. See full examples here.
                resContains methods to build a response and return information. See full examples here.Contains methods to build a response and return information. See full examples here.
                log()Method to log information to the Appwrite Console, end users will not be able to see these logs. See full examples here.Method to log information to the Appwrite Console, end users will not be able to see these logs. See full examples here.
                error()Methoc to log errors to the Appwrite Console, end users will not be able to see these errors. See full examples here.Methoc to log errors to the Appwrite Console, end users will not be able to see these errors. See full examples here.
                + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                TypeQuery ParamExample
                text/?type=texthttps://64d4d22db370ae41a32e.appwrite.global/?type=text
                json/?type=jsonhttps://64d4d22db370ae41a32e.appwrite.global/?type=json
                redirect/?type=redirecthttps://64d4d22db370ae41a32e.appwrite.global/?type=redirect
                html/?type=htmlhttps://64d4d22db370ae41a32e.appwrite.global/?type=html
                empty/https://64d4d22db370ae41a32e.appwrite.global/
                +

                Logging

                To protect user privacy, the request and response objects are not logged to the Appwrite Console by default. @@ -1348,10 +1401,10 @@ namespace runtime { -

                Function Environment Variables

                +

                Function-level Environment Variables

                - Function environment variables will only be accessible in the function they belong to. - Function environment variables will override global environment variables when they have conflicting names. + Function-level environment variables will only be accessible in the function they belong to. + Function-level environment variables will override project-level variables when they have conflicting names.

                1. In Appwrite Console, navigate to Functions.
                2. @@ -1360,13 +1413,13 @@ namespace runtime {
                3. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
                -

                Global Environment Variables

                +

                Project-level Variables

                - Global environment variables are accessible to all Appwrite Functions in your project. - Local environment variables will override global environment variables when they have conflicting names. + Project-level variables are accessible to all Appwrite Functions in your project. + Function-level environment variables will override project-level variables when they have conflicting names.

                  -
                1. In Appwrite Console, navigate to your project's Settings page.
                2. +
                3. In the Appwrite Console, navigate to your project's Settings page.
                4. Navigate to Global variables section.
                5. Create an environment variable by clicking Create variable, using the Editor, or import new variables through a .env file.
                @@ -1506,79 +1559,119 @@ namespace runtime { Your function's dependencies should be managed by the package manager of each language. By default, we include the following package managers in each runtime:

                -

                - To install your dependencies before your function is built, you should add the relevant install command to the top your functions Build Commands script. -

                - - - + + + + + + + + + + + + + +
                LanguagePackage ManagerLanguagePackage Manager Commands
                + Node icon + Node.js npm npm install
                + PHP icon + PHP Composer composer install
                + Python icon + Python pip pip install -r requirements.txt
                + Ruby icon + Ruby Bundler bundle install
                + Deno icon + Deno deno deno cache <ENTRYPOINT_FILE>
                + Dart icon + Dart pub pub get
                + Swift icon + Swift Swift Package Manager N/A
                + Swift icon + .NET NuGet N/A
                + Swift icon + Kotlin Gradle N/A
                + Swift icon + Java Gradle N/A
                + C++ icon + C++ None N/A
                +

                + To install your dependencies before your function is built, you should add the relevant install command to the top your function's Build setting > Commands. +

                +

                Using Appwrite in a Function

                -

                Appwrite can be used in your functions by adding the relevant SDK to your function's dependencies

                -

                Authenticating with Appwrite is done via an API key or a JWT token. You can read more about authentication in the Server Authentication section of the docs.

                +

                + Appwrite can be used in your functions by adding the relevant SDK to your function's dependencies. + Authenticating with Appwrite is done via an API key or a JWT token. + API keys must be generated and exported as an environment variable. +

                +

                + You can read more about authentication in the Server Authentication section of the docs. +

                Using with API Key

                diff --git a/app/views/docs/functions-examples.phtml b/app/views/docs/functions-examples.phtml index d30c0b827..485c4fe3b 100644 --- a/app/views/docs/functions-examples.phtml +++ b/app/views/docs/functions-examples.phtml @@ -4,7 +4,7 @@ Take a look at the following.

                -

                Currency Conversion API

                +

                Currency Conversion API

                Here's a currency conversion API that converts from Euros and Indian Rupees to US Dollars. We'll use an external API to get the latest exchange rates, and query it using an dependency specific to each runtime. @@ -233,7 +233,7 @@ end

                -

                Voting System Using Appwrite

                +

                Voting System Using Appwrite

                Here's a simple voting system that allows users to vote on various topics. Appwrite Functions and the server SDK are used to enforce voting rules and prevent multiple votes from the same user for a single topic.

                @@ -611,10 +611,9 @@ def main(context): For example, [YOUR_FUNCTION_URL]/?userId=[USER_ID]&topicId=[TOPIC_ID]&vote=yes to cast a vote.

                -

                HTML Contact Form

                - +

                HTML Contact Form

                - Here's a simple form page that can be used to store a user's message in a collection. + Here's a simple form page that handles form submissions, and can be used to store a user's message in a collection. The form is submitted to the function using the POST method and the form data is sent as a URL-encoded string in the request body.

                From 8d9be11cf97dcd2b79720d06c8083ac98bd802ac Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Sun, 27 Aug 2023 15:12:05 +0000 Subject: [PATCH 80/80] use enabled instead of supported in migrations --- app/views/docs/migrations.phtml | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/views/docs/migrations.phtml b/app/views/docs/migrations.phtml index e130ae472..7d4308bac 100644 --- a/app/views/docs/migrations.phtml +++ b/app/views/docs/migrations.phtml @@ -8,7 +8,7 @@

                Supported sources

                You can transfer from these sources to an Appwrite project. - Resources marked supported are migrated automatically. + Resources marked enabled are migrated automatically. Resources marked partial can be migrated, but with limitations or caveats, check the guide for each source to learn more. Resources marked manual require manual migration.

                @@ -29,51 +29,51 @@

                Firebase

                - supported - supported + enabled + enabled partial - supported + enabled manual

                Supabase

                - supported - supported + enabled + enabled partial - supported + enabled manual

                NHost

                - supported - supported + enabled + enabled partial - supported + enabled manual

                Cloud to Self-hosted

                - supported - supported - supported - supported - supported + enabled + enabled + enabled + enabled + enabled

                Self-hosted to Cloud

                - supported - supported - supported - supported - supported + enabled + enabled + enabled + enabled + enabled