move rhailib to herolib
This commit is contained in:
530
rhailib/docs/API_INTEGRATION_GUIDE.md
Normal file
530
rhailib/docs/API_INTEGRATION_GUIDE.md
Normal file
@@ -0,0 +1,530 @@
|
||||
# API Integration Guide for RhaiLib
|
||||
|
||||
## Quick Start
|
||||
|
||||
This guide shows you how to integrate external APIs with Rhai scripts using RhaiLib's async architecture.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Setup and Configuration](#setup-and-configuration)
|
||||
2. [Basic API Calls](#basic-api-calls)
|
||||
3. [Stripe Payment Integration](#stripe-payment-integration)
|
||||
4. [Error Handling Patterns](#error-handling-patterns)
|
||||
5. [Advanced Usage](#advanced-usage)
|
||||
6. [Extending to Other APIs](#extending-to-other-apis)
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
### 1. Environment Variables
|
||||
|
||||
Create a `.env` file in your project:
|
||||
|
||||
```bash
|
||||
# .env
|
||||
STRIPE_SECRET_KEY=sk_test_your_stripe_key_here
|
||||
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key_here
|
||||
```
|
||||
|
||||
### 2. Rust Setup
|
||||
|
||||
```rust
|
||||
use rhailib_dsl::payment::register_payment_rhai_module;
|
||||
use rhai::{Engine, EvalAltResult, Scope};
|
||||
use std::env;
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
// Load environment variables
|
||||
dotenv::from_filename(".env").ok();
|
||||
|
||||
// Create Rhai engine and register payment module
|
||||
let mut engine = Engine::new();
|
||||
register_payment_rhai_module(&mut engine);
|
||||
|
||||
// Set up scope with API credentials
|
||||
let mut scope = Scope::new();
|
||||
let stripe_key = env::var("STRIPE_SECRET_KEY").unwrap();
|
||||
scope.push("STRIPE_API_KEY", stripe_key);
|
||||
|
||||
// Execute your Rhai script
|
||||
let script = std::fs::read_to_string("payment_script.rhai")?;
|
||||
engine.eval_with_scope::<()>(&mut scope, &script)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Rhai Script Configuration
|
||||
|
||||
```rhai
|
||||
// Configure the API client
|
||||
let config_result = configure_stripe(STRIPE_API_KEY);
|
||||
print(`Configuration: ${config_result}`);
|
||||
```
|
||||
|
||||
## Basic API Calls
|
||||
|
||||
### Simple Product Creation
|
||||
|
||||
```rhai
|
||||
// Create a basic product
|
||||
let product = new_product()
|
||||
.name("My Product")
|
||||
.description("A great product");
|
||||
|
||||
try {
|
||||
let product_id = product.create();
|
||||
print(`✅ Created product: ${product_id}`);
|
||||
} catch(error) {
|
||||
print(`❌ Error: ${error}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Price Configuration
|
||||
|
||||
```rhai
|
||||
// One-time payment price
|
||||
let one_time_price = new_price()
|
||||
.amount(1999) // $19.99 in cents
|
||||
.currency("usd")
|
||||
.product(product_id);
|
||||
|
||||
let price_id = one_time_price.create();
|
||||
|
||||
// Subscription price
|
||||
let monthly_price = new_price()
|
||||
.amount(999) // $9.99 in cents
|
||||
.currency("usd")
|
||||
.product(product_id)
|
||||
.recurring("month");
|
||||
|
||||
let monthly_price_id = monthly_price.create();
|
||||
```
|
||||
|
||||
## Stripe Payment Integration
|
||||
|
||||
### Complete Payment Workflow
|
||||
|
||||
```rhai
|
||||
// 1. Configure Stripe
|
||||
configure_stripe(STRIPE_API_KEY);
|
||||
|
||||
// 2. Create Product
|
||||
let product = new_product()
|
||||
.name("Premium Software License")
|
||||
.description("Professional software solution")
|
||||
.metadata("category", "software")
|
||||
.metadata("tier", "premium");
|
||||
|
||||
let product_id = product.create();
|
||||
|
||||
// 3. Create Pricing Options
|
||||
let monthly_price = new_price()
|
||||
.amount(2999) // $29.99
|
||||
.currency("usd")
|
||||
.product(product_id)
|
||||
.recurring("month")
|
||||
.metadata("billing", "monthly");
|
||||
|
||||
let annual_price = new_price()
|
||||
.amount(29999) // $299.99 (save $60)
|
||||
.currency("usd")
|
||||
.product(product_id)
|
||||
.recurring("year")
|
||||
.metadata("billing", "annual")
|
||||
.metadata("discount", "save_60");
|
||||
|
||||
let monthly_price_id = monthly_price.create();
|
||||
let annual_price_id = annual_price.create();
|
||||
|
||||
// 4. Create Discount Coupons
|
||||
let welcome_coupon = new_coupon()
|
||||
.duration("once")
|
||||
.percent_off(25)
|
||||
.metadata("campaign", "welcome_offer");
|
||||
|
||||
let coupon_id = welcome_coupon.create();
|
||||
|
||||
// 5. Create Payment Intent for One-time Purchase
|
||||
let payment_intent = new_payment_intent()
|
||||
.amount(2999)
|
||||
.currency("usd")
|
||||
.customer("cus_customer_id")
|
||||
.description("Monthly subscription payment")
|
||||
.add_payment_method_type("card")
|
||||
.metadata("price_id", monthly_price_id);
|
||||
|
||||
let intent_id = payment_intent.create();
|
||||
|
||||
// 6. Create Subscription
|
||||
let subscription = new_subscription()
|
||||
.customer("cus_customer_id")
|
||||
.add_price(monthly_price_id)
|
||||
.trial_days(14)
|
||||
.coupon(coupon_id)
|
||||
.metadata("source", "website");
|
||||
|
||||
let subscription_id = subscription.create();
|
||||
```
|
||||
|
||||
### Builder Pattern Examples
|
||||
|
||||
#### Product with Metadata
|
||||
```rhai
|
||||
let product = new_product()
|
||||
.name("Enterprise Software")
|
||||
.description("Full-featured business solution")
|
||||
.metadata("category", "enterprise")
|
||||
.metadata("support_level", "premium")
|
||||
.metadata("deployment", "cloud");
|
||||
```
|
||||
|
||||
#### Complex Pricing
|
||||
```rhai
|
||||
let tiered_price = new_price()
|
||||
.amount(4999) // $49.99
|
||||
.currency("usd")
|
||||
.product(product_id)
|
||||
.recurring_with_count("month", 12) // 12 monthly payments
|
||||
.metadata("tier", "professional")
|
||||
.metadata("features", "advanced");
|
||||
```
|
||||
|
||||
#### Multi-item Subscription
|
||||
```rhai
|
||||
let enterprise_subscription = new_subscription()
|
||||
.customer("cus_enterprise_customer")
|
||||
.add_price_with_quantity(user_license_price_id, 50) // 50 user licenses
|
||||
.add_price(support_addon_price_id) // Premium support
|
||||
.add_price(analytics_addon_price_id) // Analytics addon
|
||||
.trial_days(30)
|
||||
.metadata("plan", "enterprise")
|
||||
.metadata("contract_length", "annual");
|
||||
```
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
### Basic Error Handling
|
||||
|
||||
```rhai
|
||||
try {
|
||||
let result = some_api_call();
|
||||
print(`Success: ${result}`);
|
||||
} catch(error) {
|
||||
print(`Error occurred: ${error}`);
|
||||
// Continue with fallback logic
|
||||
}
|
||||
```
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
```rhai
|
||||
// Try to create with coupon, fallback without coupon
|
||||
let subscription_id;
|
||||
try {
|
||||
subscription_id = new_subscription()
|
||||
.customer(customer_id)
|
||||
.add_price(price_id)
|
||||
.coupon(coupon_id)
|
||||
.create();
|
||||
} catch(error) {
|
||||
print(`Coupon failed: ${error}, creating without coupon`);
|
||||
subscription_id = new_subscription()
|
||||
.customer(customer_id)
|
||||
.add_price(price_id)
|
||||
.create();
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Before API Calls
|
||||
|
||||
```rhai
|
||||
// Validate inputs before making API calls
|
||||
if customer_id == "" {
|
||||
print("❌ Customer ID is required");
|
||||
return;
|
||||
}
|
||||
|
||||
if price_id == "" {
|
||||
print("❌ Price ID is required");
|
||||
return;
|
||||
}
|
||||
|
||||
// Proceed with API call
|
||||
let subscription = new_subscription()
|
||||
.customer(customer_id)
|
||||
.add_price(price_id)
|
||||
.create();
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Conditional Logic
|
||||
|
||||
```rhai
|
||||
// Different pricing based on customer type
|
||||
let price_id;
|
||||
if customer_type == "enterprise" {
|
||||
price_id = enterprise_price_id;
|
||||
} else if customer_type == "professional" {
|
||||
price_id = professional_price_id;
|
||||
} else {
|
||||
price_id = standard_price_id;
|
||||
}
|
||||
|
||||
let subscription = new_subscription()
|
||||
.customer(customer_id)
|
||||
.add_price(price_id);
|
||||
|
||||
// Add trial for new customers
|
||||
if is_new_customer {
|
||||
subscription = subscription.trial_days(14);
|
||||
}
|
||||
|
||||
let subscription_id = subscription.create();
|
||||
```
|
||||
|
||||
### Dynamic Metadata
|
||||
|
||||
```rhai
|
||||
// Build metadata dynamically
|
||||
let product = new_product()
|
||||
.name(product_name)
|
||||
.description(product_description);
|
||||
|
||||
// Add metadata based on conditions
|
||||
if has_support {
|
||||
product = product.metadata("support", "included");
|
||||
}
|
||||
|
||||
if is_premium {
|
||||
product = product.metadata("tier", "premium");
|
||||
}
|
||||
|
||||
if region != "" {
|
||||
product = product.metadata("region", region);
|
||||
}
|
||||
|
||||
let product_id = product.create();
|
||||
```
|
||||
|
||||
### Bulk Operations
|
||||
|
||||
```rhai
|
||||
// Create multiple prices for a product
|
||||
let price_configs = [
|
||||
#{amount: 999, interval: "month", name: "Monthly"},
|
||||
#{amount: 9999, interval: "year", name: "Annual"},
|
||||
#{amount: 19999, interval: "", name: "Lifetime"}
|
||||
];
|
||||
|
||||
let price_ids = [];
|
||||
for config in price_configs {
|
||||
let price = new_price()
|
||||
.amount(config.amount)
|
||||
.currency("usd")
|
||||
.product(product_id)
|
||||
.metadata("plan_name", config.name);
|
||||
|
||||
if config.interval != "" {
|
||||
price = price.recurring(config.interval);
|
||||
}
|
||||
|
||||
let price_id = price.create();
|
||||
price_ids.push(price_id);
|
||||
print(`Created ${config.name} price: ${price_id}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Extending to Other APIs
|
||||
|
||||
### Adding New API Support
|
||||
|
||||
To extend the architecture to other APIs, follow this pattern:
|
||||
|
||||
#### 1. Define Configuration Structure
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomApiConfig {
|
||||
pub api_key: String,
|
||||
pub base_url: String,
|
||||
pub client: Client,
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Implement Request Handler
|
||||
|
||||
```rust
|
||||
async fn handle_custom_api_request(
|
||||
config: &CustomApiConfig,
|
||||
request: &AsyncRequest
|
||||
) -> Result<String, String> {
|
||||
let url = format!("{}/{}", config.base_url, request.endpoint);
|
||||
|
||||
let response = config.client
|
||||
.request(Method::from_str(&request.method).unwrap(), &url)
|
||||
.header("Authorization", format!("Bearer {}", config.api_key))
|
||||
.json(&request.data)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Request failed: {}", e))?;
|
||||
|
||||
let response_text = response.text().await
|
||||
.map_err(|e| format!("Failed to read response: {}", e))?;
|
||||
|
||||
Ok(response_text)
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Register Rhai Functions
|
||||
|
||||
```rust
|
||||
#[rhai_fn(name = "custom_api_call", return_raw)]
|
||||
pub fn custom_api_call(
|
||||
endpoint: String,
|
||||
data: rhai::Map
|
||||
) -> Result<String, Box<EvalAltResult>> {
|
||||
let registry = CUSTOM_API_REGISTRY.lock().unwrap();
|
||||
let registry = registry.as_ref().ok_or("API not configured")?;
|
||||
|
||||
let form_data: HashMap<String, String> = data.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect();
|
||||
|
||||
registry.make_request(endpoint, "POST".to_string(), form_data)
|
||||
.map_err(|e| e.to_string().into())
|
||||
}
|
||||
```
|
||||
|
||||
### Example: GitHub API Integration
|
||||
|
||||
```rhai
|
||||
// Hypothetical GitHub API integration
|
||||
configure_github_api(GITHUB_TOKEN);
|
||||
|
||||
// Create a repository
|
||||
let repo_data = #{
|
||||
name: "my-new-repo",
|
||||
description: "Created via Rhai script",
|
||||
private: false
|
||||
};
|
||||
|
||||
let repo_result = github_api_call("user/repos", repo_data);
|
||||
print(`Repository created: ${repo_result}`);
|
||||
|
||||
// Create an issue
|
||||
let issue_data = #{
|
||||
title: "Initial setup",
|
||||
body: "Setting up the repository structure",
|
||||
labels: ["enhancement", "setup"]
|
||||
};
|
||||
|
||||
let issue_result = github_api_call("repos/user/my-new-repo/issues", issue_data);
|
||||
print(`Issue created: ${issue_result}`);
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### 1. Batch Operations
|
||||
```rhai
|
||||
// Instead of creating items one by one, batch when possible
|
||||
let items_to_create = [item1, item2, item3];
|
||||
let created_items = [];
|
||||
|
||||
for item in items_to_create {
|
||||
try {
|
||||
let result = item.create();
|
||||
created_items.push(result);
|
||||
} catch(error) {
|
||||
print(`Failed to create item: ${error}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Reuse Configuration
|
||||
```rhai
|
||||
// Configure once, use multiple times
|
||||
configure_stripe(STRIPE_API_KEY);
|
||||
|
||||
// Multiple operations use the same configuration
|
||||
let product1_id = new_product().name("Product 1").create();
|
||||
let product2_id = new_product().name("Product 2").create();
|
||||
let price1_id = new_price().product(product1_id).amount(1000).create();
|
||||
let price2_id = new_price().product(product2_id).amount(2000).create();
|
||||
```
|
||||
|
||||
### 3. Error Recovery
|
||||
```rhai
|
||||
// Implement retry logic for transient failures
|
||||
let max_retries = 3;
|
||||
let retry_count = 0;
|
||||
let success = false;
|
||||
|
||||
while retry_count < max_retries && !success {
|
||||
try {
|
||||
let result = api_operation();
|
||||
success = true;
|
||||
print(`Success: ${result}`);
|
||||
} catch(error) {
|
||||
retry_count += 1;
|
||||
print(`Attempt ${retry_count} failed: ${error}`);
|
||||
if retry_count < max_retries {
|
||||
print("Retrying...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !success {
|
||||
print("❌ All retry attempts failed");
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging and Monitoring
|
||||
|
||||
### Enable Detailed Logging
|
||||
|
||||
```rhai
|
||||
// The architecture automatically logs key operations:
|
||||
// 🔧 Configuring Stripe...
|
||||
// 🚀 Async worker thread started
|
||||
// 🔄 Processing POST request to products
|
||||
// 📥 Stripe response: {...}
|
||||
// ✅ Request successful with ID: prod_xxx
|
||||
```
|
||||
|
||||
### Monitor Request Performance
|
||||
|
||||
```rhai
|
||||
// Time API operations
|
||||
let start_time = timestamp();
|
||||
let result = expensive_api_operation();
|
||||
let end_time = timestamp();
|
||||
print(`Operation took ${end_time - start_time}ms`);
|
||||
```
|
||||
|
||||
### Handle Rate Limits
|
||||
|
||||
```rhai
|
||||
// Implement backoff for rate-limited APIs
|
||||
try {
|
||||
let result = api_call();
|
||||
} catch(error) {
|
||||
if error.contains("rate limit") {
|
||||
print("Rate limited, waiting before retry...");
|
||||
// In a real implementation, you'd add delay logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Always handle errors gracefully** - Use try/catch blocks for all API calls
|
||||
2. **Validate inputs** - Check required fields before making API calls
|
||||
3. **Use meaningful metadata** - Add context to help with debugging and analytics
|
||||
4. **Configure once, use many** - Set up API clients once and reuse them
|
||||
5. **Implement retry logic** - Handle transient network failures
|
||||
6. **Monitor performance** - Track API response times and success rates
|
||||
7. **Secure credentials** - Use environment variables for API keys
|
||||
8. **Test with demo data** - Use test API keys during development
|
||||
|
||||
This architecture provides a robust foundation for integrating any HTTP-based API with Rhai scripts while maintaining the simplicity and safety that makes Rhai attractive for domain-specific scripting.
|
Reference in New Issue
Block a user