Project Guidelines
This document is describing the connectors guidelines for the project.
EnvironmentAdapter
Section titled âEnvironmentAdapterâOverview
Section titled âOverviewâThe EnvironmentAdapter
class provides a unified interface for environment-specific operations across Google Apps Script and Node.js environments. It abstracts away platform differences and allows your code to work consistently in both environments.
Motivation
Section titled âMotivationâ- Use singe connectors source code in both environments: Node.js and Apps Script
Key Features
Section titled âKey Featuresâ- Environment detection: Automatically detects the runtime environment
- Environment-specific code: Use environment-specific code for each environment
- Unified HTTP requests: Consistent API for making HTTP requests
- Response wrapping: Unified response interface across platforms
- Utility functions: Date formatting, UUID generation, encoding, and more
Table of Contents
Section titled âTable of Contentsâ- Key Features
- Getting Started
- Essential EnvironmentAdapter Rules for Data Analysts
- API Reference
- Usage Examples
- HTTP Requests
- Utility Methods
- Error Handling
- Troubleshooting
Getting Started
Section titled âGetting StartedâImport and Setup
Section titled âImport and Setupâ// The EnvironmentAdapter is available as a global class// No explicit import needed in Google Apps Script
// In Node.js, ensure it's available through your bundle system
Dependencies
Section titled âDependenciesâMake sure the following constants and exceptions are available:
// Required constants from CommonConstants.jsvar ENVIRONMENT = { UNKNOWN: 1, APPS_SCRIPT: 2, NODE: 3,};
// Required exception class from Exceptions.jsclass UnsupportedEnvironmentException extends AbstractException {}
Essential EnvironmentAdapter Rules for Data Analysts
Section titled âEssential EnvironmentAdapter Rules for Data AnalystsâWhat You Must Know
Section titled âWhat You Must KnowâEnvironmentAdapter is a required class that makes your connector work in both Google Apps Script and Node.js environments. You MUST use it for all HTTP requests and environment-specific operations.
What NOT to Do
Section titled âWhat NOT to Doââ Never use fetch()
directly
â Never use UrlFetchApp
directly
â Never use Utilities
directly
â Never skip error checking on API responses
â Never make requests without proper headers
â Never ignore rate limits
Must Remember
Section titled âMust Rememberâ- Always use EnvironmentAdapter.fetch() for any HTTP request
- Always check response.getResponseCode() before processing data
- Always handle errors with try-catch blocks
- Always respect API rate limits with sleep() between requests
- Always include proper headers especially for authentication
- Always use EnvironmentAdapter utilities for date formatting, UUID generation, encoding, and more
Core Rules
Section titled âCore Rulesâ1. Always Use EnvironmentAdapter for HTTP Requests
Section titled â1. Always Use EnvironmentAdapter for HTTP Requestsâ// â
CORRECT - Use this for all API callsconst response = EnvironmentAdapter.fetch("https://api.example.com/data");
// â WRONG - Never use direct fetch()const response = fetch("https://api.example.com/data");
// â WRONG - Never use direct UrlFetchApp.fetch()const response = UrlFetchApp.fetch("https://api.example.com/data");
2. Standard Response Handling Pattern
Section titled â2. Standard Response Handling Patternâconst response = EnvironmentAdapter.fetch(url, options);
// Check if request succeededif (response.getResponseCode() === 200) { const data = response.getAsJson(); // For JSON APIs // Process your data here} else { // Handle error console.error("API request failed:", response.getResponseCode());}
3. Required Request Format for APIs
Section titled â3. Required Request Format for APIsâconst options = { method: "GET", // or "POST", "PUT", etc. headers: { "Authorization": "Bearer your-token", "Content-Type": "application/json" }};
// For POST requests with dataconst postOptions = { method: "POST", headers: { "Content-Type": "application/json" }, payload: JSON.stringify({ // your data here })};
4. Utility Functions
Section titled â4. Utility Functionsâ// Generate unique IDsconst id = EnvironmentAdapter.getUuid();
// Wait between API calls (to avoid rate limits)EnvironmentAdapter.sleep(1000); // Wait 1 second
// Encode dataconst encoded = EnvironmentAdapter.base64Encode("your-data");
API Reference
Section titled âAPI ReferenceâStatic Methods
Section titled âStatic MethodsâgetEnvironment()
Section titled âgetEnvironment()âReturns the current runtime environment.
static getEnvironment(): ENVIRONMENT
Returns: One of ENVIRONMENT.APPS_SCRIPT
, ENVIRONMENT.NODE
, or ENVIRONMENT.UNKNOWN
fetch(url, options)
Section titled âfetch(url, options)âMakes HTTP requests with unified response interface.
static fetch(url: string, options: Object = {}): FetchResponse
Parameters:
url
(string): The URL to fetch data fromoptions
(Object): Request options (method, headers, payload, etc.)
Returns: FetchResponse
object with unified methods
Throws: UnsupportedEnvironmentException
if environment is not supported
sleep(ms)
Section titled âsleep(ms)âPauses execution for specified milliseconds.
static sleep(ms: number): void
Parameters:
ms
(number): Milliseconds to sleep
Throws: UnsupportedEnvironmentException
if environment is not supported
formatDate(date, timezone, format)
Section titled âformatDate(date, timezone, format)âFormats a date according to environment capabilities.
static formatDate(date: Date, timezone: string, format: string): string
Parameters:
date
(Date): Date to formattimezone
(string): Timezone for formattingformat
(string): Format pattern
Returns: Formatted date string
Note: In Node.js, only returns ISO date format regardless of parameters
getUuid()
Section titled âgetUuid()âGenerates a UUID string.
static getUuid(): string
Returns: UUID in format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
base64Encode(data)
Section titled âbase64Encode(data)âEncodes data to base64.
static base64Encode(data: string): string
Parameters:
data
(string): Data to encode
Returns: Base64 encoded string
computeHmacSignature(algorithm, data, key)
Section titled âcomputeHmacSignature(algorithm, data, key)âComputes HMAC signature.
static computeHmacSignature(algorithm: string, data: string, key: string): string
Parameters:
algorithm
(string): HMAC algorithm (e.g., âsha256â, âsha1â)data
(string): Data to signkey
(string): Secret key
Returns: HMAC signature as hex string
parseCsv(csvString)
Section titled âparseCsv(csvString)âParses CSV string into array of arrays.
static parseCsv(csvString: string): Array<Array<string>>
Parameters:
csvString
(string): The CSV string to parse
Returns: Array of arrays containing parsed CSV data
Note: Each inner array represents a row, with cells as string values
unzip(data)
Section titled âunzip(data)âUnzips blob/buffer data.
static unzip(data: Blob|Buffer): Array<{getDataAsString: Function}>
Parameters:
data
(Blob|Buffer): The data to unzip
Returns: Array of file-like objects with getDataAsString
method
Note: In Node.js environment requires adm-zip
package
Usage Examples
Section titled âUsage ExamplesâMaking HTTP Requests
Section titled âMaking HTTP RequestsâGET Request
Section titled âGET Requestâtry { const response = EnvironmentAdapter.fetch("https://api.example.com/data");
// Check response status if (response.getResponseCode() === 200) { // Parse JSON response const data = response.getAsJson(); console.log("Data received:", data);
// Or get raw text const text = response.getContentText(); console.log("Response text:", text); } else { console.error("Request failed with status:", response.getResponseCode()); }} catch (error) { console.error("Error making request:", error.message);}
POST Request with JSON
Section titled âPOST Request with JSONâconst requestOptions = { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer your-token-here" }, payload: JSON.stringify({ name: "John Doe", email: "john@example.com" })};
try { const response = EnvironmentAdapter.fetch("https://api.example.com/users", requestOptions); const result = response.getAsJson(); console.log("User created:", result);} catch (error) { console.error("Error creating user:", error.message);}
GET Request with Headers
Section titled âGET Request with Headersâconst options = { method: "GET", headers: { "User-Agent": "MyApp/1.0", "Accept": "application/json" }};
const response = EnvironmentAdapter.fetch("https://api.example.com/profile", options);const headers = response.getHeaders();console.log("Response headers:", headers);
Utility Functions
Section titled âUtility FunctionsâWorking with Dates
Section titled âWorking with Datesâconst now = new Date();
// Format date (behavior differs by environment)const formatted = EnvironmentAdapter.formatDate(now, "America/New_York", "yyyy-MM-dd");console.log("Formatted date:", formatted);
Generating UUIDs
Section titled âGenerating UUIDsâconst uniqueId = EnvironmentAdapter.getUuid();console.log("Generated UUID:", uniqueId);// Output: e.g., "f47ac10b-58cc-4372-a567-0e02b2c3d479"
Base64 Encoding
Section titled âBase64 Encodingâconst originalData = "Hello, World!";const encoded = EnvironmentAdapter.base64Encode(originalData);console.log("Encoded:", encoded);// Output: "SGVsbG8sIFdvcmxkIQ=="
HMAC Signatures
Section titled âHMAC Signaturesâconst message = "important data";const secretKey = "my-secret-key";const signature = EnvironmentAdapter.computeHmacSignature("sha256", message, secretKey);console.log("HMAC signature:", signature);
Sleep/Delay
Section titled âSleep/Delayâconsole.log("Starting operation...");EnvironmentAdapter.sleep(2000); // Wait 2 secondsconsole.log("Operation completed after delay");
Parse CSV
Section titled âParse CSVâconst csvString = "name,age,city\nJohn,30,New York\nJane,25,London";const parsedData = EnvironmentAdapter.parseCsv(csvString);console.log("Parsed CSV:", parsedData);// Output: [// ["name", "age", "city"],// ["John", "30", "New York"],// ["Jane", "25", "London"]// ]
Unzip Data
Section titled âUnzip Dataâ// Assuming you have zip data from an API response or fileconst response = EnvironmentAdapter.fetch("https://example.com/data.zip");const zipData = response.getContent();const unzippedFiles = EnvironmentAdapter.unzip(zipData);
// Read contents of each fileunzippedFiles.forEach(file => { const content = file.getDataAsString(); console.log("File content:", content);});
HTTP Requests
Section titled âHTTP RequestsâFetchResponse Interface
Section titled âFetchResponse InterfaceâAll HTTP requests return a FetchResponse
object with these methods:
interface FetchResponse { getHeaders(): Object; // Get response headers getAsJson(): any; // Parse response as JSON getContent(): string|Buffer; // Get raw content getContentText(): string; // Get content as text getResponseCode(): number; // Get HTTP status code}
Request Options
Section titled âRequest OptionsâThe options
parameter for fetch()
supports:
const options = { method: "GET|POST|PUT|DELETE|PATCH", headers: { "Header-Name": "Header-Value" }, payload: "request body data", // Google Apps Script specific options muteHttpExceptions: true, followRedirects: true, validateHttpsCertificates: true};
Error Handling for HTTP Requests
Section titled âError Handling for HTTP Requestsâfunction safeApiCall(url, options) { try { const response = EnvironmentAdapter.fetch(url, options);
const statusCode = response.getResponseCode(); if (statusCode >= 400) { const errorText = response.getContentText(); throw new Error(`HTTP ${statusCode}: ${errorText}`); }
return response.getAsJson(); } catch (error) { if (error.message.includes("Invalid JSON")) { console.error("Response is not valid JSON"); return null; } throw error; }}
Utility Methods
Section titled âUtility MethodsâDate Formatting Limitations
Section titled âDate Formatting Limitationsââ ď¸ Important: Date formatting behavior differs between environments:
- Google Apps Script: Full formatting support with timezone and format patterns
- Node.js: Always returns ISO date format (YYYY-MM-DD) regardless of parameters
// Cross-platform date handlingfunction getFormattedDate(date) { if (EnvironmentAdapter.getEnvironment() === ENVIRONMENT.APPS_SCRIPT) { return EnvironmentAdapter.formatDate(date, "UTC", "yyyy-MM-dd"); } else { // Node.js: implement custom formatting if needed return date.toISOString().split('T')[0]; }}
UUID Generation
Section titled âUUID GenerationâUUIDs are generated using:
- Google Apps Script: Platform-specific UUID generation
- Node.js:
crypto.randomUUID()
Encoding and Cryptography
Section titled âEncoding and CryptographyâBoth platforms support:
- Base64 encoding
- HMAC signature computation with various algorithms (sha1, sha256, etc.)
Error Handling
Section titled âError HandlingâCommon Exceptions
Section titled âCommon Exceptionsâtry { const response = EnvironmentAdapter.fetch(url);} catch (error) { if (error instanceof UnsupportedEnvironmentException) { console.error("Environment not supported:", error.message); } else { console.error("Other error:", error.message); }}
Best Practices for Error Handling
Section titled âBest Practices for Error Handlingâfunction robustApiCall(url, retries = 3) { for (let i = 0; i < retries; i++) { try { const response = EnvironmentAdapter.fetch(url); if (response.getResponseCode() === 200) { return response.getAsJson(); } throw new Error(`HTTP ${response.getResponseCode()}`); } catch (error) { console.warn(`Attempt ${i + 1} failed:`, error.message); if (i === retries - 1) throw error;
// Wait before retry EnvironmentAdapter.sleep(1000 * Math.pow(2, i)); // Exponential backoff } }}
Troubleshooting
Section titled âTroubleshootingâCommon Issues and Solutions
Section titled âCommon Issues and Solutionsâ1. UnsupportedEnvironmentException
Section titled â1. UnsupportedEnvironmentExceptionâProblem: Getting UnsupportedEnvironmentException
error.
Solution:
- Ensure youâre running in Google Apps Script or Node.js
- Check that required dependencies are installed in Node.js
- Verify
ENVIRONMENT
constants are properly defined
2. JSON Parsing Errors
Section titled â2. JSON Parsing ErrorsâProblem: response.getAsJson()
throws âInvalid JSON responseâ error.
Solution:
const response = EnvironmentAdapter.fetch(url);const text = response.getContentText();console.log("Raw response:", text); // Debug the actual response
try { const json = response.getAsJson();} catch (error) { console.error("Not a JSON response, got:", text);}
3. HTTP Request Timeouts
Section titled â3. HTTP Request TimeoutsâProblem: Requests hanging or timing out.
Solution:
// For Google Apps Script, add timeout optionsconst options = { method: "GET", muteHttpExceptions: true, // Add other GAS-specific options as needed};
4. Missing Dependencies (Node.js)
Section titled â4. Missing Dependencies (Node.js)âProblem: request
or deasync
not found.
Solution:
npm install sync-request deasync
Debug Mode
Section titled âDebug ModeâEnable detailed logging for troubleshooting:
function debugFetch(url, options = {}) { console.log("Environment:", EnvironmentAdapter.getEnvironment()); console.log("Request URL:", url); console.log("Request options:", options);
try { const response = EnvironmentAdapter.fetch(url, options); console.log("Response code:", response.getResponseCode()); console.log("Response headers:", response.getHeaders());
return response; } catch (error) { console.error("Request failed:", error); throw error; }}