feat: add integration tests and runnable examples
Add 91 integration tests (all #[ignore]) mirroring the JavaScript SDK's integration test suite. Tests cover all endpoints: ping, apps, fields, records, files, lists, and reports with success, auth, access, and not-found scenarios. Add 7 runnable examples (cargo run --example <name>) for quick manual smoke testing against a real Onspring instance. Both use env vars (API_BASE_URL, SANDBOX_API_KEY, TEST_*) matching the JavaScript SDK setup. Also rename crate to onspring-api-sdk-rust with [lib] name = "onspring" so imports remain `use onspring::*`.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
use onspring::{OnspringClient, PagingRequest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
// List apps with paging
|
||||
let paging = PagingRequest {
|
||||
page_number: 1,
|
||||
page_size: 10,
|
||||
};
|
||||
let response = client.list_apps(Some(paging)).await?;
|
||||
println!("Total apps: {:?}", response.total_records);
|
||||
|
||||
if let Some(items) = &response.items {
|
||||
for app in items {
|
||||
println!(
|
||||
" App: {} (id: {})",
|
||||
app.name.as_deref().unwrap_or("N/A"),
|
||||
app.id
|
||||
);
|
||||
}
|
||||
|
||||
// Get a single app by ID
|
||||
if let Some(first) = items.first() {
|
||||
let app = client.get_app(first.id).await?;
|
||||
println!(
|
||||
"\nFetched app: {} (id: {})",
|
||||
app.name.as_deref().unwrap_or("N/A"),
|
||||
app.id
|
||||
);
|
||||
}
|
||||
|
||||
// Batch get apps
|
||||
let ids: Vec<i32> = items.iter().take(3).map(|a| a.id).collect();
|
||||
if !ids.is_empty() {
|
||||
let batch = client.batch_get_apps(&ids).await?;
|
||||
println!(
|
||||
"\nBatch fetched {} apps",
|
||||
batch.items.map(|i| i.len()).unwrap_or(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use onspring::{OnspringClient, PagingRequest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
let app_id: i32 = std::env::var("TEST_APP_ID")
|
||||
.expect("TEST_APP_ID is required")
|
||||
.parse()
|
||||
.expect("TEST_APP_ID must be an integer");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
// List fields for an app
|
||||
let paging = PagingRequest {
|
||||
page_number: 1,
|
||||
page_size: 10,
|
||||
};
|
||||
let response = client.list_fields(app_id, Some(paging)).await?;
|
||||
println!("Total fields: {:?}", response.total_records);
|
||||
|
||||
if let Some(items) = &response.items {
|
||||
for field in items {
|
||||
println!(
|
||||
" Field: {} (id: {}, type: {:?})",
|
||||
field.name.as_deref().unwrap_or("N/A"),
|
||||
field.id,
|
||||
field.field_type.as_deref().unwrap_or("N/A"),
|
||||
);
|
||||
}
|
||||
|
||||
// Get a single field
|
||||
if let Some(first) = items.first() {
|
||||
let field = client.get_field(first.id).await?;
|
||||
println!(
|
||||
"\nFetched field: {} (id: {})",
|
||||
field.name.as_deref().unwrap_or("N/A"),
|
||||
field.id
|
||||
);
|
||||
}
|
||||
|
||||
// Batch get fields
|
||||
let ids: Vec<i32> = items.iter().take(3).map(|f| f.id).collect();
|
||||
if !ids.is_empty() {
|
||||
let batch = client.batch_get_fields(&ids).await?;
|
||||
println!(
|
||||
"\nBatch fetched {} fields",
|
||||
batch.items.map(|i| i.len()).unwrap_or(0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use onspring::{OnspringClient, SaveFileRequest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
let record_id: i32 = std::env::var("TEST_RECORD")
|
||||
.expect("TEST_RECORD is required")
|
||||
.parse()
|
||||
.expect("TEST_RECORD must be an integer");
|
||||
let field_id: i32 = std::env::var("TEST_ATTACHMENT_FIELD")
|
||||
.expect("TEST_ATTACHMENT_FIELD is required")
|
||||
.parse()
|
||||
.expect("TEST_ATTACHMENT_FIELD must be an integer");
|
||||
let file_id: i32 = std::env::var("TEST_ATTACHMENT")
|
||||
.expect("TEST_ATTACHMENT is required")
|
||||
.parse()
|
||||
.expect("TEST_ATTACHMENT must be an integer");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
// Get file info
|
||||
let info = client.get_file_info(record_id, field_id, file_id).await?;
|
||||
println!("File info:");
|
||||
println!(" Name: {:?}", info.name);
|
||||
println!(" Content type: {:?}", info.content_type);
|
||||
println!(" Created: {:?}", info.created_date);
|
||||
println!(" Owner: {:?}", info.owner);
|
||||
|
||||
// Download file content
|
||||
let file = client.get_file(record_id, field_id, file_id).await?;
|
||||
println!("\nDownloaded file:");
|
||||
println!(" File name: {:?}", file.file_name);
|
||||
println!(" Content type: {:?}", file.content_type);
|
||||
println!(" Size: {} bytes", file.data.len());
|
||||
|
||||
// Upload a file
|
||||
let request = SaveFileRequest {
|
||||
record_id,
|
||||
field_id,
|
||||
notes: Some("Uploaded from Rust SDK example".to_string()),
|
||||
modified_date: Some(chrono::Utc::now()),
|
||||
file_name: "example-upload.txt".to_string(),
|
||||
file_data: b"Hello from the Rust SDK!".to_vec(),
|
||||
content_type: "text/plain".to_string(),
|
||||
};
|
||||
|
||||
let uploaded = client.upload_file(request).await?;
|
||||
println!("\nUploaded file with ID: {}", uploaded.id);
|
||||
|
||||
// Delete the uploaded file
|
||||
client.delete_file(record_id, field_id, uploaded.id).await?;
|
||||
println!("Deleted file {}", uploaded.id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use onspring::{OnspringClient, SaveListItemRequest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
let list_id: i32 = std::env::var("TEST_LIST_ID")
|
||||
.expect("TEST_LIST_ID is required")
|
||||
.parse()
|
||||
.expect("TEST_LIST_ID must be an integer");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
// Add a list item
|
||||
let name = format!("rust_sdk_example_{}", chrono::Utc::now().timestamp_millis());
|
||||
let request = SaveListItemRequest {
|
||||
id: None,
|
||||
name: name.clone(),
|
||||
numeric_value: Some(42.0),
|
||||
color: Some("#FF5733".to_string()),
|
||||
};
|
||||
|
||||
let response = client.save_list_item(list_id, request).await?;
|
||||
println!("Added list item '{}' with ID: {}", name, response.id);
|
||||
|
||||
// Update the list item
|
||||
let update_name = format!("updated_{}", name);
|
||||
let update_request = SaveListItemRequest {
|
||||
id: Some(response.id),
|
||||
name: update_name.clone(),
|
||||
numeric_value: Some(99.0),
|
||||
color: Some("#00FF00".to_string()),
|
||||
};
|
||||
|
||||
client.save_list_item(list_id, update_request).await?;
|
||||
println!("Updated list item to '{}'", update_name);
|
||||
|
||||
// Delete the list item
|
||||
client.delete_list_item(list_id, response.id).await?;
|
||||
println!("Deleted list item {}", response.id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use onspring::OnspringClient;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
client.ping().await?;
|
||||
println!("Connected to the Onspring API successfully.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use onspring::{OnspringClient, PagingRequest, QueryRecordsRequest, SaveRecordRequest};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
let app_id: i32 = std::env::var("TEST_SURVEY_ID")
|
||||
.expect("TEST_SURVEY_ID is required")
|
||||
.parse()
|
||||
.expect("TEST_SURVEY_ID must be an integer");
|
||||
let field_id = std::env::var("TEST_TEXT_FIELD").expect("TEST_TEXT_FIELD is required");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
// List records
|
||||
let paging = PagingRequest {
|
||||
page_number: 1,
|
||||
page_size: 5,
|
||||
};
|
||||
let response = client
|
||||
.list_records(app_id, Some(paging), None, None)
|
||||
.await?;
|
||||
println!("Total records: {:?}", response.total_records);
|
||||
|
||||
if let Some(items) = &response.items {
|
||||
for record in items {
|
||||
println!(" Record ID: {}", record.record_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a record
|
||||
let mut fields = HashMap::new();
|
||||
fields.insert(field_id.clone(), serde_json::json!("Hello from Rust SDK"));
|
||||
|
||||
let request = SaveRecordRequest {
|
||||
app_id,
|
||||
record_id: None,
|
||||
fields,
|
||||
};
|
||||
|
||||
let saved = client.save_record(request).await?;
|
||||
println!("\nCreated record with ID: {}", saved.id);
|
||||
|
||||
// Get the record back
|
||||
let record = client.get_record(app_id, saved.id, None, None).await?;
|
||||
println!(
|
||||
"Fetched record: app_id={}, record_id={}",
|
||||
record.app_id, record.record_id
|
||||
);
|
||||
|
||||
// Update the record
|
||||
let mut fields = HashMap::new();
|
||||
fields.insert(field_id, serde_json::json!("Updated from Rust SDK"));
|
||||
|
||||
let update = SaveRecordRequest {
|
||||
app_id,
|
||||
record_id: Some(saved.id),
|
||||
fields,
|
||||
};
|
||||
client.save_record(update).await?;
|
||||
println!("Updated record {}", saved.id);
|
||||
|
||||
// Query records
|
||||
let query = QueryRecordsRequest {
|
||||
app_id,
|
||||
filter: format!(
|
||||
"{} contains 'Rust'",
|
||||
std::env::var("TEST_TEXT_FIELD").unwrap()
|
||||
),
|
||||
field_ids: None,
|
||||
data_format: None,
|
||||
};
|
||||
let query_result = client.query_records(query, None).await?;
|
||||
println!("\nQuery found {:?} records", query_result.total_records);
|
||||
|
||||
// Delete the record
|
||||
client.delete_record(app_id, saved.id).await?;
|
||||
println!("Deleted record {}", saved.id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use onspring::{OnspringClient, PagingRequest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> onspring::Result<()> {
|
||||
let base_url = std::env::var("API_BASE_URL").expect("API_BASE_URL is required");
|
||||
let api_key = std::env::var("SANDBOX_API_KEY").expect("SANDBOX_API_KEY is required");
|
||||
let app_id: i32 = std::env::var("TEST_SURVEY_ID")
|
||||
.expect("TEST_SURVEY_ID is required")
|
||||
.parse()
|
||||
.expect("TEST_SURVEY_ID must be an integer");
|
||||
|
||||
let client = OnspringClient::builder(&api_key)
|
||||
.base_url(&base_url)
|
||||
.build();
|
||||
|
||||
// List reports for an app
|
||||
let paging = PagingRequest {
|
||||
page_number: 1,
|
||||
page_size: 10,
|
||||
};
|
||||
let response = client.list_reports(app_id, Some(paging)).await?;
|
||||
println!("Total reports: {:?}", response.total_records);
|
||||
|
||||
if let Some(items) = &response.items {
|
||||
for report in items {
|
||||
println!(
|
||||
" Report: {} (id: {}, app_id: {})",
|
||||
report.name.as_deref().unwrap_or("N/A"),
|
||||
report.id,
|
||||
report.app_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Get report data for the first report
|
||||
if let Some(first) = items.first() {
|
||||
let data = client.get_report(first.id, None, None).await?;
|
||||
println!(
|
||||
"\nReport '{}' data:",
|
||||
first.name.as_deref().unwrap_or("N/A")
|
||||
);
|
||||
println!(" Columns: {:?}", data.columns);
|
||||
|
||||
if let Some(rows) = &data.rows {
|
||||
println!(" Rows: {}", rows.len());
|
||||
for (i, row) in rows.iter().take(3).enumerate() {
|
||||
println!(
|
||||
" Row {}: record_id={:?}, cells={:?}",
|
||||
i, row.record_id, row.cells
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user