0

I have appwrite dart function dart-2.17 hosted locally, when function is created using appwrite init function and immediately deploy & execute with out any changes appwrite deploy function it all works fine. But now when i do the following code it always return an execution timeout error, (does not matter if i add try/catch block)

@.env (from appwrite function settings console)

APPWRITE_FUNCTION_ENDPOINT=http://localhost/v1
APPWRITE_FUNCTION_API_KEY=%MyKey%

@functions/Create Company/lib/main.dart

import 'dart:convert';

import 'package:dart_appwrite/dart_appwrite.dart';

/*
  'req' variable has:
    'headers' - object with request headers
    'payload' - request body data as a string
    'variables' - object with function variables

  'res' variable has:
    'send(text, status: status)' - function to return text response. Status code defaults to 200
    'json(obj, status: status)' - function to return JSON response. Status code defaults to 200
  
  If an error is thrown, a response with code 500 will be returned.
*/

Future<void> start(final req, final res) async {
  final client = Client();

  final database = Databases(client);
  final teams = Teams(client);

  if (req.variables['APPWRITE_FUNCTION_ENDPOINT'] == null || req.variables['APPWRITE_FUNCTION_API_KEY'] == null) {
    res.send('Endpoint is not yet Configred', 500);
    return;
  }
  client
      .setEndpoint(req.variables['APPWRITE_FUNCTION_ENDPOINT'])
      .setProject(req.variables['APPWRITE_FUNCTION_PROJECT_ID'])
      .setKey(req.variables['APPWRITE_FUNCTION_API_KEY'])
      .setSelfSigned(status: true);

  print(req.payload);
  final data = jsonDecode(req.payload);
  print(data);
  print('checking data');
  final applicationModalAccess = data['applicationModalAccess'];
  if (applicationModalAccess is! String) {
    res.send('field "applicationModalAccess" is suppose to be string 0-32', 400);
    return;
  }
  String redirectUrl;
  try {
    final appModal = await database.getDocument(databaseId: 'STORE', collectionId: 'APPLICATION-MODALS', documentId: applicationModalAccess);
    redirectUrl = appModal.data['redirectUrl'];
  } catch (err) {
    res.send('field "applicationModalAccess" as not a valid id', 404);
    return;
  }
  final companyName = data['companyName'];
  if (companyName is! String || !RegExp(r'^[a-zA-Z0-9.\-_\(\)]{5,50}$').hasMatch(companyName)) {
    res.send('field "companyName" is suppose to be string of length 5-50', 400);
    return;
  }
  final ownerEmail = data['ownerEmail'];
  if (ownerEmail is! String || !RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(ownerEmail)) {
    res.send('field "ownerEmail" is suppose to be email formated string', 400);
    return;
  }
  final ownerName = data['ownerName'];
  if (ownerName is! String || !RegExp('^[A-Za-z0-9]{5,20}\$').hasMatch(ownerName)) {
    res.send('field "ownerName" is suppose to be string of length 5-20', 400);
    return;
  }

  print('creating team');
  final team = await teams.create(teamId: ID.unique(), name: companyName, roles: []);
  final companyID = team.$id;

  print('creating database');
  await database.create(databaseId: companyID, name: '$companyName(STANDARD)');

  print('setting up products collection');
  final productPermission = [
    Permission.read(Role.team(companyID)),
    Permission.create(Role.team(companyID, 'OWNER')),
    Permission.update(Role.team(companyID, 'OWNER')),
    Permission.create(Role.team(companyID, 'PRODUCT-CREATE')),
    Permission.update(Role.team(companyID, 'PRODUCT-UPDATE'))
  ];
  await database.createCollection(databaseId: companyID, collectionId: 'PRODUCTS', name: 'Products', permissions: productPermission, documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'name', size: 25, xrequired: true);
  await database.createUrlAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'imgUrl', xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'measurmentUnit', size: 10, xrequired: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'selectionCode', size: 5, xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'selectionType', size: 15, xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'selection1DBarCode', size: 30, xrequired: false);
  await database.createIntegerAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'sgst', xrequired: false, xdefault: 0, min: 0, max: 10000);
  await database.createIntegerAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'cgst', xrequired: false, xdefault: 0, min: 0, max: 10000);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCTS', key: 'hsn', size: 10, xrequired: false);
  await database.createIndex(databaseId: companyID, collectionId: 'PRODUCTS', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);

  await database.createCollection(databaseId: companyID, collectionId: 'DELETED-PRODUCTS', name: 'Deleted Products', permissions: [Permission.read(Role.team(companyID))], documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'DELETED-PRODUCTS', key: 'deletedBy', size: 15, xrequired: true);
  await database.createIndex(databaseId: companyID, collectionId: 'DELETED-PRODUCTS', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);

  print('setting up branches collection');
  final branchPermission = [
    Permission.read(Role.team(companyID)),
    Permission.create(Role.team(companyID, 'OWNER')),
    Permission.update(Role.team(companyID, 'OWNER')),
    Permission.create(Role.team(companyID, 'BRANCH-CREATE')),
    Permission.update(Role.team(companyID, 'BRANCH-UPDATE'))
  ];
  await database.createCollection(databaseId: companyID, collectionId: 'BRANCHES', name: 'Branches', permissions: branchPermission, documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'BRANCHES', key: 'name', size: 25, xrequired: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'BRANCHES', key: 'type', size: 10, xrequired: false);

  await database.createCollection(databaseId: companyID, collectionId: 'DELETED-BRANCHES', name: 'Deleted Branches', permissions: [Permission.read(Role.team(companyID))], documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'DELETED-BRANCHES', key: 'deletedBy', size: 15, xrequired: true);
  await database.createIndex(databaseId: companyID, collectionId: 'DELETED-BRANCHES', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);

  print('setting up inventory');
  final inventoryPermissions = [Permission.read(Role.team(companyID, 'OWNER')), Permission.read(Role.team(companyID, 'READ-ALL-INVENTORY'))];
  await database.createStringAttribute(databaseId: companyID, collectionId: 'INVENTORY', key: 'productID', size: 32, xrequired: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'INVENTORY', key: 'branchID', size: 32, xrequired: true);
  await database.createCollection(databaseId: companyID, collectionId: 'INVENTORY', name: 'Inventory', permissions: inventoryPermissions, documentSecurity: true);
  await database.createIntegerAttribute(databaseId: companyID, collectionId: 'INVENTORY', key: 'quantity', xrequired: true);
  await database.createIndex(databaseId: companyID, collectionId: 'INVENTORY', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);
  await database.createIndex(databaseId: companyID, collectionId: 'INVENTORY', key: 'uniqueID', type: 'unique', attributes: ['productID', 'branchID']);

  print('setting up other companies');
  final otherCompaniesPermissions = [
    Permission.read(Role.team(companyID)),
    Permission.create(Role.team(companyID, 'OWNER')),
    Permission.update(Role.team(companyID, 'OWNER')),
    Permission.create(Role.team(companyID, 'OTHER-COMPANY-MANAGER')),
    Permission.update(Role.team(companyID, 'OTHER-COMPANY-MANAGER')),
  ];
  await database.createCollection(databaseId: companyID, collectionId: 'OTHER-COMPANIES', name: 'Other Companies', permissions: otherCompaniesPermissions, documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'name', size: 15, xrequired: true);
  await database.createUrlAttribute(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'imgUrl', xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'phone', size: 15, xrequired: false);
  await database.createEmailAttribute(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'email', xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'branchIDs', size: 15, xrequired: true, array: true);
  await database.createIndex(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);
  await database.createIndex(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'email', type: 'fulltext', attributes: ['email']);
  await database.createIndex(databaseId: companyID, collectionId: 'OTHER-COMPANIES', key: 'phone', type: 'fulltext', attributes: ['phone']);

  await database.createCollection(databaseId: companyID, collectionId: 'DELETED-COMPANIES', name: 'Deleted Branches', permissions: [Permission.read(Role.team(companyID))], documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'DELETED-COMPANIES', key: 'deletedBy', size: 15, xrequired: true);
  await database.createIndex(databaseId: companyID, collectionId: 'DELETED-COMPANIES', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);

  print('setting up product rates');
  final productRatesPermissions = [
    Permission.read(Role.team(companyID, 'OWNER')),
    Permission.write(Role.team(companyID, 'OWNER')),
    Permission.read(Role.team(companyID, 'OTHER-PRODUCT-MANAGER')),
    Permission.create(Role.team(companyID, 'OTHER-PRODUCT-MANAGER')),
    Permission.update(Role.team(companyID, 'OTHER-PRODUCT-MANAGER')),
    Permission.read(Role.team(companyID, 'READ-ALL-PRODUCT-RATES'))
  ];
  await database.createCollection(databaseId: companyID, collectionId: 'PRODUCT-RATES', name: 'Company Rates', permissions: productRatesPermissions, documentSecurity: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'productID', size: 32, xrequired: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'branchID', size: 32, xrequired: false, xdefault: '*');
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'companyID', size: 32, xrequired: false, xdefault: '*');
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'type', size: 10, xrequired: false, xdefault: '*');
  await database.createIntegerAttribute(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'aboveCondition', xrequired: false, min: 0, xdefault: 0);
  await database.createIntegerAttribute(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'rate', xrequired: true);
  await database.createIndex(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);
  await database.createIndex(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'aboveCondition', type: 'key', attributes: ['aboveCondition']);
  await database.createIndex(databaseId: companyID, collectionId: 'PRODUCT-RATES', key: 'uniqueID', type: 'unique', attributes: ['productID', 'branchID', 'companyID', 'type']);

  print('setting up due money');
  final dueMoneyPermissions = [Permission.read(Role.team(companyID, 'OWNER')), Permission.read(Role.team(companyID, 'READ-ALL-DUE-MONEY'))];
  await database.createCollection(databaseId: companyID, collectionId: 'DUE-MONEY', name: 'Due Money', permissions: dueMoneyPermissions, documentSecurity: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'DUE-MONEY', key: 'branchID', size: 32, xrequired: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'DUE-MONEY', key: 'companyID', size: 32, xrequired: true);
  await database.createIntegerAttribute(databaseId: companyID, collectionId: 'DUE-MONEY', key: 'dueMoney', xrequired: true);
  await database.createIndex(databaseId: companyID, collectionId: 'DUE-MONEY', key: '\$updatedAt', type: 'key', attributes: ['\$updatedAt']);
  await database.createIndex(databaseId: companyID, collectionId: 'DUE-MONEY', key: 'uniqueID', type: 'unique', attributes: ['branchID', 'companyID']);

  print('setting up customers');
  await database.createCollection(databaseId: companyID, collectionId: 'CUSTOMER', name: 'Customer', permissions: [Permission.read(Role.team(companyID))], documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'CUSTOMER', key: 'name', size: 15, xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'CUSTOMER', key: 'phone', size: 15, xrequired: false);
  await database.createEmailAttribute(databaseId: companyID, collectionId: 'CUSTOMER', key: 'email', xrequired: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'CUSTOMER', key: 'address', size: 20, xrequired: false, array: true);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'CUSTOMER', key: 'branchIDs', size: 15, xrequired: true, array: true);
  await database.createIndex(databaseId: companyID, collectionId: 'CUSTOMER', key: 'email', type: 'fulltext', attributes: ['email']);
  await database.createIndex(databaseId: companyID, collectionId: 'CUSTOMER', key: 'phone', type: 'fulltext', attributes: ['phone']);

  print('setting up company prefrences');
  await database.createCollection(databaseId: companyID, collectionId: 'PREFRENCES', name: 'Prefrences', documentSecurity: false);
  await database.createStringAttribute(databaseId: companyID, collectionId: 'PREFRENCES', key: 'value', size: 100, xrequired: true);

  print('creating company document in global database');
  final now = DateTime.now().toIso8601String();
  final companyStoreDoc = {"type": 'STANDARD', "name": companyName, "inventoryUpdatedAt": now, "dewMoneyUpdateAt": now, "customersUpdatedAt": now, "applicationModalAccess": applicationModalAccess};
  await database.createDocument(databaseId: 'STORE', collectionId: 'COMPANIES', documentId: companyID, data: companyStoreDoc, permissions: [Permission.read(Role.team(companyID))]);

  print('setting up company owner');
  await teams.createMembership(teamId: companyID, email: ownerEmail, roles: ['OWNER'], url: redirectUrl);

  res.send('Company was successfully created under "$companyID" ID', 201);
}

@functions/Create Company/lib/pubspec.yaml

name: appwrite_function
description: Dart starter for Appwrite function.
version: 1.0.0
publish_to: 'none'

environment:
  sdk: '>=2.17.0 <3.0.0'

dependencies:
  convert: ^3.1.0
  dart_appwrite: ^7.1.0

dev_dependencies:
  lints: ^2.0.1

@appwrite.json

{
    "projectId": "63c4397e307e9341d13e",
    "projectName": "Local Market",
    "functions": [
        {
            "$id": "63de1a41cb003b045a4d",
            "name": "Create Company",
            "runtime": "dart-2.17",
            "path": "functions/Create Company",
            "entrypoint": "lib/main.dart",
            "ignore": [
                ".packages",
                ".dart_tool"
            ],
            "execute": [],
            "events": [],
            "schedule": "",
            "timeout": 60
        }
    ]
}

@http://localhost/console/project-63c4397e307e9341d13e/functions/function-63de1a41cb003b045a4d/executions @ execution, Response & Logs both tabs are empty @Errors tab

An internal curl error has occurred within the executor! Error Msg: Operation timed out

@docker logs appwrite-executor @last few lines are (including deploy & execution)

...other logs

Building container : 63c4397e307e9341d13e-63de2755b59b086677d6
Build Stage completed in 1 seconds
Executing Runtime: 63c4397e307e9341d13e-63de2755b59b086677d6
[Error] Type: Exception
[Error] Message: An internal curl error has occurred within the executor! Error Msg: Operation timed out
[Error] File: /usr/src/code/app/executor.php
[Error] Line: 544

How do i resolve this???

Panth
  • 35
  • 6

2 Answers2

1

This error typically happens because an exception is thrown in your Function. I highly recommend wrapping your code in a try/catch and calling res.json({'error': error.toString()}) in the catch to return the error to the Appwrite Console.

In addition, it looks like you're using http://localhost/v1 in your Function. This won't work because your Function will try to connect to itself rather than Appwrite. You can try using your LAN IP instead.

Reference: Functions Docs

Steven Nguyen
  • 452
  • 4
  • 4
0

If you are using Appwrite locally on your computer, you may have problems connecting to the API. In my case, replacing the localhost address in the endpoint with the IP address of my computer helped.

Do not use "http://localhost/v1" or "http://127.0.0.1/v1" when calling Appwrite API FUNCTIONS if the server is running locally. Instead, specify the actual IP address of your machine in the endpoint.

I spent two days figuring out this problem. I hope my experience will help someone save time and avoid headaches due to the wrong endpoint.

MACTEPyc
  • 1
  • 1