$ cat node-template.py

Drive Folder Reader

// Reads all documents from a selected Drive folder recursively.

Input
Storage
template.py
1import os2import sys3import json4import traceback5import requests67# Environment variables automatically set by workspace executor8EMBLEMA_API_BASE_URL = os.getenv("EMBLEMA_API_BASE_URL")9USER_TOKEN = os.getenv("USER_TOKEN")1011if not EMBLEMA_API_BASE_URL:12    raise ValueError("EMBLEMA_API_BASE_URL environment variable not set")131415def api_request(method, path, **kwargs):16    """Make authenticated API request.1718    Args:19        method: HTTP method (GET, POST, PUT, DELETE, etc.)20        path: API path (e.g., "/api/v2/drive/items")21        **kwargs: Additional arguments for requests.request()22    """23    if not USER_TOKEN:24        raise ValueError("USER_TOKEN environment variable not set")2526    headers = {27        "Authorization": f"Bearer {USER_TOKEN}",28        "Content-Type": "application/json",29    }30    url = f"{EMBLEMA_API_BASE_URL}{path}"31    response = requests.request(method, url, headers=headers, **kwargs)32    response.raise_for_status()33    return response.json()343536def main():37    """Main execution function"""38    try:39        # Read execution input from stdin40        input_json = sys.stdin.read()41        execution_input = json.loads(input_json)4243        # Extract folder ID from inputs44        inputs = execution_input.get("inputs", {})45        folder_id = inputs.get("driveItemId")4647        if not folder_id:48            raise ValueError("driveItemId input is required")4950        # Call REST API to get folder contents RECURSIVELY51        # API automatically applies user permissions via PAT52        # Use query syntax to filter:53        # - path descendantOf 'folder-id' - RECURSIVE (includes all nested subfolders)54        # - refId != null - only files/documents, NOT folders55        result = api_request("GET", "/api/v2/drive/items", params={56            "q": f"path descendantOf '{folder_id}' and refId!=null",57            "pageSize": 1000,58        })5960        # Extract DriveItem IDs from items61        # IMPORTANT: We return DriveItem IDs, NOT Document IDs (refId)!62        # This ensures proper permission validation via DriveItem path63        # DriveItem wraps the Document and carries permission information64        drive_item_ids = []65        for item in result.get("items", []):66            # All items returned from query should be documents (refId!=null filter)67            # We return the DriveItem ID so downstream nodes can validate permissions68            if item.get("id"):69                drive_item_ids.append(item["id"])7071        # Output result (matches OUTPUT_SCHEMA)72        output = {73            "driveItemIds": drive_item_ids,74        }75        print(json.dumps(output, indent=2))7677    except requests.HTTPError as e:78        # HTTP error (401, 403, 404, etc.)79        error_output = {80            "error": f"API request failed: {e}",81            "errorType": "HTTPError",82            "status": e.response.status_code,83            "detail": e.response.text,84        }85        print(json.dumps(error_output), file=sys.stderr)86        sys.exit(1)87    except Exception as e:88        # Other errors89        error_output = {90            "error": str(e),91            "errorType": type(e).__name__,92            "traceback": traceback.format_exc(),93        }94        print(json.dumps(error_output), file=sys.stderr)95        sys.exit(1)969798if __name__ == "__main__":99    main()