π WebAPIDataSource
Complete REST API integration with authentication, caching, and circuit breaker patterns
- Core partial files in
DataManagementEngineStandard/WebAPI/:WebAPIDataSource.cs— properties, constructor, helper wiring, disposeWebAPIDataSource.Connection.cs—Openconnection/Closeconnection/ transactionsWebAPIDataSource.Http.cs—GetAsync,PostAsync,PutAsync,DeleteAsyncconvenience methodsWebAPIDataSource.Data.cs— CRUD:GetEntity,InsertEntity,UpdateEntity,DeleteEntity,UpdateEntitiesWebAPIDataSource.DataOperations.cs— extended data retrieval and manipulationWebAPIDataSource.EntityMethods.cs— entity discovery and CRUD orchestrationWebAPIDataSource.Structure.cs—GetEntitesList,CheckEntityExist,GetEntityStructureWebAPIDataSource.Query.cs— query execution and scalar retrievalWebAPIDataSource.QueryMethods.cs— extended query processingWebAPIDataSource.PagingAsync.cs— pagination and async data retrievalWebAPIDataSource.Bulk.cs— bulk operation stubsWebAPIDataSource.BulkOperations.cs— enhanced batch processingWebAPIDataSource.Scripting.cs— script execution and entity creationWebAPIDataSource.ScriptTransactions.cs— transaction lifecycle stubsWebAPIDataSource.WebAPI.cs—IWebAPIDataSource-specificReadDataWebAPIDataSource.CoreInterface.cs—IDataSourceinterface implementationWebAPIDataSource.CoreInterface.Fixed.cs— additional interface correctionsWebAPIDataSource.Interface.cs— supplementary interface contractsWebAPIDataSource.Complete.cs— final assembly membersWebAPIDataSource.Main.cs— orchestration entry points
- Helpers in
WebAPI/Helpers/(allIDisposable):WebAPIConfigurationHelper— strongly-typed config properties, validation, endpoint/pagination settingsWebAPIAuthenticationHelper— OAuth2/Bearer/API Key/Basic auth with token refreshWebAPIRequestHelper— centralizedHttpClientexecution with concurrency semaphore and retryWebAPIDataHelper— URL construction, JSON parsing, ID extraction, cache-key helpersWebAPICacheHelper— concurrent in-memory cache with TTL and cleanup timerWebAPIRateLimitHelper— token bucket algorithm per-endpoint throttlingWebAPISchemaHelper— schema inference from JSON samples, entity validationWebAPIErrorHelper— circuit breaker pattern, exponential backoff, error categorizationWebAPIInterfaces—IWebAPISchemaHelper,IWebAPIDataHelpercontracts
- Connection model:
WebAPIConnectionProperties(withAuthTypeEnum,ValidateConfiguration(),GetValidationErrors()) /WebAPIDataConnection(normalizesIConnectionProperties). - Example connector:
BeepDataSources/Connectors/SocialMedia/Twitter/TwitterDataSource.cs.
Updated Configuration Guide
This documentation uses the optimized WebAPIConnectionProperties class structure:
- WebAPIConnectionProperties - Comprehensive Web API connection class with all required properties
- AuthTypeEnum - Strongly-typed authentication types (None, ApiKey, Basic, Bearer, OAuth2)
- Built-in validation - ValidateConfiguration() and GetValidationErrors() methods
- Parameter management - GetParameterValue() and SetParameterValue() methods
- Smart defaults - Automatic headers and authentication requirement detection
- Complete interface compliance - Implements all IConnectionProperties members
π Table of Contents
Overview
The WebAPIDataSource is a comprehensive implementation of the IDataSource interface
specifically designed for REST API integration within the BeepDM framework. It provides a unified way to interact
with web APIs using familiar database-like operations while handling the complexities of HTTP communication,
authentication, caching, and error recovery.
π Database-like Operations
Use familiar CRUD operations (Create, Read, Update, Delete) with any REST API
Γ°ΕΈβΒ Multiple Authentication
OAuth2, Bearer tokens, API keys, Basic authentication, and custom providers
β‘ Circuit Breaker Pattern
Automatic failure recovery and resilience for production environments
Γ°ΕΈβΒΎ Intelligent Caching
Response caching with TTL and invalidation for improved performance
Γ°ΕΈΕ‘Β¦ Rate Limiting
Built-in throttling and request queuing to respect API limits
π Schema Inference
Automatic API schema discovery from JSON responses
Architecture
Partial Class Structure
The WebAPIDataSource follows a partial class pattern (20 files) for maintainability, mirroring the RDBSource approach:
WebAPIDataSource.cs // Core state (properties/fields), constructor, dispose
WebAPIDataSource.CoreInterface.cs // IDataSource interface + event handling
WebAPIDataSource.CoreInterface.Fixed.cs // Additional/corrected interface members
WebAPIDataSource.Interface.cs // Supplementary interface contracts
WebAPIDataSource.Connection.cs // Openconnection/Closeconnection, auth integration
WebAPIDataSource.Data.cs // CRUD: GetEntity, InsertEntity, UpdateEntity, DeleteEntity
WebAPIDataSource.DataOperations.cs // Extended data manipulation helpers
WebAPIDataSource.EntityMethods.cs // Entity discovery and CRUD orchestration
WebAPIDataSource.Http.cs // Centralized HTTP execution (GetAsync/PostAsync etc.)
WebAPIDataSource.Structure.cs // Schema & entity structure discovery
WebAPIDataSource.Query.cs // Query execution and scalar operations
WebAPIDataSource.QueryMethods.cs // Extended query processing
WebAPIDataSource.PagingAsync.cs // Pagination + async data retrieval
WebAPIDataSource.Bulk.cs // Bulk operation stubs
WebAPIDataSource.BulkOperations.cs // Enhanced batch processing
WebAPIDataSource.Scripting.cs // Script execution and entity creation
WebAPIDataSource.ScriptTransactions.cs // Transaction lifecycle stubs
WebAPIDataSource.WebAPI.cs // IWebAPIDataSource-specific ReadData
WebAPIDataSource.Complete.cs // Final assembly/completion members
WebAPIDataSource.Main.cs // Additional orchestration entry points
Helper Classes
The implementation uses specialized helper classes for different concerns, all in the WebAPI/Helpers/ folder under TheTechIdea.Beep.WebAPI.Helpers namespace:
| Helper | File | Key Capabilities |
|---|---|---|
| WebAPIConfigurationHelper | WebAPIConfigurationHelper.cs | Base URL, auth type, timeout, retry, caching, rate limit, pagination, proxy, SSL, response format β all from IConnectionProperties |
| WebAPIAuthenticationHelper | WebAPIAuthenticationHelper.cs | Token refresh with EnsureAuthenticatedAsync(), header injection for None/ApiKey/Basic/Bearer/OAuth2 |
| WebAPIRequestHelper | WebAPIRequestHelper.cs | SendWithRetryAsync with SemaphoreSlim concurrency control, request cloning for retries |
| WebAPIDataHelper | WebAPIDataHelper.cs | BuildEndpointUrl, ProcessApiResponse, ExtractIdFromResponse, GenerateDataCacheKey, ConvertToEntityStructure |
| WebAPICacheHelper | WebAPICacheHelper.cs | Concurrent Dictionary-backed in-memory cache with TTL, cleanup timer, hit/miss stats |
| WebAPIRateLimitHelper | WebAPIRateLimitHelper.cs | Token bucket algorithm per endpoint with WaitForCapacityAsync, stale bucket cleanup |
| WebAPISchemaHelper | WebAPISchemaHelper.cs | InferSchemaFromJsonAsync, DiscoverEntitiesAsync, schema caching with configurable expiry |
| WebAPIErrorHelper | WebAPIErrorHelper.cs | Exponential backoff retry, circuit breaker (FailureThreshold/RecoveryTimeoutSeconds), transient error classification |
| WebAPIInterfaces | WebAPIInterfaces.cs | IWebAPISchemaHelper, IWebAPIDataHelper, DataValidationResult |
All helpers are composed in the WebAPIDataSource constructor and disposed in Dispose().
Configuration
Connection Properties
Configure your WebAPIDataSource using the WebAPIConnectionProperties class with built-in validation:
// Create WebAPI connection with comprehensive settings
var webApiConnection = new WebAPIConnectionProperties
{
// Core connection settings
ConnectionName = "GitHub API",
ConnectionString = "https://api.github.com",
AuthType = AuthTypeEnum.Bearer,
ApiKey = "ghp_your-github-token",
// Request configuration
TimeoutMs = 30000,
MaxRetries = 3,
RetryIntervalMs = 1000,
// Performance settings
EnableCaching = true,
CacheExpiryMinutes = 15,
MaxConcurrentRequests = 10,
// Rate limiting
EnableRateLimit = true,
RateLimitRequestsPerMinute = 60,
// Response handling
ResponseFormat = "json",
UserAgent = "MyApp/1.0"
};
// Add custom headers
webApiConnection.Headers.Add(new WebApiHeader
{
Headername = "Accept",
Headervalue = "application/vnd.github.v3+json"
});
// Set additional parameters
webApiConnection.SetParameterValue("DefaultPageSize", "50");
webApiConnection.SetParameterValue("MaxPageSize", "100");
// Validate configuration
if (webApiConnection.ValidateConfiguration())
{
Console.WriteLine("Configuration is valid");
}
else
{
Console.WriteLine($"Configuration errors: {webApiConnection.GetValidationErrors()}");
}
Authentication Types
WebAPIConnectionProperties supports multiple authentication methods through the AuthTypeEnum:
// No Authentication
var noAuthConnection = new WebAPIConnectionProperties
{
ConnectionString = "https://api.publicdata.com",
AuthType = AuthTypeEnum.None
};
// API Key Authentication
var apiKeyConnection = new WebAPIConnectionProperties
{
ConnectionString = "https://api.service.com",
AuthType = AuthTypeEnum.ApiKey,
ApiKey = "your-api-key",
ApiKeyHeader = "X-API-Key" // Default, can be customized
};
// Basic Authentication
var basicAuthConnection = new WebAPIConnectionProperties
{
ConnectionString = "https://api.internal.com",
AuthType = AuthTypeEnum.Basic,
UserID = "username",
Password = "password"
};
// Bearer Token Authentication
var bearerConnection = new WebAPIConnectionProperties
{
ConnectionString = "https://api.service.com",
AuthType = AuthTypeEnum.Bearer,
ApiKey = "your-bearer-token" // Static token
};
// OAuth2 Authentication
var oauth2Connection = new WebAPIConnectionProperties
{
ConnectionString = "https://api.service.com",
AuthType = AuthTypeEnum.OAuth2,
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
TokenUrl = "https://auth.service.com/oauth/token",
GrantType = "client_credentials", // or "password", "authorization_code"
Scope = "read write" // Optional
};
Usage Examples
Basic Setup
// Initialize the data source
var webApiDS = new WebAPIDataSource("MyAPI", logger, dmeEditor, DataSourceType.WebApi, errorObject);
// Open connection (performs authentication)
var connectionResult = webApiDS.Openconnection();
// Get list of available entities
var entities = webApiDS.GetEntitesList();
// Get entity structure
var userStructure = webApiDS.GetEntityStructure("Users", false);
GitHub API Integration
Advanced Configuration
Configure advanced settings for production environments:
var advancedConnection = new WebAPIConnectionProperties
{
ConnectionName = "Production API",
ConnectionString = "https://api.production.com/v2",
AuthType = AuthTypeEnum.OAuth2,
// OAuth2 settings
ClientId = "prod-client-id",
ClientSecret = Environment.GetEnvironmentVariable("API_CLIENT_SECRET"),
TokenUrl = "https://auth.production.com/oauth/token",
GrantType = "client_credentials",
Scope = "api:read api:write",
// Performance tuning
TimeoutMs = 60000, // 60 seconds for slow APIs
MaxRetries = 5,
RetryIntervalMs = 2000, // 2 second delay between retries
MaxConcurrentRequests = 20,
// Caching strategy
EnableCaching = true,
CacheExpiryMinutes = 30,
// Rate limiting
EnableRateLimit = true,
RateLimitRequestsPerMinute = 120,
// Security settings
ValidateServerCertificate = true,
IgnoreSSLErrors = false,
// Proxy configuration (if needed)
UseProxy = true,
ProxyUrl = "http://corporate-proxy.com",
ProxyPort = 8080,
UseDefaultProxyCredentials = true,
// Response handling
ResponseFormat = "json",
DataPath = "$.data", // JSONPath to extract data
TotalCountPath = "$.meta.total", // JSONPath for pagination
// Pagination
PageNumberParameter = "page",
PageSizeParameter = "per_page",
DefaultPageSize = 50,
MaxPageSize = 200,
// Custom headers
UserAgent = "MyApp/2.1.0 (+https://mycompany.com/contact)"
};
// Add authentication header template
advancedConnection.Headers.Add(new WebApiHeader
{
Headername = "Accept",
Headervalue = "application/vnd.api+json"
});
// Store endpoint mappings
advancedConnection.SetParameterValue("Endpoints.Users", "/api/v2/users");
advancedConnection.SetParameterValue("Endpoints.Orders", "/api/v2/orders");
advancedConnection.SetParameterValue("Endpoints.Products", "/api/v2/products");
// Validate before use
if (!advancedConnection.ValidateConfiguration())
{
throw new InvalidOperationException($"Invalid API configuration: {advancedConnection.GetValidationErrors()}");
}
GitHub API Integration
GitHub API Configuration
// Configure for GitHub API v4 (GraphQL) and v3 (REST)
var githubConnection = new WebAPIConnectionProperties
{
ConnectionName = "GitHub API",
ConnectionString = "https://api.github.com",
AuthType = AuthTypeEnum.Bearer,
ApiKey = "ghp_your_personal_access_token",
// GitHub-specific settings
TimeoutMs = 30000,
MaxRetries = 3,
RetryIntervalMs = 1000,
RateLimitRequestsPerMinute = 60, // GitHub's rate limit
// Response settings
ResponseFormat = "json",
UserAgent = "MyApp/1.0 (https://github.com/myuser/myapp)",
// Pagination for GitHub API
PageSizeParameter = "per_page",
PageNumberParameter = "page",
DefaultPageSize = 30,
MaxPageSize = 100
};
// Add GitHub-specific headers
githubConnection.Headers.Add(new WebApiHeader
{
Headername = "Accept",
Headervalue = "application/vnd.github.v3+json"
});
// Configure endpoint mappings
githubConnection.SetParameterValue("Repos.List", "/user/repos");
githubConnection.SetParameterValue("Repos.Get", "/repos/{owner}/{repo}");
githubConnection.SetParameterValue("Issues.List", "/repos/{owner}/{repo}/issues");
githubConnection.SetParameterValue("Pulls.List", "/repos/{owner}/{repo}/pulls");
githubConnection.SetParameterValue("Commits.List", "/repos/{owner}/{repo}/commits");
// Validate configuration
if (githubConnection.ValidateConfiguration())
{
Console.WriteLine("GitHub API configuration is valid");
}
Usage Example
// Initialize data source with GitHub connection
var githubDataSource = new WebAPIDataSource("GitHub", logger, dmeEditor, DataSourceType.WebApi, errorObject);
githubDataSource.ConnectionProp = githubConnection;
// Open connection
var connectionResult = githubDataSource.Openconnection();
if (connectionResult == ConnectionState.Open)
{
// Get repository information
var filters = new List<AppFilter>
{
new AppFilter {FieldName = "owner", FilterValue = "microsoft", Operator = "=" },
new AppFilter {FieldName = "repo", FilterValue = "vscode", Operator = "=" }
};
var repoData = githubDataSource.GetEntity("repos", filters);
// Get issues with pagination
var issueFilters = new List<AppFilter>
{
new AppFilter {FieldName = "owner", FilterValue = "microsoft", Operator = "=" },
new AppFilter {FieldName = "repo", FilterValue = "vscode", Operator = "=" },
new AppFilter {FieldName = "state", FilterValue = "open", Operator = "=" }
};
var pagedIssues = githubDataSource.GetEntity("issues", issueFilters, 1, 50);
// Execute custom query
var recentCommits = githubDataSource.RunQuery("GET /repos/microsoft/vscode/commits?since=2024-01-01T00:00:00Z");
}
Twitter API v2
Twitter API Configuration
// Configure for Twitter API v2 using standard ConnectionProperties
ConnectionProp.ConnectionString = "https://api.twitter.com/2";
ConnectionProp.ApiKey = "your-twitter-bearer-token";
// Add authorization header
ConnectionProp.Headers.Add(new WebApiHeader
{
Headername = "Authorization",
Headervalue = "Bearer your-twitter-bearer-token"
});
// Define endpoints using Parameters
ConnectionProp.Parameters = "Endpoints.Tweets.Get=/tweets/search/recent;Endpoints.Users.Get=/users/by/username/{username};Endpoints.Spaces.Get=/spaces/{id}";
// Or using WebAPIConnectionProperties
var webApiProps = new WebAPIConnectionProperties
{
ConnectionString = "https://api.twitter.com/2",
AuthType = "Bearer",
ApiKey = "your-twitter-bearer-token"
};
Usage Example
// Search for recent tweets
var tweetFilters = new List<AppFilter> {
new AppFilter {FieldName = "query", FilterValue = "#CSharp", Operator = "=" },
new AppFilter {FieldName = "max_results", FilterValue = "100", Operator = "=" }
};
var tweets = webApiDS.GetEntity("Tweets", tweetFilters);
// Get user information
var userFilters = new List<AppFilter> {
new AppFilter {FieldName = "username", FilterValue = "GitHub", Operator = "=" }
};
var userData = webApiDS.GetEntity("Users", userFilters);
Stripe API (Payment Processing)
Stripe API Configuration
// Configure for Stripe API using standard ConnectionProperties
ConnectionProp.ConnectionString = "https://api.stripe.com/v1";
ConnectionProp.ApiKey = "sk_test_your-stripe-secret-key";
// Add authorization header
ConnectionProp.Headers.Add(new WebApiHeader
{
Headername = "Authorization",
Headervalue = "Bearer sk_test_your-stripe-secret-key"
});
// Define endpoints using Parameters
ConnectionProp.Parameters = "Endpoints.Customers.Get=/customers;Endpoints.Customers.Post=/customers;Endpoints.Charges.Get=/charges;Endpoints.Invoices.Get=/invoices";
// Or using WebAPIConnectionProperties
var webApiProps = new WebAPIConnectionProperties
{
ConnectionString = "https://api.stripe.com/v1",
AuthType = "Bearer",
ApiKey = "sk_test_your-stripe-secret-key"
};
Usage Example
// Get customer list
var customers = webApiDS.GetEntity("Customers", null);
// Create new customer
var newCustomer = new {
email = "customer@example.com",
name = "John Doe",
description = "New customer"
};
var insertResult = webApiDS.InsertEntity("Customers", newCustomer);
// Get charges with filtering
var chargeFilters = new List<AppFilter> {
new AppFilter {FieldName = "customer", FilterValue = "cus_1234567890", Operator = "=" },
new AppFilter {FieldName = "limit", FilterValue = "50", Operator = "=" }
};
var charges = webApiDS.GetEntity("Charges", chargeFilters);
Company Internal API
Internal API Configuration
// Configure for internal company API using standard ConnectionProperties
ConnectionProp.ConnectionString = "https://api.company.internal/v2";
ConnectionProp.UserID = "company-app-client-id";
ConnectionProp.Password = "company-app-client-secret";
// Store OAuth2 configuration in Parameters
ConnectionProp.Parameters = "AuthType=OAuth2;TokenUrl=https://auth.company.internal/oauth/token;GrantType=client_credentials";
// Define endpoints using Parameters
ConnectionProp.Parameters += ";Endpoints.Employees.Get=/hr/employees;Endpoints.Employees.Post=/hr/employees;Endpoints.Projects.Get=/pm/projects;Endpoints.Timesheets.Get=/hr/timesheets";
// Or using WebAPIConnectionProperties
var webApiProps = new WebAPIConnectionProperties
{
ConnectionString = "https://api.company.internal/v2",
AuthType = "OAuth2",
ClientId = "company-app-client-id",
ClientSecret = "company-app-client-secret",
TokenUrl = "https://auth.company.internal/oauth/token",
GrantType = "client_credentials"
};
Usage Example
// Get employee directory
var employees = webApiDS.GetEntity("Employees", null);
// Get projects with filtering
var projectFilters = new List<AppFilter> {
new AppFilter {FieldName = "status", FilterValue = "active", Operator = "=" },
new AppFilter {FieldName = "department", FilterValue = "engineering", Operator = "=" }
};
var projects = webApiDS.GetEntity("Projects", projectFilters);
// Submit timesheet entry
var timesheetEntry = new {
employee_id = "12345",
project_id = "PROJ-001",
hours = 8.0,
date = DateTime.Today.ToString("yyyy-MM-dd"),
description = "Feature development"
};
var submitResult = webApiDS.InsertEntity("Timesheets", timesheetEntry);
RESTful CRUD Operations
// Full CRUD example with a generic REST API
// CREATE - Insert new record
var newRecord = new {
name = "John Doe",
email = "john.doe@example.com",
department = "Engineering"
};
var insertResult = webApiDS.InsertEntity("Users", newRecord);
// READ - Get all records
var allUsers = webApiDS.GetEntity("Users", null);
// READ - Get with filtering
var filters = new List<AppFilter> {
new AppFilter {FieldName = "department", FilterValue = "Engineering", Operator = "=" }
};
var engineers = webApiDS.GetEntity("Users", filters);
// READ - Get single record by ID
var userFilters = new List<AppFilter> {
new AppFilter {FieldName = "id", FilterValue = "123", Operator = "=" }
};
var user = webApiDS.GetEntity("Users", userFilters);
// UPDATE - Modify existing record
var updatedUser = new {
id = 123,
name = "John Smith",
email = "john.smith@example.com",
department = "Engineering"
};
var updateResult = webApiDS.UpdateEntity("Users", updatedUser);
// DELETE - Remove record
var deleteResult = webApiDS.DeleteEntity("Users", updatedUser);
Advanced Features
Circuit Breaker Pattern
The WebAPIDataSource automatically implements circuit breaker functionality:
// Circuit breaker automatically handles failures
// - Closed: Normal operation
// - Open: Failing fast after repeated failures
// - Half-Open: Testing recovery
// The circuit breaker is automatically managed by WebAPIErrorHelper
// and integrated into all HTTP requests via WebAPIRequestHelper
Performance Optimization
// High-performance configuration for heavy workloads
var performanceConnection = new WebAPIConnectionProperties
{
ConnectionName = "High Performance API",
ConnectionString = "https://api.high-volume.com/v1",
AuthType = AuthTypeEnum.ApiKey,
ApiKey = "your-high-perf-key",
// Aggressive performance settings
TimeoutMs = 10000, // Quick timeout for fast APIs
MaxRetries = 1, // Minimal retries for speed
RetryIntervalMs = 500, // Fast retry
MaxConcurrentRequests = 50, // High concurrency
// Optimized caching
EnableCaching = true,
CacheExpiryMinutes = 5, // Short cache for fresh data
// Aggressive rate limiting
EnableRateLimit = true,
RateLimitRequestsPerMinute = 1000,
// Compression for bandwidth
EnableCompression = true,
// Minimal response processing
ResponseFormat = "json",
DataPath = "$", // Direct response, no nesting
// Large page sizes
DefaultPageSize = 500,
MaxPageSize = 1000,
UserAgent = "HighPerf-Client/1.0"
};
// Minimal headers for performance
performanceConnection.Headers.Clear();
performanceConnection.Headers.Add(new WebApiHeader { Headername = "Accept", Headervalue = "application/json" });
performanceConnection.Headers.Add(new WebApiHeader { Headername = "Accept-Encoding", Headervalue = "gzip, deflate" });
Configuration Validation and Error Handling
// Comprehensive validation example
public static WebAPIConnectionProperties CreateValidatedConnection(string configName)
{
var connection = new WebAPIConnectionProperties();
try
{
// Load configuration from various sources
connection.ConnectionName = configName;
connection.ConnectionString = Configuration.GetConnectionString(configName);
connection.AuthType = Enum.Parse<AuthTypeEnum>(Configuration[$"{configName}:AuthType"]);
// Set auth-specific properties
switch (connection.AuthType)
{
case AuthTypeEnum.ApiKey:
connection.ApiKey = Configuration[$"{configName}:ApiKey"];
connection.ApiKeyHeader = Configuration[$"{configName}:ApiKeyHeader"] ?? "X-API-Key";
break;
case AuthTypeEnum.Basic:
connection.UserID = Configuration[$"{configName}:Username"];
connection.Password = Configuration[$"{configName}:Password"];
break;
case AuthTypeEnum.OAuth2:
connection.ClientId = Configuration[$"{configName}:ClientId"];
connection.ClientSecret = Configuration[$"{configName}:ClientSecret"];
connection.TokenUrl = Configuration[$"{configName}:TokenUrl"];
connection.Scope = Configuration[$"{configName}:Scope"];
break;
}
// Apply environment-specific settings
var environment = Configuration["Environment"];
if (environment == "Production")
{
connection.TimeoutMs = 60000;
connection.MaxRetries = 3;
connection.ValidateServerCertificate = true;
}
else if (environment == "Development")
{
connection.TimeoutMs = 30000;
connection.MaxRetries = 1;
connection.IgnoreSSLErrors = true; // Only for dev!
}
// Validate the configuration
if (!connection.ValidateConfiguration())
{
var errors = connection.GetValidationErrors();
throw new ConfigurationException($"Invalid {configName} configuration: {errors}");
}
Logger.Information($"Successfully configured {configName} connection");
return connection;
}
catch (Exception ex)
{
Logger.Error($"Failed to create {configName} connection: {ex.Message}");
throw;
}
}
// Usage
var githubConnection = CreateValidatedConnection("GitHub");
var stripeConnection = CreateValidatedConnection("Stripe");
Custom Authentication
// Implement custom authentication by extending WebAPIAuthenticationHelper
public class CustomAuthHelper : WebAPIAuthenticationHelper
{
public override async Task<bool> EnsureAuthenticatedAsync()
{
// Custom authentication logic
var token = await GetCustomToken();
// Set authentication headers
return true;
}
}
Best Practices
Γ’Ε‘Β Γ―ΒΈΒ Security Considerations
- Never store API keys or secrets in plain text configuration files
- Use environment variables or secure credential stores
- Implement proper token refresh logic for long-running applications
- Validate SSL certificates for production environments
β Performance Optimization
- Enable caching for frequently accessed data
- Use pagination for large datasets
- Configure appropriate timeouts and retry policies
- Monitor rate limits and implement backoff strategies
Error Handling
// Always check error status after operations
var result = webApiDS.InsertEntity("Users", userData);
if (result.Flag == Errors.Failed)
{
Logger.WriteLog($"Operation failed: {result.Message}");
// Handle error appropriately
}
// Use try-catch for comprehensive error handling
try
{
var data = webApiDS.GetEntity("Users", filters);
}
catch (HttpRequestException ex)
{
Logger.WriteLog($"HTTP Error: {ex.Message}");
// Handle network errors
}
catch (Exception ex)
{
Logger.WriteLog($"Unexpected error: {ex.Message}");
// Handle other errors
}
Connection Management
// Always open connection before use
if (webApiDS.ConnectionStatus != ConnectionState.Open)
{
var result = webApiDS.Openconnection();
if (result == ConnectionState.Open)
{
// Connection successful
}
}
// Close connection when done
webApiDS.Closeconnection();
// Use using statement for automatic disposal
using (var ds = new WebAPIDataSource(name, logger, editor, type, errors))
{
// Use data source
} // Automatically disposed
Troubleshooting
Common Issues
Troubleshooting Guide
// Comprehensive troubleshooting helper
public static class WebAPITroubleshooter
{
public static void DiagnoseConnection(WebAPIConnectionProperties connection)
{
Console.WriteLine("=== WebAPI Connection Diagnostics ===");
// Basic validation
Console.WriteLine($"Connection Name: {connection.ConnectionName ?? "Not Set"}");
Console.WriteLine($"Connection String: {connection.ConnectionString ?? "Not Set"}");
Console.WriteLine($"Auth Type: {connection.AuthType}");
// Validate configuration
if (connection.ValidateConfiguration())
{
Console.WriteLine("Γ’Εβ Configuration validation passed");
}
else
{
Console.WriteLine($"Γ’Εβ Configuration errors: {connection.GetValidationErrors()}");
}
// Check authentication setup
CheckAuthentication(connection);
// Check network settings
CheckNetworkSettings(connection);
// Check performance settings
CheckPerformanceSettings(connection);
}
private static void CheckAuthentication(WebAPIConnectionProperties connection)
{
Console.WriteLine("\n--- Authentication Check ---");
switch (connection.AuthType)
{
case AuthTypeEnum.None:
Console.WriteLine("Γ’Εβ No authentication required");
break;
case AuthTypeEnum.ApiKey:
Console.WriteLine($"API Key: {(!string.IsNullOrEmpty(connection.ApiKey) ? "Set" : "Γ’Εβ Not Set")}");
Console.WriteLine($"API Key Header: {connection.ApiKeyHeader}");
break;
case AuthTypeEnum.Basic:
Console.WriteLine($"Username: {(!string.IsNullOrEmpty(connection.UserID) ? "Set" : "Γ’Εβ Not Set")}");
Console.WriteLine($"Password: {(!string.IsNullOrEmpty(connection.Password) ? "Set" : "Γ’Εβ Not Set")}");
break;
case AuthTypeEnum.Bearer:
Console.WriteLine($"Bearer Token: {(!string.IsNullOrEmpty(connection.ApiKey) ? "Set" : "Γ’Εβ Not Set")}");
break;
case AuthTypeEnum.OAuth2:
Console.WriteLine($"Client ID: {(!string.IsNullOrEmpty(connection.ClientId) ? "Set" : "Γ’Εβ Not Set")}");
Console.WriteLine($"Client Secret: {(!string.IsNullOrEmpty(connection.ClientSecret) ? "Set" : "Γ’Εβ Not Set")}");
Console.WriteLine($"Token URL: {connection.TokenUrl ?? "Γ’Εβ Not Set"}");
Console.WriteLine($"Grant Type: {connection.GrantType}");
Console.WriteLine($"Scope: {connection.Scope ?? "Not Set"}");
break;
}
}
private static void CheckNetworkSettings(WebAPIConnectionProperties connection)
{
Console.WriteLine("\n--- Network Settings ---");
Console.WriteLine($"Timeout: {connection.TimeoutMs}ms");
Console.WriteLine($"Max Retries: {connection.MaxRetries}");
Console.WriteLine($"Retry Interval: {connection.RetryIntervalMs}ms");
Console.WriteLine($"SSL Validation: {(connection.ValidateServerCertificate ? "Enabled" : "Disabled")}");
Console.WriteLine($"Ignore SSL Errors: {(connection.IgnoreSSLErrors ? "Yes" : "No")}");
if (connection.UseProxy)
{
Console.WriteLine($"Proxy: {connection.ProxyUrl}:{connection.ProxyPort}");
Console.WriteLine($"Proxy Credentials: {(connection.UseDefaultProxyCredentials ? "Default" : "Custom")}");
}
else
{
Console.WriteLine("Proxy: Not Used");
}
}
private static void CheckPerformanceSettings(WebAPIConnectionProperties connection)
{
Console.WriteLine("\n--- Performance Settings ---");
Console.WriteLine($"Caching: {(connection.EnableCaching ? "Enabled" : "Disabled")}");
if (connection.EnableCaching)
{
Console.WriteLine($"Cache Expiry: {connection.CacheExpiryMinutes} minutes");
}
Console.WriteLine($"Rate Limiting: {(connection.EnableRateLimit ? "Enabled" : "Disabled")}");
if (connection.EnableRateLimit)
{
Console.WriteLine($"Rate Limit: {connection.RateLimitRequestsPerMinute} requests/minute");
}
Console.WriteLine($"Max Concurrent Requests: {connection.MaxConcurrentRequests}");
Console.WriteLine($"Compression: {(connection.EnableCompression ? "Enabled" : "Disabled")}");
Console.WriteLine($"Default Page Size: {connection.DefaultPageSize}");
Console.WriteLine($"Max Page Size: {connection.MaxPageSize}");
Console.WriteLine($"\nHeaders ({connection.Headers.Count}):");
foreach (var header in connection.Headers)
{
Console.WriteLine($" {header.Headername}: {header.Headervalue}");
}
}
}
// Usage
var connection = new WebAPIConnectionProperties { /* your config */ };
WebAPITroubleshooter.DiagnoseConnection(connection);
Schema Inference Problems
// Force schema refresh
var structure = webApiDS.GetEntityStructure("EntityName", true);
// Manually define schema if inference fails
var customStructure = new EntityStructure
{
EntityName = "CustomEntity",
Fields = new List<EntityField> {
new EntityField {FieldName = "id", Fieldtype = "System.Int32" },
new EntityField {FieldName = "name", Fieldtype = "System.String" }
}
};
API Reference
Class: WebAPIDataSource
public partial class WebAPIDataSource : IWebAPIDataSource, IDisposable β 20 partial files, 34 implemented methods across 8 files (12 partials are stubs for future implementation).
Properties
| Property | Type | Default | Description |
|---|---|---|---|
ColumnDelimiter | string | " | Delimiter used to separate columns |
ParameterDelimiter | string | ":" | Delimiter used for parameters |
GuidID | string | Guid.NewGuid() | Unique identifier |
DatasourceType | DataSourceType | WebApi | Type of data source |
Category | DatasourceCategory | WEBAPI | Category of data source |
Dataconnection | IDataConnection | β | Data connection interface |
DatasourceName | string | β | Name of the data source |
ErrorObject | IErrorsInfo | β | Error handling object |
Id | string | β | Secondary identifier |
Logger | IDMLogger | β | Logger instance |
EntitiesNames | List<string> | new() | Entity name list |
Entities | List<EntityStructure> | new() | Entity structures |
DMEEditor | IDMEEditor | β | DME editor reference |
ConnectionStatus | ConnectionState | Closed | Current connection state |
ApiKey | string | β | API key for auth |
Resource | string | β | Resource endpoint |
Parameters | Dictionary<string,string> | new() | Request parameters |
Event
event EventHandler<PassedArgs> PassEventβ Raised when a specific action or event is passed
Constructor
WebAPIDataSource(string datasourcename, IDMLogger logger, IDMEEditor dmeEditor, DataSourceType databasetype, IErrorsInfo errorObject)
Initializes helpers (WebAPIConfigurationHelper, WebAPIAuthenticationHelper, WebAPIRequestHelper, WebAPICacheHelper, WebAPIDataHelper, WebAPIRateLimitHelper, WebAPISchemaHelper, WebAPIErrorHelper), creates shared HttpClient, loads saved connection from ConfigEditor.
Implemented Methods by Partial File
Connection.cs (5 methods)
| Method | Return | Description |
|---|---|---|
Openconnection() | ConnectionState | Opens connection, ensures authentication via _authHelper.EnsureAuthenticatedAsync() |
Closeconnection() | ConnectionState | Closes the connection, sets state to Closed |
BeginTransaction(PassedArgs args) | IErrorsInfo | Begins a transaction (stub) |
EndTransaction(PassedArgs args) | IErrorsInfo | Ends a transaction (stub) |
Commit(PassedArgs args) | IErrorsInfo | Commits a transaction (stub) |
Data.cs (7 methods β core CRUD)
| Method | Return | Description |
|---|---|---|
GetEntity(string, List<AppFilter>) | IEnumerable<object> | Retrieves entities with optional filters. Delegates to GetEntityAsync |
GetEntity(string, List<AppFilter>, int, int) | PagedResult | Paged entity retrieval with page number and page size |
GetEntityAsync(string, List<AppFilter>) | Task<IEnumerable<object>> | Async entity retrieval with endpoint resolution, rate limiting, and caching |
InsertEntity(string, object) | IErrorsInfo | Creates a new record via POST |
UpdateEntity(string, object) | IErrorsInfo | Updates a record via PUT |
DeleteEntity(string, object) | IErrorsInfo | Deletes a record via DELETE |
UpdateEntities(string, object, IProgress<PassedArgs>) | IErrorsInfo | Batch update with progress reporting |
Http.cs (11 methods β HTTP convenience layer)
| Method | Return | Description |
|---|---|---|
GetAsync(string, Dictionary?, Dictionary?, CancellationToken) | Task<HttpResponseMessage> | HTTP GET with optional query params and headers |
GetAsync<T>(string, Dictionary?, Dictionary?, CancellationToken) | Task<T> | HTTP GET with deserialization to T |
PostAsync(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<HttpResponseMessage> | HTTP POST with JSON body |
PostAsync<T>(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<T> | HTTP POST with deserialization |
PutAsync(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<HttpResponseMessage> | HTTP PUT with JSON body |
PutAsync<T>(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<T> | HTTP PUT with deserialization |
PatchAsync(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<HttpResponseMessage> | HTTP PATCH with JSON body |
PatchAsync<T>(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<T> | HTTP PATCH with deserialization |
DeleteAsync(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<HttpResponseMessage> | HTTP DELETE with optional query params |
DeleteAsync<T>(string, object?, Dictionary?, Dictionary?, CancellationToken) | Task<T> | HTTP DELETE with deserialization |
All HTTP methods auto-apply auth headers via _authHelper.AddAuthenticationHeaders(), ensure auth state, and add default headers from config. The private helper BuildUrlWithQuery(string, Dictionary) constructs URLs with query string parameters.
Structure.cs (11 methods β schema & entity structure)
| Method | Return | Description |
|---|---|---|
GetEntitesList() | IEnumerable<string> | Returns configured entity names from endpoint config |
CheckEntityExist(string) | bool | Checks if an entity is configured |
GetEntityIdx(string) | int | Gets index of entity in Entities list |
GetEntityStructure(string, bool) | EntityStructure | Gets entity schema, optionally from API response inference |
GetEntityStructure(EntityStructure, bool) | EntityStructure | Overload accepting EntityStructure object |
GetEntityType(string) | Type | Gets CLR type for entity |
GetEntityforeignkeys(string, string) | IEnumerable<RelationShipKeys> | Gets foreign key relationships (stub) |
GetChildTablesList(string, string, string) | IEnumerable<ChildRelation> | Gets child table list (stub) |
CreateEntityAs(EntityStructure) | bool | Creates entity definition |
Query.cs (4 methods)
| Method | Return | Description |
|---|---|---|
RunQuery(string) | IEnumerable<object> | Executes a query string (delegates to HTTP GET) |
GetScalar(string) | double | Executes scalar query |
GetScalarAsync(string) | Task<double> | Async scalar query |
ExecuteSql(string) | IErrorsInfo | Executes SQL-like command |
Scripting.cs (3 methods)
| Method | Return | Description |
|---|---|---|
RunScript(ETLScriptDet) | IErrorsInfo | Runs an ETL script |
GetCreateEntityScript(List<EntityStructure>?) | IEnumerable<ETLScriptDet> | Generates create-entity script |
CreateEntities(List<EntityStructure>) | IErrorsInfo | Creates entities from script |
WebAPI.cs (1 method)
| Method | Return | Description |
|---|---|---|
ReadData(bool, int, int) | Task<IEnumerable<object>> | IWebAPIDataSource-specific data reading with header flag and line range |
Empty / Stub Partial Files (12 files)
The following partial class files exist for future implementation but currently contain no method bodies:
WebAPIDataSource.CoreInterface.csWebAPIDataSource.CoreInterface.Fixed.csWebAPIDataSource.Interface.csWebAPIDataSource.DataOperations.csWebAPIDataSource.EntityMethods.csWebAPIDataSource.QueryMethods.csWebAPIDataSource.PagingAsync.csWebAPIDataSource.Bulk.csWebAPIDataSource.BulkOperations.csWebAPIDataSource.ScriptTransactions.csWebAPIDataSource.Complete.csWebAPIDataSource.Main.cs
Class: WebAPIConnectionProperties
public class WebAPIConnectionProperties : IConnectionProperties β 707 lines, 50+ properties with validation.
Core Connection Properties
| Property | Type | Default |
|---|---|---|
ID | int | 0 |
GuidID | string | Guid.NewGuid() |
ConnectionName | string | null |
ConnectionString | string | null |
Database | string | null |
DatabaseType | DataSourceType | WebApi |
Category | DatasourceCategory | WEBAPI |
DriverName | string | null |
DriverVersion | string | null |
Host | string | null |
Port | int | 0 |
UserID | string | null |
Password | string | null |
SchemaName | string | null |
Parameters | string | null |
Headers | List<WebApiHeader> | new() (defaults: Accept, User-Agent) |
Entities | List<EntityStructure> | new() |
Web API Specific Properties
| Property | Type | Default | Description |
|---|---|---|---|
ApiVersion | string | "v1.0" | API version string |
AuthType | AuthTypeEnum | None | Authentication mode (triggers UpdateAuthenticationRequirements()) |
ClientId | string | null | OAuth2 client ID |
ClientSecret | string | null | OAuth2 client secret |
AuthUrl | string | null | OAuth2 authorization URL |
TokenUrl | string | null | OAuth2 token endpoint |
Scope | string | null | OAuth2 scope |
GrantType | string | "client_credentials" | OAuth2 grant type |
ApiKeyHeader | string | "X-API-Key" | Header name for API key auth |
ApiKey | string | null | API key / bearer token value |
RedirectUri | string | null | OAuth2 redirect URI |
AuthCode | string | null | OAuth2 authorization code |
BearerToken | string | null | Static bearer token |
AccessToken | string | maps to ApiKey | Instagram compat alias |
AppId | string | maps to ClientId | Instagram compat alias |
AppSecret | string | maps to ClientSecret | Instagram compat alias |
ConsumerKey | string | maps to ClientId | Twitter/OAuth compat alias |
ConsumerSecret | string | maps to ClientSecret | Twitter/OAuth compat alias |
Configuration Settings
| Property | Type | Default | Range |
|---|---|---|---|
TimeoutMs | int | 30000 | β₯ 1000 |
MaxRetries | int | 3 | β₯ 0 |
RetryIntervalMs | int | 1000 | β₯ 0 |
RetryCount | int | 3 | β₯ 0 |
RetryDelayMs | int | 1000 | β₯ 0 |
CacheExpiryMinutes | int | 15 | β₯ 1 |
MaxConcurrentRequests | int | 10 | β₯ 1 |
EnableCaching | bool | true | β |
EnableCompression | bool | true | β |
UserAgent | string | "BeepDM-WebAPI/1.0" | β |
RequiresAuthentication | bool | false | Set by AuthType |
RequiresTokenRefresh | bool | false | true for Bearer/OAuth2 |
Proxy Settings
| Property | Type | Default |
|---|---|---|
UseProxy | bool | false |
ProxyUrl | string | null |
ProxyPort | int | 0 |
ProxyUser | string | null |
ProxyPassword | string | null |
BypassProxyOnLocal | bool | false |
UseDefaultProxyCredentials | bool | false |
SSL / Certificate Settings
| Property | Type | Default |
|---|---|---|
IgnoreSSLErrors | bool | false |
ValidateServerCertificate | bool | true |
ClientCertificatePath | string | null |
ClientCertificatePassword | string | null |
CertificatePath | string | null |
Rate Limiting
| Property | Type | Default |
|---|---|---|
RateLimitRequestsPerMinute | int | 60 |
EnableRateLimit | bool | true |
Response Handling
| Property | Type | Default | Description |
|---|---|---|---|
ResponseFormat | string | "json" | Expected format: json, xml, csv, text |
DataPath | string | null | JSONPath/XPath to extract data |
TotalCountPath | string | null | JSONPath/XPath for total count |
Pagination Settings
| Property | Type | Default |
|---|---|---|
PageNumberParameter | string | "page" |
PageSizeParameter | string | "limit" |
DefaultPageSize | int | 100 |
MaxPageSize | int | 1000 |
Methods
| Method | Return | Description |
|---|---|---|
ValidateConfiguration() | bool | Validates required properties for the current AuthType |
GetValidationErrors() | string | Returns detailed error messages for missing configuration |
GetParameterValue(string) | string? | Gets a parameter from the Parameters string by name |
SetParameterValue(string, string) | void | Sets a parameter in the Parameters string |
AuthTypeEnum
| Value | Requires | Token Refresh |
|---|---|---|
None | β | No |
ApiKey | ApiKey | No |
Basic | UserID + Password | No |
Bearer | KeyToken or ApiKey | Yes |
OAuth2 | ClientId + ClientSecret + TokenUrl | Yes |
Class: WebAPIDataConnection
public class WebAPIDataConnection : IDataConnection β 643 lines, full connection management.
Properties
| Property | Type | Description |
|---|---|---|
ConnectionProp | IConnectionProperties | Connection properties (cast to WebAPIConnectionProperties via WebAPIProperties) |
ConnectionStatus | ConnectionState | Current state (default: Closed) |
Logger | IDMLogger | Logger instance |
ErrorObject | IErrorsInfo | Error information |
DMEEditor | IDMEEditor | DME editor reference |
Id | string | Unique identifier (Guid) |
ConnectionString | string | Builds from properties / parses into properties |
DatabaseType | DataSourceType | WebApi |
WebAPIProperties | WebAPIConnectionProperties? | Cast accessor for ConnectionProp |
Methods
| Method | Return | Description |
|---|---|---|
OpenConnection() | ConnectionState | Opens connection with current properties (validates + tests URL) |
OpenConnection(IConnectionProperties) | ConnectionState | Opens connection with specific properties (normalizes to WebAPIConnectionProperties) |
OpenConnection(DataSourceType, string) | ConnectionState | Opens connection from connection string |
OpenConnection(DataSourceType, string, int, string, string, string, string) | ConnectionState | Opens connection with individual parameters |
CloseConn() | ConnectionState | Closes connection, sets state to Closed |
ReplaceValueFromConnectionString() | string | Legacy method, replaces {Host}/{UserID} etc. placeholders |
Static method:
static NormalizeConnectionProperties(IConnectionProperties)β Converts anyIConnectionPropertiestoWebAPIConnectionProperties, preserving all mapped fields
Helper Classes β Full API Reference
All helpers reside in TheTechIdea.Beep.WebAPI.Helpers namespace under WebAPI/Helpers/.
WebAPIConfigurationHelper
public class WebAPIConfigurationHelper : IDisposable (813 lines)
Properties: BaseUrl, ApiVersion, AuthenticationType, TimeoutMs, MaxRetries, RetryDelayMs, CacheEnabled, CacheDurationMinutes, RateLimitEnabled, RequestsPerSecond, PagingEnabled, PageSize, MaxConcurrentRequests β all read-only, driven from WebAPIConnectionProperties
Methods:
GetConfigValue<T>(string key, T defaultValue)β Generic config value retrieval with cachingSetConfigValue(string key, object value)β Sets config and updates connection propertiesGetHeaders()β Returns HTTP headers dict from configGetEndpointConfiguration(string entityName)β ReturnsEndpointConfiguration(Get/Post/Put/Delete/List endpoints, method, auth, cache, rate limit)GetAuthenticationConfiguration()β Returns theWebAPIConnectionPropertieswith auth settingsGetPaginationConfiguration()β ReturnsPaginationConfiguration(page/size/offset/limit param names, style)ValidateConfiguration()β ReturnsConfigurationValidationResult(BaseUrl, auth, timeout, retry, rate limit, pagination checks)
Nested types: EndpointConfiguration, ConfigurationValidationResult, PaginationConfiguration
WebAPIAuthenticationHelper
public class WebAPIAuthenticationHelper : IDisposable (239 lines)
Methods:
EnsureAuthenticatedAsync()β Validates auth state, triggers OAuth2 token refresh if neededAddAuthenticationHeaders(HttpRequestMessage)β Injects auth headers: Bearer token, API Key (custom header name), Basic (base64 credentials)
Auth flow: Parses AuthType from Parameters string. Supports three OAuth2 grant types: client_credentials, password, authorization_code. Token caching with thread-safe refresh (_tokenLock + _tokenExpiry). Static bearer token fallback chain: KeyToken β BearerToken β ApiKey β Parameters.
WebAPIRequestHelper
public class WebAPIRequestHelper : IDisposable (103 lines)
Methods:
SendWithRetryAsync(HttpRequestMessage, string? operationName)β Executes HTTP request with semaphore-based concurrency control, delegates retry/backoff toWebAPIErrorHelperSendWithRetryAsync<T>(HttpRequestMessage, Func<HttpResponseMessage, Task<T>>, string? operationName)β Typed version with custom response handler
Behavior: Uses SemaphoreSlim(maxConcurrentRequests) for concurrency. Clones requests for retry safety via CloneHttpRequestMessageAsync (copies headers, content, version).
WebAPIDataHelper
public class WebAPIDataHelper (477 lines, no IDisposable)
URL building:
BuildEndpointUrl(baseUrl, endpoint, filters?, pageNumber?, pageSize?)β Full URL with query paramsBuildEntityEndpoint(EntityStructure, filters?, pageNumber?, pageSize?)β Entity endpoint from structureBuildEntityEndpoint(entityName, id)β Endpoint with ID path segment
Response parsing:
ProcessApiResponse(jsonResponse, entityStructure)β JSON toIEnumerable<object>(handles array, object, and nested data fields)GetTotalRecordsFromResponse(jsonResponse, headers?)β Extracts total count from X-Total-Count header or JSON fields (total, totalCount, count, etc.)ParseEntitiesFromDiscoveryResponse(jsonResponse)β Parses entity discovery JSON
Data utilities:
CreateRequest(method, endpoint, data?)β CreatesHttpRequestMessagewith camelCase JSON bodyAddCustomHeaders(request, headers)β Adds custom headers (with content-header fallback)ExtractIdValue(object data)β Extracts ID from common field names (id, Id, ID, _id, uuid, key)InferEntityStructureFromSample(entityName, JsonElement)β Infers schema from sample dataGenerateCacheKey(baseKey, filters?, pageNumber, pageSize)β Cache key generation
WebAPICacheHelper
public class WebAPICacheHelper : IDisposable (203 lines)
Storage: ConcurrentDictionary<string, CacheEntry> with background cleanup timer (every 5 min).
Methods:
GenerateCacheKey(operation, params[])β Key format:{datasource}:{operation}:{params}Get<T>(cacheKey)β Retrieves cached value (null if expired/missing)Set<T>(cacheKey, value, ttl?)β Stores with optional TTL (default from constructor)Remove(cacheKey)β Removes specific entryRemoveByPattern(pattern)β Removes entries matching substringClear()β Clears all entriesGetStatistics()β ReturnsCacheStatistics(total/valid/expired entries, efficiency %)GetOrSetAsync<T>(cacheKey, factory, ttl?)β Get-or-set pattern with async factory
Nested type: CacheStatistics
WebAPIRateLimitHelper
public class WebAPIRateLimitHelper : IDisposable (293 lines)
Algorithm: Token bucket (per-endpoint buckets in ConcurrentDictionary). Background cleanup of stale buckets.
Properties: DefaultRequestsPerSecond (10), DefaultBurstCapacity (20), CleanupIntervalMinutes (5)
Methods:
WaitForPermissionAsync(endpoint, requestsPerSecond?, burstCapacity?, CancellationToken)β Waits until a token is available (delays if rate limited)CanMakeRequest(endpoint, requestsPerSecond?, burstCapacity?)β Non-blocking checkGetStatus(endpoint)β ReturnsRateLimitStatus(tokens available, refill rate, next refill time)
Nested type: RateLimitStatus (Endpont, TokensAvailable, RefillRate, NextRefillTime)
WebAPISchemaHelper
public class WebAPISchemaHelper : IDisposable (621 lines)
Properties: SchemaCacheExpirationMinutes (60), MaxNestingDepth (5), MinSampleSize (3)
Methods:
InferEntityStructureAsync(entityName)β Auto-discovers schema by fetching sample data from entity endpointInferSchemaFromJsonAsync(entityName, jsonData, forceRefresh?)β Infers schema from JSON samples (up to 50)ValidateSchemaAsync(EntityStructure, jsonData)β Validates schema against actual data (field existence, type consistency)UpdateSchemaAsync(EntityStructure, jsonData)β Re-infers schema preserving existing field metadata (IsKey, IsUnique, etc.)TryGetCachedSchema(entityName, out EntityStructure)β Cache lookup with expiration checkClearCache()β Clears cached schemas
Nested type: SchemaValidationResult (EntityName, IsValid, Issues, SuggestedChanges)
WebAPIErrorHelper
public class WebAPIErrorHelper : IDisposable (634 lines)
Properties: DefaultMaxRetries (3), DefaultBaseDelayMs (1000), DefaultFailureThreshold (5), DefaultRecoveryTimeoutSeconds (60)
Methods:
ExecuteWithRetryAsync<T>(operation, operationName, maxRetries?, baseDelayMs?, CancellationToken)β Executes with exponential backoff + jitter, circuit breaker protectionHandleErrorResponseAsync(HttpResponseMessage)β Logs categorized error from HTTP responseCategorizeErrorAsync(HttpResponseMessage)β ReturnsWebApiError(status code, category, message, content, timestamp)AnalyzeError(Exception)β ReturnsErrorAnalysisResultwith category, retryability, suggested actionGetCircuitBreakerStatus(operationName)β Returns current circuit breaker stateResetCircuitBreaker(operationName)β Manually resets circuit breaker
Error categories: WebApiErrorCategory (ClientError, AuthenticationError, AuthorizationError, ServerError, NetworkError, Unknown), ErrorCategory (Network, Authentication, Configuration, Timeout, Logic, Unknown)
Circuit breaker states: Closed β Open (after FailureThreshold failures) β HalfOpen (after RecoveryTimeout) β Closed (on success). Per-operation instances.
WebAPIInterfaces
File: WebAPIInterfaces.cs (35 lines)
IWebAPISchemaHelper (extends IDisposable):
DiscoverEntitiesAsync(HttpClient, ConnectionProperties)βTask<List<EntityStructure>>InferEntityStructureAsync(entityName, HttpClient, ConnectionProperties)βTask<EntityStructure>GetEntityType(entityName)βTypeValidateEntityStructure(EntityStructure)βboolEntityExistsAsync(entityName)βTask<bool>
IWebAPIDataHelper (extends IDisposable):
CreateDynamicType(entityName)βTypeParseJsonToSingleObject(string)βobjectParseJsonToObjects(string)βList<object>TransformToJsonString(object)βstringValidateData(object, string)βDataValidationResult
DataValidationResult: IsValid (bool), Errors (List<string>)
Migration Guide
From Custom HTTP Clients
// Before: Manual HTTP client usage
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync("https://api.example.com/users");
var content = await response.Content.ReadAsStringAsync();
// Manual JSON parsing and error handling...
}
// After: WebAPIDataSource usage
var users = webApiDS.GetEntity("Users", null);
// Automatic authentication, error handling, caching, and parsing
From Database DataSources
// Database-style operations work seamlessly
var filters = new List<AppFilter> {
new AppFilter {FieldName = "status", FilterValue = "active", Operator = "=" }
};
var activeUsers = webApiDS.GetEntity("Users", filters); // Just like RDBSource!
// CRUD operations
webApiDS.InsertEntity("Users", newUser);
webApiDS.UpdateEntity("Users", updatedUser);
webApiDS.DeleteEntity("Users", userToDelete);
HTTP Convenience Methods
In addition to the IDataSource CRUD interface, WebAPIDataSource
exposes low-level HTTP helpers defined in WebAPIDataSource.Http.cs.
These are the preferred way to call API-specific endpoints that don't map cleanly to
entity operations, and they are the building blocks used by connector subclasses.
GET — raw response or deserialized
// Raw HttpResponseMessage
HttpResponseMessage resp = await ds.GetAsync(
"users/me",
query: new Dictionary<string, string> { ["fields"] = "id,name,email" },
headers: new Dictionary<string, string> { ["Accept-Language"] = "en" }
);
// Deserialize directly to T
var me = await ds.GetAsync<UserProfile>("users/me");
// Returns default(T) on failure β never throws
POST / PUT / DELETE
// POST with a body
await ds.PostAsync("tweets", body: new { text = "Hello from Beep!" });
// PUT (full replace)
await ds.PutAsync($"users/{userId}", body: updatedUser);
// DELETE
await ds.DeleteAsync($"tweets/{tweetId}");
All four methods call _authHelper.EnsureAuthenticatedAsync() and
_authHelper.AddAuthenticationHeaders(request) before sending, and
apply the default headers from WebAPIConnectionProperties.
Retry logic is handled by WebAPIRequestHelper.SendWithRetryAsync.
If endpointOrUrl starts with http it is used as-is.
Otherwise it is combined with _configHelper.BaseUrl (the
ConnectionString value).
Building a Custom API Connector
The recommended way to integrate a specific REST API — Twitter, GitHub, Stripe, any social
or SaaS platform — is to subclass WebAPIDataSource and override the methods
that need API-specific URL or response handling. The TwitterDataSource is the
canonical reference implementation.
Connector Pattern
Minimum viable connector skeleton
// 1. Annotate with [AddinAttribute] so AssemblyHandler registers it automatically
[AddinAttribute(
Category = DatasourceCategory.Connector,
DatasourceType = DataSourceType.Twitter // use the correct enum value for your API
)]
public class MyApiDataSource : WebAPIDataSource
{
// 2. Map logical entity names to API endpoint paths
private static readonly Dictionary<string, string> EntityEndpoints
= new(StringComparer.OrdinalIgnoreCase)
{
["items.list"] = "items",
["items.by_id"] = "items/{id}",
["items.search"] = "items/search"
};
// 3. Declare which AppFilter keys are required for each entity
private static readonly Dictionary<string, string[]> RequiredFilters
= new(StringComparer.OrdinalIgnoreCase)
{
["items.by_id"] = new[] { "id" },
["items.search"] = new[] { "query" }
};
public MyApiDataSource(
string datasourcename, IDMLogger logger,
IDMEEditor dmeEditor, DataSourceType databasetype, IErrorsInfo errorObject)
: base(datasourcename, logger, dmeEditor, databasetype, errorObject)
{
EntitiesNames = EntityEndpoints.Keys.ToList();
Entities = EntitiesNames
.Select(n => new EntityStructure { EntityName = n, DatasourceEntityName = n })
.ToList();
}
// 4. Override GetEntityAsync to drive URL resolution and response extraction
public override async Task<IEnumerable<object>> GetEntityAsync(
string EntityName, List<AppFilter> Filter)
{
if (!EntityEndpoints.TryGetValue(EntityName, out var endpoint))
throw new InvalidOperationException($"Unknown entity '{EntityName}'");
var q = FiltersToQuery(Filter);
RequireFilters(EntityName, q,
RequiredFilters.GetValueOrDefault(EntityName, Array.Empty<string>()));
var resolvedEndpoint = ResolveEndpoint(endpoint, q);
using var resp = await GetAsync(resolvedEndpoint, q);
if (resp is null || !resp.IsSuccessStatusCode) return Array.Empty<object>();
return ExtractArray(resp, "data"); // key in JSON root where items live
}
// 5. Expose sync overload
public override IEnumerable<object> GetEntity(string EntityName, List<AppFilter> filter)
=> GetEntityAsync(EntityName, filter).GetAwaiter().GetResult() ?? Array.Empty<object>();
}
| Method | What it does |
|---|---|
FiltersToQuery(filters) | Converts List<AppFilter> to Dictionary<string,string> query-string map |
RequireFilters(name, q, keys) | Throws if any mandatory keys are absent from the query map |
ResolveEndpoint(template, q) | Substitutes {param} placeholders from query map and removes consumed keys |
ExtractArray(response, key) | Reads the JSON body and returns List<object> from the named root key ("data", "results", etc.) |
BuildUrlWithQuery(endpoint, q) | Combines base URL, path, and remaining query-string params |
Twitter: Connection Setup
TwitterDataSource targets the Twitter API v2 (https://api.twitter.com/2)
with Bearer token auth. Set up the connection via WebAPIConnectionProperties or
through ConfigEditor.AddDataConnection.
Register via ConfigEditor (recommended)
var conn = new WebAPIConnectionProperties
{
ConnectionName = "Twitter",
ConnectionString = "https://api.twitter.com/2",
DatabaseType = DataSourceType.Twitter,
Category = DatasourceCategory.Connector,
DriverName = "TwitterDataSource", // matches [AddinAttribute]
// Authentication
ApiKey = "AAAA...", // Bearer token
// Store auth type in Parameters (read by WebAPIAuthenticationHelper)
};
// Set bearer auth type in extra Parameters
conn.Parameters = "AuthType=Bearer";
// Add a default Authorization header directly (simplest approach)
conn.Headers.Add(new WebApiHeader
{
Key = "Authorization",
Value = $"Bearer {bearerToken}"
});
dmeEditor.ConfigEditor.AddDataConnection(conn);
// Get the data source (AssemblyHandler resolves TwitterDataSource via [AddinAttribute])
var twitter = (TwitterDataSource)dmeEditor.GetDataSource("Twitter");
twitter.Openconnection();
Load your Bearer token from environment variables or a secrets manager. Never commit tokens to version control.
Twitter: Entities & Filters
TwitterDataSource registers 10 logical entity names that map to Twitter API v2
endpoints. Each entity declares which AppFilter.FieldName keys are required.
| Entity name | Twitter API v2 path | Required filters |
|---|---|---|
tweets.search | tweets/search/recent | query |
tweets.by_id | tweets | ids |
users.by_username | users/by | usernames |
users.by_id | users | ids |
users.tweets | users/{id}/tweets | id |
users.followers | users/{id}/followers | id |
users.following | users/{id}/following | id |
lists.by_user | users/{id}/owned_lists | id |
lists.tweets | lists/{id}/tweets | id |
spaces.search | spaces/search | query |
FiltersToQuery converts each AppFilter to a
key=value query-string entry using AppFilter.FieldName
as the key and AppFilter.FilterValue as the value.
Endpoint path parameters (e.g., {id}) are consumed by
ResolveEndpoint and removed from the query string.
Twitter: Reading Data
Search recent tweets (IDataSource style)
var filters = new List<AppFilter>
{
new AppFilter { FieldName = "query", FilterValue = "BeepDM", Operator = "=" },
new AppFilter { FieldName = "max_results", FilterValue = "20", Operator = "=" }
};
var raw = twitter.GetEntity("tweets.search", filters);
// Returns IEnumerable<object> (each item is a deserialized JSON object)
Get user's followers
var filters = new List<AppFilter>
{
new AppFilter { FieldName = "id", FilterValue = "12345678", Operator = "=" }
};
var followers = twitter.GetEntity("users.followers", filters);
Get tweets for a list
var filters = new List<AppFilter>
{
new AppFilter { FieldName = "id", FilterValue = "listId123", Operator = "=" },
new AppFilter { FieldName = "max_results", FilterValue = "50", Operator = "=" }
};
var tweets = twitter.GetEntity("lists.tweets", filters);
Twitter: Typed [CommandAttribute] Methods
TwitterDataSource exposes strongly-typed methods decorated with
[CommandAttribute]. These are discovered by the Beep UI and tooling
to build call panels automatically.
Search tweets — typed
// Returns IEnumerable<TwitterTweet> (from Models.cs)
IEnumerable<TwitterTweet> tweets = await twitter.GetTweets(
query: "#dotnet lang:en",
maxResults: 25
);
foreach (var tweet in tweets)
{
Console.WriteLine($"{tweet.CreatedAt:s} @{tweet.AuthorId}");
Console.WriteLine($" {tweet.Text}");
Console.WriteLine($" Likes: {tweet.PublicMetrics?.LikeCount} " +
$"RT: {tweet.PublicMetrics?.RetweetCount}");
}
User timeline
var timeline = await twitter.GetUserTimeline(
userId: "12345678",
maxResults: 50
);
var popular = timeline
.OrderByDescending(t => t.PublicMetrics?.LikeCount ?? 0)
.Take(5);
foreach (var t in popular)
Console.WriteLine($"{t.PublicMetrics?.LikeCount,6} likes — {t.Text[..50]}");
User lookup
// By username
var users = await twitter.GetUserByUsername("beep_dm");
var user = users.FirstOrDefault();
Console.WriteLine($"{user?.Name} — {user?.PublicMetrics?.FollowersCount} followers");
// By ID
var byId = await twitter.GetUserById("12345678");
| Method | Returns | Required params |
|---|---|---|
GetTweets(query, maxResults, nextToken?) | IEnumerable<TwitterTweet> | query |
GetUserTimeline(userId, maxResults, nextToken?) | IEnumerable<TwitterTweet> | userId |
GetUserByUsername(username) | IEnumerable<TwitterUser> | username |
GetUserById(userId) | IEnumerable<TwitterUser> | userId |
Twitter: Cursor Pagination
Twitter API v2 uses cursor-based pagination via meta.next_token.
TwitterDataSource.GetEntity(name, filter, pageNumber, pageSize) emulates
page-number semantics by walking the cursor chain up to pageNumber pages,
then returns the requested page.
Paged retrieval (IDataSource PagedResult)
var filters = new List<AppFilter>
{
new AppFilter { FieldName = "query", FilterValue = "#csharp", Operator = "=" }
};
// Page 1 (first 20 results)
PagedResult page1 = twitter.GetEntity("tweets.search", filters, pageNumber: 1, pageSize: 20);
Console.WriteLine($"Page 1: {page1.Data.Count} items");
Console.WriteLine($"Has next: {page1.HasNextPage}");
// Page 2
PagedResult page2 = twitter.GetEntity("tweets.search", filters, pageNumber: 2, pageSize: 20);
Explicit cursor pass-through (typed method)
string? cursor = null;
do
{
var batch = await twitter.GetTweets("#dotnet", maxResults: 100, nextToken: cursor);
ProcessBatch(batch);
// Extract next_token from the last raw page for the next call
// (use typed method overload or inspect PagedResult.TotalPages instead)
cursor = GetNextCursor(batch); // your own helper to read meta.next_token
}
while (cursor != null);
Twitter API v2 Free tier: 500,000 tweet reads / month; 1 app / 1 user.
Basic tier: 10,000 tweet reads / month with higher rate ceilings.
Use WebAPIConnectionProperties.EnableRateLimit = true and
RateLimitRequestsPerMinute to throttle automatically.
Connector Checklist
Follow these steps when building any new WebAPIDataSource subclass:
- Add
[AddinAttribute(Category = DatasourceCategory.Connector, DatasourceType = DataSourceType.YourType)]. - Define
EntityEndpoints(entity name → URL template dictionary). - Define
RequiredFilters(entity name → requiredAppFilter.FieldNamearray). - Populate
EntitiesNamesandEntitiesin the constructor. - Override
GetEntityAsync: callFiltersToQuery→RequireFilters→ResolveEndpoint→GetAsync→ExtractArray. - Add sync
GetEntityoverride that delegates toGetEntityAsync(...).GetAwaiter().GetResult(). - Optionally override
GetEntity(paged) for cursor or offset pagination. - Add typed
[CommandAttribute]methods for Beep tooling discoverability. - Create a
Models.cswith JSON-annotated POCOs for response deserialization. - Store the connector in
BeepDataSources/Connectors/<Category>/<Name>/.