style: format code and reorganize imports across rfsclient codebase
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use sal_rfs_client::RfsClient;
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials};
|
||||
use sal_rfs_client::RfsClient;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -12,10 +12,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}),
|
||||
timeout_seconds: 30,
|
||||
};
|
||||
|
||||
|
||||
let mut client = RfsClient::new(config);
|
||||
println!("Client created with authentication credentials");
|
||||
|
||||
|
||||
// Authenticate with the server
|
||||
client.authenticate().await?;
|
||||
if client.is_authenticated() {
|
||||
@@ -30,13 +30,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
credentials: None,
|
||||
timeout_seconds: 30,
|
||||
};
|
||||
|
||||
|
||||
let client_no_auth = RfsClient::new(config_no_auth);
|
||||
println!("Client created without authentication credentials");
|
||||
|
||||
|
||||
// Check health endpoint (doesn't require authentication)
|
||||
let health = client_no_auth.health_check().await?;
|
||||
println!("Server health: {:?}", health);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use sal_rfs_client::RfsClient;
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials};
|
||||
use openapi::models::{VerifyBlock, VerifyBlocksRequest};
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials};
|
||||
use sal_rfs_client::RfsClient;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -13,45 +13,52 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}),
|
||||
timeout_seconds: 60,
|
||||
};
|
||||
|
||||
|
||||
let mut client = RfsClient::new(config);
|
||||
|
||||
|
||||
// Authenticate with the server
|
||||
client.authenticate().await?;
|
||||
println!("Authentication successful");
|
||||
|
||||
|
||||
// Create a test file to upload for block testing
|
||||
let test_file_path = "/tmp/block_test.txt";
|
||||
let test_content = "This is a test file for RFS client block management";
|
||||
std::fs::write(test_file_path, test_content)?;
|
||||
println!("Created test file at {}", test_file_path);
|
||||
|
||||
|
||||
// Upload the file to get blocks
|
||||
println!("Uploading file to get blocks...");
|
||||
let file_hash = client.upload_file(test_file_path, None).await?;
|
||||
println!("File uploaded with hash: {}", file_hash);
|
||||
|
||||
|
||||
// Get blocks by file hash
|
||||
println!("Getting blocks for file hash: {}", file_hash);
|
||||
let blocks = client.get_blocks_by_hash(&file_hash).await?;
|
||||
println!("Found {} blocks for the file", blocks.blocks.len());
|
||||
|
||||
|
||||
// Print block information
|
||||
for (i, block_data) in blocks.blocks.iter().enumerate() {
|
||||
println!("Block {}: Hash={}, Index={}", i, block_data.hash, block_data.index);
|
||||
println!(
|
||||
"Block {}: Hash={}, Index={}",
|
||||
i, block_data.hash, block_data.index
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Verify blocks with complete information
|
||||
println!("Verifying blocks...");
|
||||
|
||||
|
||||
// Create a list of VerifyBlock objects with complete information
|
||||
let verify_blocks = blocks.blocks.iter().map(|block| {
|
||||
VerifyBlock {
|
||||
block_hash: block.hash.clone(),
|
||||
block_index: block.index,
|
||||
file_hash: file_hash.clone(), // Using the actual file hash
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
let verify_blocks = blocks
|
||||
.blocks
|
||||
.iter()
|
||||
.map(|block| {
|
||||
VerifyBlock {
|
||||
block_hash: block.hash.clone(),
|
||||
block_index: block.index,
|
||||
file_hash: file_hash.clone(), // Using the actual file hash
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Create the request with the complete block information
|
||||
for block in verify_blocks.iter() {
|
||||
@@ -59,27 +66,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Block index: {}", block.block_index);
|
||||
println!("File hash: {}", block.file_hash);
|
||||
}
|
||||
let request = VerifyBlocksRequest { blocks: verify_blocks };
|
||||
|
||||
let request = VerifyBlocksRequest {
|
||||
blocks: verify_blocks,
|
||||
};
|
||||
|
||||
// Send the verification request
|
||||
let verify_result = client.verify_blocks(request).await?;
|
||||
println!("Verification result: {} missing blocks", verify_result.missing.len());
|
||||
println!(
|
||||
"Verification result: {} missing blocks",
|
||||
verify_result.missing.len()
|
||||
);
|
||||
for block in verify_result.missing.iter() {
|
||||
println!("Missing block: {}", block);
|
||||
}
|
||||
|
||||
|
||||
// List blocks (list_blocks_handler)
|
||||
println!("\n1. Listing all blocks with pagination...");
|
||||
let blocks_list = client.list_blocks(None).await?;
|
||||
println!("Server has {} blocks in total", blocks_list.len());
|
||||
if !blocks_list.is_empty() {
|
||||
let first_few = blocks_list.iter().take(3)
|
||||
let first_few = blocks_list
|
||||
.iter()
|
||||
.take(3)
|
||||
.map(|s| s.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
println!("First few blocks: {}", first_few);
|
||||
}
|
||||
|
||||
|
||||
// Check if a block exists (check_block_handler)
|
||||
if !blocks.blocks.is_empty() {
|
||||
let block_to_check = &blocks.blocks[0].hash;
|
||||
@@ -87,15 +101,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let exists = client.check_block(block_to_check).await?;
|
||||
println!("Block exists: {}", exists);
|
||||
}
|
||||
|
||||
|
||||
// Get block downloads statistics (get_block_downloads_handler)
|
||||
if !blocks.blocks.is_empty() {
|
||||
let block_to_check = &blocks.blocks[0].hash;
|
||||
println!("\n3. Getting download statistics for block: {}", block_to_check);
|
||||
println!(
|
||||
"\n3. Getting download statistics for block: {}",
|
||||
block_to_check
|
||||
);
|
||||
let downloads = client.get_block_downloads(block_to_check).await?;
|
||||
println!("Block has been downloaded {} times", downloads.downloads_count);
|
||||
println!(
|
||||
"Block has been downloaded {} times",
|
||||
downloads.downloads_count
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Get a specific block content (get_block_handler)
|
||||
if !blocks.blocks.is_empty() {
|
||||
let block_to_get = &blocks.blocks[0].hash;
|
||||
@@ -103,26 +123,31 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let block_content = client.get_block(block_to_get).await?;
|
||||
println!("Retrieved block with {} bytes", block_content.len());
|
||||
}
|
||||
|
||||
|
||||
// Get user blocks (get_user_blocks_handler)
|
||||
println!("\n6. Listing user blocks...");
|
||||
let user_blocks = client.get_user_blocks(Some(1), Some(10)).await?;
|
||||
println!("User has {} blocks (showing page 1 with 10 per page)", user_blocks.total);
|
||||
println!(
|
||||
"User has {} blocks (showing page 1 with 10 per page)",
|
||||
user_blocks.total
|
||||
);
|
||||
for block in user_blocks.blocks.iter().take(3) {
|
||||
println!(" - Block: {}, Size: {}", block.hash, block.size);
|
||||
}
|
||||
|
||||
|
||||
// Upload a block (upload_block_handler)
|
||||
println!("\n7. Uploading a new test block...");
|
||||
let test_block_data = b"This is test block data for direct block upload";
|
||||
let new_file_hash = "test_file_hash_for_block_upload";
|
||||
let block_index = 0;
|
||||
let block_hash = client.upload_block(new_file_hash, block_index, test_block_data.to_vec()).await?;
|
||||
let block_hash = client
|
||||
.upload_block(new_file_hash, block_index, test_block_data.to_vec())
|
||||
.await?;
|
||||
println!("Uploaded block with hash: {}", block_hash);
|
||||
|
||||
|
||||
// Clean up
|
||||
std::fs::remove_file(test_file_path)?;
|
||||
println!("Test file cleaned up");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials, DownloadOptions, UploadOptions};
|
||||
use sal_rfs_client::RfsClient;
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials, UploadOptions, DownloadOptions};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -12,53 +12,55 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}),
|
||||
timeout_seconds: 60,
|
||||
};
|
||||
|
||||
|
||||
let mut client = RfsClient::new(config);
|
||||
|
||||
|
||||
// Authenticate with the server
|
||||
client.authenticate().await?;
|
||||
println!("Authentication successful");
|
||||
|
||||
|
||||
// Create a test file to upload
|
||||
let test_file_path = "/tmp/test_upload.txt";
|
||||
std::fs::write(test_file_path, "This is a test file for RFS client upload")?;
|
||||
println!("Created test file at {}", test_file_path);
|
||||
|
||||
|
||||
// Upload the file with options
|
||||
println!("Uploading file...");
|
||||
let upload_options = UploadOptions {
|
||||
chunk_size: Some(1024 * 1024), // 1MB chunks
|
||||
verify: true,
|
||||
};
|
||||
|
||||
let file_hash = client.upload_file(test_file_path, Some(upload_options)).await?;
|
||||
|
||||
let file_hash = client
|
||||
.upload_file(test_file_path, Some(upload_options))
|
||||
.await?;
|
||||
println!("File uploaded with hash: {}", file_hash);
|
||||
|
||||
|
||||
// Download the file
|
||||
let download_path = "/tmp/test_download.txt";
|
||||
println!("Downloading file to {}...", download_path);
|
||||
|
||||
let download_options = DownloadOptions {
|
||||
verify: true,
|
||||
};
|
||||
|
||||
client.download_file(&file_hash, download_path, Some(download_options)).await?;
|
||||
|
||||
let download_options = DownloadOptions { verify: true };
|
||||
|
||||
client
|
||||
.download_file(&file_hash, download_path, Some(download_options))
|
||||
.await?;
|
||||
println!("File downloaded to {}", download_path);
|
||||
|
||||
|
||||
// Verify the downloaded file matches the original
|
||||
let original_content = std::fs::read_to_string(test_file_path)?;
|
||||
let downloaded_content = std::fs::read_to_string(download_path)?;
|
||||
|
||||
|
||||
if original_content == downloaded_content {
|
||||
println!("File contents match! Download successful.");
|
||||
} else {
|
||||
println!("ERROR: File contents do not match!");
|
||||
}
|
||||
|
||||
|
||||
// Clean up test files
|
||||
std::fs::remove_file(test_file_path)?;
|
||||
std::fs::remove_file(download_path)?;
|
||||
println!("Test files cleaned up");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use sal_rfs_client::RfsClient;
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials, FlistOptions, WaitOptions};
|
||||
use sal_rfs_client::RfsClient;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -13,17 +13,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}),
|
||||
timeout_seconds: 60,
|
||||
};
|
||||
|
||||
|
||||
let mut client = RfsClient::new(config);
|
||||
|
||||
|
||||
// Authenticate with the server
|
||||
client.authenticate().await?;
|
||||
println!("Authentication successful");
|
||||
|
||||
|
||||
println!("\n1. CREATE FLIST - Creating an FList from a Docker image");
|
||||
let image_name = "alpine:latest";
|
||||
println!("Creating FList for image: {}", image_name);
|
||||
|
||||
|
||||
// Use FlistOptions to specify additional parameters
|
||||
let options = FlistOptions {
|
||||
auth: None,
|
||||
@@ -34,13 +34,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
identity_token: None,
|
||||
registry_token: None,
|
||||
};
|
||||
|
||||
|
||||
// Create the FList and handle potential conflict error
|
||||
let job_id = match client.create_flist(&image_name, Some(options)).await {
|
||||
Ok(id) => {
|
||||
println!("FList creation started with job ID: {}", id);
|
||||
Some(id)
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
if e.to_string().contains("Conflict") {
|
||||
println!("FList already exists");
|
||||
@@ -50,51 +50,55 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 2. Check FList state if we have a job ID
|
||||
if let Some(job_id) = &job_id {
|
||||
println!("\n2. GET FLIST STATE - Checking FList creation state");
|
||||
let state = client.get_flist_state(job_id).await?;
|
||||
println!("Current FList state: {:?}", state.flist_state);
|
||||
|
||||
|
||||
// 3. Wait for FList creation with progress reporting
|
||||
println!("\n3. WAIT FOR FLIST CREATION - Waiting for FList to be created with progress reporting");
|
||||
let wait_options = WaitOptions {
|
||||
timeout_seconds: 60, // Shorter timeout for the example
|
||||
timeout_seconds: 60, // Shorter timeout for the example
|
||||
poll_interval_ms: 1000,
|
||||
progress_callback: Some(Box::new(|state| {
|
||||
println!("Progress: FList state is now {:?}", state);
|
||||
// No return value needed (returns unit type)
|
||||
})),
|
||||
};
|
||||
|
||||
|
||||
// Wait for the FList to be created (with a timeout)
|
||||
match client.wait_for_flist_creation(job_id, Some(wait_options)).await {
|
||||
match client
|
||||
.wait_for_flist_creation(job_id, Some(wait_options))
|
||||
.await
|
||||
{
|
||||
Ok(final_state) => {
|
||||
println!("FList creation completed with state: {:?}", final_state);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error waiting for FList creation: {}", e);
|
||||
// Continue with the example even if waiting fails
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 4. List all available FLists
|
||||
println!("\n4. LIST FLISTS - Listing all available FLists");
|
||||
|
||||
|
||||
// Variable to store the FList path for preview and download
|
||||
let mut flist_path_for_preview: Option<String> = None;
|
||||
|
||||
|
||||
match client.list_flists().await {
|
||||
Ok(flists) => {
|
||||
println!("Found {} FList categories", flists.len());
|
||||
|
||||
|
||||
for (category, files) in &flists {
|
||||
println!("Category: {}", category);
|
||||
for file in files.iter().take(2) { // Show only first 2 files per category
|
||||
for file in files.iter().take(2) {
|
||||
// Show only first 2 files per category
|
||||
println!(" - {} (size: {} bytes)", file.name, file.size);
|
||||
|
||||
|
||||
// Save the first FList path for preview
|
||||
if flist_path_for_preview.is_none() {
|
||||
let path = format!("{}/{}/{}", parent_dir, category, file.name);
|
||||
@@ -105,7 +109,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" - ... and {} more files", files.len() - 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 5. Preview an FList if we found one
|
||||
if let Some(ref flist_path) = flist_path_for_preview {
|
||||
println!("\n5. PREVIEW FLIST - Previewing FList: {}", flist_path);
|
||||
@@ -114,57 +118,59 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("FList preview for {}:", flist_path);
|
||||
println!(" - Checksum: {}", preview.checksum);
|
||||
println!(" - Metadata: {}", preview.metadata);
|
||||
|
||||
|
||||
// Display content (list of strings)
|
||||
if !preview.content.is_empty() {
|
||||
println!(" - Content entries:");
|
||||
for (i, entry) in preview.content.iter().enumerate().take(5) {
|
||||
println!(" {}. {}", i+1, entry);
|
||||
println!(" {}. {}", i + 1, entry);
|
||||
}
|
||||
if preview.content.len() > 5 {
|
||||
println!(" ... and {} more entries", preview.content.len() - 5);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error previewing FList: {}", e),
|
||||
}
|
||||
} else {
|
||||
println!("No FLists available for preview");
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error listing FLists: {}", e),
|
||||
}
|
||||
|
||||
|
||||
// 6. DOWNLOAD FLIST - Downloading an FList to a local file
|
||||
if let Some(ref flist_path) = flist_path_for_preview {
|
||||
println!("\n6. DOWNLOAD FLIST - Downloading FList: {}", flist_path);
|
||||
|
||||
|
||||
// Create a temporary output path for the downloaded FList
|
||||
let output_path = "/tmp/downloaded_flist.fl";
|
||||
|
||||
|
||||
match client.download_flist(flist_path, output_path).await {
|
||||
Ok(_) => {
|
||||
println!("FList successfully downloaded to {}", output_path);
|
||||
|
||||
|
||||
// Get file size
|
||||
match std::fs::metadata(output_path) {
|
||||
Ok(metadata) => println!("Downloaded file size: {} bytes", metadata.len()),
|
||||
Err(e) => println!("Error getting file metadata: {}", e),
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => println!("Error downloading FList: {}", e),
|
||||
}
|
||||
} else {
|
||||
println!("\n6. DOWNLOAD FLIST - No FList available for download");
|
||||
}
|
||||
|
||||
|
||||
println!("\nAll FList operations demonstrated:");
|
||||
println!("1. create_flist - Create a new FList from a Docker image");
|
||||
println!("2. get_flist_state - Check the state of an FList creation job");
|
||||
println!("3. wait_for_flist_creation - Wait for an FList to be created with progress reporting");
|
||||
println!(
|
||||
"3. wait_for_flist_creation - Wait for an FList to be created with progress reporting"
|
||||
);
|
||||
println!("4. list_flists - List all available FLists");
|
||||
println!("5. preview_flist - Preview the content of an FList");
|
||||
println!("6. download_flist - Download an FList to a local file");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use sal_rfs_client::RfsClient;
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials, WaitOptions};
|
||||
use openapi::models::FlistState;
|
||||
use sal_rfs_client::types::{ClientConfig, Credentials, WaitOptions};
|
||||
use sal_rfs_client::RfsClient;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -13,49 +13,52 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}),
|
||||
timeout_seconds: 60,
|
||||
};
|
||||
|
||||
|
||||
let mut client = RfsClient::new(config);
|
||||
|
||||
|
||||
// Authenticate with the server
|
||||
client.authenticate().await?;
|
||||
println!("Authentication successful");
|
||||
|
||||
|
||||
// Create an FList from a Docker image
|
||||
let image_name = "redis:latest";
|
||||
println!("Creating FList for image: {}", image_name);
|
||||
|
||||
|
||||
let job_id = client.create_flist(&image_name, None).await?;
|
||||
println!("FList creation started with job ID: {}", job_id);
|
||||
|
||||
|
||||
// Set up options for waiting with progress reporting
|
||||
let options = WaitOptions {
|
||||
timeout_seconds: 600, // 10 minutes timeout
|
||||
timeout_seconds: 600, // 10 minutes timeout
|
||||
poll_interval_ms: 2000, // Check every 2 seconds
|
||||
progress_callback: Some(Box::new(|state| {
|
||||
match state {
|
||||
FlistState::FlistStateInProgress(info) => {
|
||||
println!("Progress: {:.1}% - {}", info.in_progress.progress, info.in_progress.msg);
|
||||
},
|
||||
FlistState::FlistStateStarted(_) => {
|
||||
println!("FList creation started...");
|
||||
},
|
||||
FlistState::FlistStateAccepted(_) => {
|
||||
println!("FList creation request accepted...");
|
||||
},
|
||||
_ => println!("State: {:?}", state),
|
||||
progress_callback: Some(Box::new(|state| match state {
|
||||
FlistState::FlistStateInProgress(info) => {
|
||||
println!(
|
||||
"Progress: {:.1}% - {}",
|
||||
info.in_progress.progress, info.in_progress.msg
|
||||
);
|
||||
}
|
||||
FlistState::FlistStateStarted(_) => {
|
||||
println!("FList creation started...");
|
||||
}
|
||||
FlistState::FlistStateAccepted(_) => {
|
||||
println!("FList creation request accepted...");
|
||||
}
|
||||
_ => println!("State: {:?}", state),
|
||||
})),
|
||||
};
|
||||
|
||||
|
||||
// Wait for the FList to be created
|
||||
println!("Waiting for FList creation to complete...");
|
||||
|
||||
|
||||
// Use ? operator to propagate errors properly
|
||||
let state = client.wait_for_flist_creation(&job_id, Some(options)).await
|
||||
let state = client
|
||||
.wait_for_flist_creation(&job_id, Some(options))
|
||||
.await
|
||||
.map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
|
||||
|
||||
|
||||
println!("FList created successfully!");
|
||||
println!("Final state: {:?}", state);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,24 +1,23 @@
|
||||
use bytes::Bytes;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use bytes::Bytes;
|
||||
|
||||
use openapi::{
|
||||
apis::{
|
||||
authentication_api, block_management_api, flist_management_api,
|
||||
file_management_api, system_api, website_serving_api,
|
||||
configuration::Configuration,
|
||||
authentication_api, block_management_api, configuration::Configuration,
|
||||
file_management_api, flist_management_api, system_api, website_serving_api,
|
||||
Error as OpenApiError,
|
||||
},
|
||||
models::{
|
||||
SignInBody, ListBlocksParams,
|
||||
VerifyBlocksRequest, VerifyBlocksResponse, FlistBody, UserBlocksResponse, BlockDownloadsResponse,
|
||||
BlocksResponse, PreviewResponse, FileInfo, FlistState, FlistStateResponse,
|
||||
BlockDownloadsResponse, BlocksResponse, FileInfo, FlistBody, FlistState,
|
||||
FlistStateResponse, ListBlocksParams, PreviewResponse, SignInBody, UserBlocksResponse,
|
||||
VerifyBlocksRequest, VerifyBlocksResponse,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::error::{RfsError, Result, map_openapi_error};
|
||||
use crate::types::{ClientConfig, UploadOptions, DownloadOptions, FlistOptions, WaitOptions};
|
||||
use crate::error::{map_openapi_error, Result, RfsError};
|
||||
use crate::types::{ClientConfig, DownloadOptions, FlistOptions, UploadOptions, WaitOptions};
|
||||
|
||||
/// Main client for interacting with the RFS server
|
||||
#[derive(Clone)]
|
||||
@@ -33,16 +32,18 @@ impl RfsClient {
|
||||
pub fn new(client_config: ClientConfig) -> Self {
|
||||
// Create a custom reqwest client with timeout configuration
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(client_config.timeout_seconds))
|
||||
.timeout(std::time::Duration::from_secs(
|
||||
client_config.timeout_seconds,
|
||||
))
|
||||
.build()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
||||
// Create OpenAPI configuration with our custom client
|
||||
let mut config = Configuration::new();
|
||||
config.base_path = client_config.base_url.clone();
|
||||
config.user_agent = Some(format!("rfs-client/0.1.0"));
|
||||
config.user_agent = Some("rfs-client/0.1.0".to_string());
|
||||
config.client = client;
|
||||
|
||||
|
||||
Self {
|
||||
config: Arc::new(config),
|
||||
client_config,
|
||||
@@ -70,22 +71,26 @@ impl RfsClient {
|
||||
if let Some(token) = Some(result.access_token) {
|
||||
// Create a custom reqwest client with timeout configuration
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(self.client_config.timeout_seconds))
|
||||
.timeout(std::time::Duration::from_secs(
|
||||
self.client_config.timeout_seconds,
|
||||
))
|
||||
.build()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
||||
// Create a new configuration with the auth token and timeout
|
||||
let mut new_config = Configuration::new();
|
||||
new_config.base_path = self.client_config.base_url.clone();
|
||||
new_config.user_agent = Some(format!("rfs-client/0.1.0"));
|
||||
new_config.user_agent = Some("rfs-client/0.1.0".to_string());
|
||||
new_config.bearer_access_token = Some(token.clone());
|
||||
new_config.client = client;
|
||||
|
||||
|
||||
self.config = Arc::new(new_config);
|
||||
self.auth_token = Some(token);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RfsError::AuthError("No token received from server".to_string()))
|
||||
Err(RfsError::AuthError(
|
||||
"No token received from server".to_string(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(RfsError::AuthError("No credentials provided".to_string()))
|
||||
@@ -102,62 +107,79 @@ impl RfsClient {
|
||||
let result = system_api::health_check_handler(&self.config)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result.msg)
|
||||
}
|
||||
|
||||
/// Upload a file to the RFS server
|
||||
pub async fn upload_file<P: AsRef<Path>>(&self, file_path: P, options: Option<UploadOptions>) -> Result<String> {
|
||||
pub async fn upload_file<P: AsRef<Path>>(
|
||||
&self,
|
||||
file_path: P,
|
||||
options: Option<UploadOptions>,
|
||||
) -> Result<String> {
|
||||
let file_path = file_path.as_ref();
|
||||
let _options = options.unwrap_or_default();
|
||||
|
||||
|
||||
// Check if file exists
|
||||
if !file_path.exists() {
|
||||
return Err(RfsError::FileSystemError(format!("File not found: {}", file_path.display())));
|
||||
return Err(RfsError::FileSystemError(format!(
|
||||
"File not found: {}",
|
||||
file_path.display()
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
// Use the OpenAPI client to upload the file
|
||||
let result = file_management_api::upload_file_handler(&self.config, file_path.to_path_buf())
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
let result =
|
||||
file_management_api::upload_file_handler(&self.config, file_path.to_path_buf())
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
// Extract the file hash from the response
|
||||
Ok(result.file_hash.clone())
|
||||
}
|
||||
|
||||
/// Download a file from the RFS server
|
||||
pub async fn download_file<P: AsRef<Path>>(&self, file_id: &str, output_path: P, options: Option<DownloadOptions>) -> Result<()> {
|
||||
pub async fn download_file<P: AsRef<Path>>(
|
||||
&self,
|
||||
file_id: &str,
|
||||
output_path: P,
|
||||
options: Option<DownloadOptions>,
|
||||
) -> Result<()> {
|
||||
let output_path = output_path.as_ref();
|
||||
let _options = options.unwrap_or_default();
|
||||
|
||||
|
||||
// Create parent directories if needed
|
||||
if let Some(parent) = output_path.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| RfsError::FileSystemError(format!("Failed to create directory: {}", e)))?;
|
||||
std::fs::create_dir_all(parent).map_err(|e| {
|
||||
RfsError::FileSystemError(format!("Failed to create directory: {}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
// Create a FileDownloadRequest with the filename from the output path
|
||||
let file_name = output_path.file_name()
|
||||
let file_name = output_path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("downloaded_file")
|
||||
.to_string();
|
||||
|
||||
|
||||
let download_request = openapi::models::FileDownloadRequest::new(file_name);
|
||||
|
||||
|
||||
// Download the file
|
||||
let response = file_management_api::get_file_handler(&self.config, file_id, download_request)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
let response =
|
||||
file_management_api::get_file_handler(&self.config, file_id, download_request)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
// Read the response body
|
||||
let bytes = response.bytes()
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|e| RfsError::RequestError(e))?;
|
||||
|
||||
.map_err(RfsError::RequestError)?;
|
||||
|
||||
// Write the file to disk
|
||||
std::fs::write(output_path, bytes)
|
||||
.map_err(|e| RfsError::FileSystemError(format!("Failed to write file: {}", e)))?;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -168,29 +190,38 @@ impl RfsClient {
|
||||
let result = block_management_api::list_blocks_handler(&self.config, page, per_page)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result.blocks)
|
||||
}
|
||||
|
||||
/// Verify blocks
|
||||
pub async fn verify_blocks(&self, request: VerifyBlocksRequest) -> Result<VerifyBlocksResponse> {
|
||||
pub async fn verify_blocks(
|
||||
&self,
|
||||
request: VerifyBlocksRequest,
|
||||
) -> Result<VerifyBlocksResponse> {
|
||||
let result = block_management_api::verify_blocks_handler(&self.config, request)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Create a new FList from a Docker image
|
||||
pub async fn create_flist(&self, image_name: &str, options: Option<FlistOptions>) -> Result<String> {
|
||||
pub async fn create_flist(
|
||||
&self,
|
||||
image_name: &str,
|
||||
options: Option<FlistOptions>,
|
||||
) -> Result<String> {
|
||||
// Ensure the client is authenticated
|
||||
if !self.is_authenticated() {
|
||||
return Err(RfsError::AuthError("Authentication required for creating FLists".to_string()));
|
||||
return Err(RfsError::AuthError(
|
||||
"Authentication required for creating FLists".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Create FList body with the required fields
|
||||
let mut flist = FlistBody::new(image_name.to_string());
|
||||
|
||||
|
||||
// Apply options if provided
|
||||
if let Some(opts) = options {
|
||||
flist.username = opts.username.map(Some);
|
||||
@@ -201,12 +232,12 @@ impl RfsClient {
|
||||
flist.identity_token = opts.identity_token.map(Some);
|
||||
flist.registry_token = opts.registry_token.map(Some);
|
||||
}
|
||||
|
||||
|
||||
// Call the API to create the FList
|
||||
let result = flist_management_api::create_flist_handler(&self.config, flist)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
// Return the job ID
|
||||
Ok(result.id)
|
||||
}
|
||||
@@ -215,66 +246,80 @@ impl RfsClient {
|
||||
pub async fn get_flist_state(&self, job_id: &str) -> Result<FlistStateResponse> {
|
||||
// Ensure the client is authenticated
|
||||
if !self.is_authenticated() {
|
||||
return Err(RfsError::AuthError("Authentication required for accessing FList state".to_string()));
|
||||
return Err(RfsError::AuthError(
|
||||
"Authentication required for accessing FList state".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Call the API to get the FList state
|
||||
let result = flist_management_api::get_flist_state_handler(&self.config, job_id)
|
||||
.await
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
||||
/// Wait for an FList to be created
|
||||
///
|
||||
///
|
||||
/// This method polls the FList state until it reaches a terminal state (Created or Failed)
|
||||
/// or until the timeout is reached.
|
||||
pub async fn wait_for_flist_creation(&self, job_id: &str, options: Option<WaitOptions>) -> Result<FlistStateResponse> {
|
||||
pub async fn wait_for_flist_creation(
|
||||
&self,
|
||||
job_id: &str,
|
||||
options: Option<WaitOptions>,
|
||||
) -> Result<FlistStateResponse> {
|
||||
let options = options.unwrap_or_default();
|
||||
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(options.timeout_seconds);
|
||||
|
||||
let deadline =
|
||||
std::time::Instant::now() + std::time::Duration::from_secs(options.timeout_seconds);
|
||||
|
||||
loop {
|
||||
// Check if we've exceeded the timeout
|
||||
if std::time::Instant::now() > deadline {
|
||||
return Err(RfsError::TimeoutError(format!(
|
||||
"Timed out waiting for FList creation after {} seconds",
|
||||
"Timed out waiting for FList creation after {} seconds",
|
||||
options.timeout_seconds
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
// Get the current state
|
||||
let state_result = self.get_flist_state(job_id).await;
|
||||
|
||||
|
||||
match state_result {
|
||||
Ok(state) => {
|
||||
// Call progress callback if provided
|
||||
if let Some(ref callback) = options.progress_callback {
|
||||
callback(state.flist_state.as_ref());
|
||||
}
|
||||
|
||||
|
||||
// Check if we've reached a terminal state
|
||||
match state.flist_state.as_ref() {
|
||||
FlistState::FlistStateCreated(_) => {
|
||||
// Success! FList was created
|
||||
return Ok(state);
|
||||
},
|
||||
}
|
||||
FlistState::FlistStateFailed(error_msg) => {
|
||||
// Failure! FList creation failed
|
||||
return Err(RfsError::FListError(format!("FList creation failed: {}", error_msg)));
|
||||
},
|
||||
return Err(RfsError::FListError(format!(
|
||||
"FList creation failed: {}",
|
||||
error_msg
|
||||
)));
|
||||
}
|
||||
_ => {
|
||||
// Still in progress, continue polling
|
||||
tokio::time::sleep(std::time::Duration::from_millis(options.poll_interval_ms)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(
|
||||
options.poll_interval_ms,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
// If we get a 404 error, it might be because the FList job is still initializing
|
||||
// Just wait and retry
|
||||
println!("Warning: Error checking FList state: {}", e);
|
||||
println!("Retrying in {} ms...", options.poll_interval_ms);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(options.poll_interval_ms)).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(options.poll_interval_ms))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,7 +339,7 @@ impl RfsClient {
|
||||
let result = block_management_api::get_block_downloads_handler(&self.config, hash)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -303,10 +348,12 @@ impl RfsClient {
|
||||
let response = block_management_api::get_block_handler(&self.config, hash)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
let bytes = response.bytes().await
|
||||
.map_err(|e| RfsError::RequestError(e))?;
|
||||
|
||||
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(RfsError::RequestError)?;
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
@@ -315,16 +362,20 @@ impl RfsClient {
|
||||
let result = block_management_api::get_blocks_by_hash_handler(&self.config, hash)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Get blocks uploaded by the current user
|
||||
pub async fn get_user_blocks(&self, page: Option<i32>, per_page: Option<i32>) -> Result<UserBlocksResponse> {
|
||||
pub async fn get_user_blocks(
|
||||
&self,
|
||||
page: Option<i32>,
|
||||
per_page: Option<i32>,
|
||||
) -> Result<UserBlocksResponse> {
|
||||
let result = block_management_api::get_user_blocks_handler(&self.config, page, per_page)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -333,11 +384,12 @@ impl RfsClient {
|
||||
// Create a temporary file to hold the block data
|
||||
let temp_dir = std::env::temp_dir();
|
||||
let temp_file_path = temp_dir.join(format!("{}-{}", file_hash, idx));
|
||||
|
||||
|
||||
// Write the data to the temporary file
|
||||
std::fs::write(&temp_file_path, &data)
|
||||
.map_err(|e| RfsError::FileSystemError(format!("Failed to write temporary block file: {}", e)))?;
|
||||
|
||||
std::fs::write(&temp_file_path, &data).map_err(|e| {
|
||||
RfsError::FileSystemError(format!("Failed to write temporary block file: {}", e))
|
||||
})?;
|
||||
|
||||
// Upload the block
|
||||
let result = block_management_api::upload_block_handler(
|
||||
&self.config,
|
||||
@@ -347,12 +399,12 @@ impl RfsClient {
|
||||
)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
// Clean up the temporary file
|
||||
if let Err(e) = std::fs::remove_file(temp_file_path) {
|
||||
eprintln!("Warning: Failed to remove temporary block file: {}", e);
|
||||
}
|
||||
|
||||
|
||||
// Return the hash from the response
|
||||
Ok(result.hash)
|
||||
}
|
||||
@@ -362,7 +414,7 @@ impl RfsClient {
|
||||
let result = flist_management_api::list_flists_handler(&self.config)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -371,7 +423,7 @@ impl RfsClient {
|
||||
let result = flist_management_api::preview_flist_handler(&self.config, flist_path)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -380,7 +432,7 @@ impl RfsClient {
|
||||
let result = website_serving_api::serve_website_handler(&self.config, website_id, path)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -389,25 +441,30 @@ impl RfsClient {
|
||||
let result = system_api::health_check_handler(&self.config)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
|
||||
Ok(result.msg)
|
||||
}
|
||||
|
||||
|
||||
/// Download an FList file
|
||||
///
|
||||
///
|
||||
/// This method downloads an FList from the server and saves it to the specified path.
|
||||
pub async fn download_flist<P: AsRef<Path>>(&self, flist_path: &str, output_path: P) -> Result<()> {
|
||||
pub async fn download_flist<P: AsRef<Path>>(
|
||||
&self,
|
||||
flist_path: &str,
|
||||
output_path: P,
|
||||
) -> Result<()> {
|
||||
let response = flist_management_api::serve_flists(&self.config, flist_path)
|
||||
.await
|
||||
.map_err(map_openapi_error)?;
|
||||
|
||||
let bytes = response.bytes().await
|
||||
.map_err(|e| RfsError::RequestError(e))?;
|
||||
|
||||
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(RfsError::RequestError)?;
|
||||
|
||||
std::fs::write(output_path, &bytes)
|
||||
.map_err(|e| RfsError::FileSystemError(e.to_string()))?;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
153
packages/clients/rfsclient/src/diff.rs
Normal file
153
packages/clients/rfsclient/src/diff.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
diff --git a/packages/clients/rfsclient/src/rhai.rs b/packages/clients/rfsclient/src/rhai.rs
|
||||
index fd686ba..b19c50f 100644
|
||||
--- a/packages/clients/rfsclient/src/rhai.rs
|
||||
+++ b/packages/clients/rfsclient/src/rhai.rs
|
||||
@@ -17,6 +17,14 @@ lazy_static! {
|
||||
static ref RUNTIME: Mutex<Option<Runtime>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
+/// Overload: list blocks with explicit pagination integers
|
||||
+fn rfs_list_blocks_with_pagination(
|
||||
+ page: rhai::INT,
|
||||
+ per_page: rhai::INT,
|
||||
+) -> Result<String, Box<EvalAltResult>> {
|
||||
+ rfs_list_blocks(Some(page), Some(per_page))
|
||||
+}
|
||||
+
|
||||
/// Wrapper around RfsClient to make it thread-safe for global usage
|
||||
struct RfsClientWrapper {
|
||||
client: Mutex<RfsClient>,
|
||||
@@ -47,6 +55,8 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>
|
||||
|
||||
// Register block management functions
|
||||
engine.register_fn("rfs_list_blocks", rfs_list_blocks);
|
||||
+ // Overload accepting explicit integer pagination params
|
||||
+ engine.register_fn("rfs_list_blocks", rfs_list_blocks_with_pagination);
|
||||
engine.register_fn("rfs_upload_block", rfs_upload_block);
|
||||
engine.register_fn("rfs_check_block", rfs_check_block);
|
||||
engine.register_fn("rfs_get_block_downloads", rfs_get_block_downloads);
|
||||
diff --git a/packages/clients/rfsclient/tests/rhai_integration_tests.rs b/packages/clients/rfsclient/tests/rhai_integration_tests.rs
|
||||
index 2c90001..cc38f4a 100644
|
||||
--- a/packages/clients/rfsclient/tests/rhai_integration_tests.rs
|
||||
+++ b/packages/clients/rfsclient/tests/rhai_integration_tests.rs
|
||||
@@ -114,8 +114,7 @@ fn test_rfs_flist_management_integration() {
|
||||
Err(e) => {
|
||||
let error_msg = e.to_string();
|
||||
println!("FList preview error: {}", error_msg);
|
||||
-
|
||||
- // Check if it's an authentication error (shouldn't happen with valid creds)
|
||||
+ // Authentication should not fail in this integration test
|
||||
if error_msg.contains("Authentication") {
|
||||
panic!("❌ Authentication should work with valid credentials: {}", error_msg);
|
||||
} else {
|
||||
@@ -141,6 +140,7 @@ fn test_rfs_create_flist_integration() {
|
||||
let create_script = format!(r#"
|
||||
rfs_create_client("{}", "{}", "{}", 30);
|
||||
rfs_authenticate();
|
||||
+ if !rfs_is_authenticated() {{ throw "Not authenticated after rfs_authenticate()"; }}
|
||||
rfs_create_flist("busybox:latest", "docker.io", "", "")
|
||||
"#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD);
|
||||
|
||||
@@ -466,10 +466,10 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
- // Test listing blocks with default pagination - using optional parameters
|
||||
+ // Test listing blocks with explicit pagination parameters
|
||||
let list_script = r#"
|
||||
- let result = rfs_list_blocks();
|
||||
- if typeof(result) != "string" {
|
||||
+ let result = rfs_list_blocks(1, 50);
|
||||
+ if result.type_of() != "string" {
|
||||
throw "Expected string result ";
|
||||
}
|
||||
true
|
||||
@@ -506,7 +506,7 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let download_script = format!(
|
||||
r#"
|
||||
let result = rfs_download_block("test_block_hash", '{}', false);
|
||||
- if typeof(result) != "string" {{
|
||||
+ if result.type_of() != "string" {{
|
||||
throw "Expected string result";
|
||||
}}
|
||||
true
|
||||
@@ -540,9 +540,9 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Test verifying blocks with a test hash
|
||||
let verify_script = r#"
|
||||
- let hashes = '["test_block_hash"]';
|
||||
+ let hashes = "[\"test_block_hash\"]";
|
||||
let result = rfs_verify_blocks(hashes);
|
||||
- if typeof(result) != "string" {
|
||||
+ if result.type_of() != "string" {
|
||||
throw "Expected string result";
|
||||
}
|
||||
true
|
||||
@@ -574,16 +574,29 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Test getting block info with a test hash
|
||||
let info_script = r#"
|
||||
let result = rfs_get_blocks_by_hash("test_block_hash");
|
||||
- if typeof(result) != "string" {
|
||||
+ if result.type_of() != "string" {
|
||||
throw "Expected string result";
|
||||
}
|
||||
true
|
||||
"#;
|
||||
|
||||
- let result: bool = engine.eval(info_script)?;
|
||||
- assert!(result, "Failed to get block info");
|
||||
-
|
||||
- Ok(())
|
||||
+ match engine.eval::<bool>(info_script) {
|
||||
+ Ok(result) => {
|
||||
+ assert!(result, "Failed to get block info");
|
||||
+ Ok(())
|
||||
+ }
|
||||
+ Err(e) => {
|
||||
+ let error_msg = e.to_string();
|
||||
+ println!("Block info error (may be expected): {}", error_msg);
|
||||
+ assert!(
|
||||
+ error_msg.contains("404") ||
|
||||
+ error_msg.contains("not found") ||
|
||||
+ error_msg.contains("OpenAPI") ||
|
||||
+ error_msg.contains("RFS error")
|
||||
+ );
|
||||
+ Ok(())
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -614,10 +627,10 @@ fn test_rfs_download_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Test downloading a file (assuming test file hash exists)
|
||||
let download_script = format!(
|
||||
r#"
|
||||
- let options = #{{ verify: false }};
|
||||
- let result = rfs_download_file("test_file_hash", '{}', options);
|
||||
- if typeof(result) != "string" {{
|
||||
- throw "Expected string result";
|
||||
+ // rfs_download_file returns unit and throws on error
|
||||
+ let result = rfs_download_file("test_file_hash", '{}', false);
|
||||
+ if result.type_of() != "()" {{
|
||||
+ throw "Expected unit return";
|
||||
}}
|
||||
true
|
||||
"#,
|
||||
@@ -839,7 +852,7 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let download_script = format!(
|
||||
r#"
|
||||
let result = rfs_download_flist("flists/test/test.fl", '{}');
|
||||
- if typeof(result) != "string" {{
|
||||
+ if result.type_of() != "string" {{
|
||||
throw "Expected string result";
|
||||
}}
|
||||
true
|
||||
@@ -874,7 +887,7 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box<dyn std::error::
|
||||
// Test waiting for FList creation with a test job ID
|
||||
let wait_script = r#"
|
||||
let result = rfs_wait_for_flist_creation("test_job_id", 10, 1000);
|
||||
- if typeof(result) != "string" {
|
||||
+ if result.type_of() != "string" {
|
||||
throw "Expected string result";
|
||||
}
|
||||
true
|
@@ -3,8 +3,8 @@
|
||||
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod types;
|
||||
pub mod rhai;
|
||||
pub mod types;
|
||||
|
||||
pub use client::RfsClient;
|
||||
pub use error::RfsError;
|
||||
|
@@ -24,7 +24,9 @@ struct RfsClientWrapper {
|
||||
|
||||
impl RfsClientWrapper {
|
||||
fn new(client: RfsClient) -> Self {
|
||||
Self { client: Mutex::new(client) }
|
||||
Self {
|
||||
client: Mutex::new(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +46,7 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>
|
||||
engine.register_fn("rfs_get_system_info", rfs_get_system_info);
|
||||
engine.register_fn("rfs_is_authenticated", rfs_is_authenticated);
|
||||
engine.register_fn("rfs_health_check", rfs_health_check);
|
||||
|
||||
|
||||
// Register block management functions
|
||||
engine.register_fn("rfs_list_blocks", rfs_list_blocks);
|
||||
engine.register_fn("rfs_list_blocks", rfs_list_blocks);
|
||||
@@ -55,11 +57,11 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>
|
||||
engine.register_fn("rfs_get_block", rfs_get_block);
|
||||
engine.register_fn("rfs_get_blocks_by_hash", rfs_get_blocks_by_hash);
|
||||
engine.register_fn("rfs_get_user_blocks", rfs_get_user_blocks);
|
||||
|
||||
|
||||
// Register file operations functions
|
||||
engine.register_fn("rfs_upload_file", rfs_upload_file);
|
||||
engine.register_fn("rfs_download_file", rfs_download_file);
|
||||
|
||||
|
||||
// Register FList management functions
|
||||
engine.register_fn("rfs_create_flist", rfs_create_flist);
|
||||
engine.register_fn("rfs_list_flists", rfs_list_flists);
|
||||
@@ -67,10 +69,10 @@ pub fn register_rfs_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>
|
||||
engine.register_fn("rfs_preview_flist", rfs_preview_flist);
|
||||
engine.register_fn("rfs_download_flist", rfs_download_flist);
|
||||
engine.register_fn("rfs_wait_for_flist_creation", rfs_wait_for_flist_creation);
|
||||
|
||||
|
||||
// Register Website functions
|
||||
engine.register_fn("rfs_get_website", rfs_get_website);
|
||||
|
||||
|
||||
// Register System and Utility functions
|
||||
engine.register_fn("rfs_is_authenticated", rfs_is_authenticated);
|
||||
engine.register_fn("rfs_health_check", rfs_health_check);
|
||||
@@ -250,7 +252,7 @@ pub fn rfs_authenticate() -> Result<bool, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Check if the client is authenticated with the RFS server
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// `true` if authenticated, `false` otherwise
|
||||
fn rfs_is_authenticated() -> Result<bool, Box<EvalAltResult>> {
|
||||
@@ -261,7 +263,7 @@ fn rfs_is_authenticated() -> Result<bool, Box<EvalAltResult>> {
|
||||
rhai::Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
Ok(client.is_authenticated())
|
||||
}
|
||||
|
||||
@@ -300,7 +302,7 @@ pub fn rfs_get_system_info() -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Check the health status of the RFS server
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// The health status as a string
|
||||
fn rfs_health_check() -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -321,10 +323,8 @@ fn rfs_health_check() -> Result<String, Box<EvalAltResult>> {
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.health_check().await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.health_check().await });
|
||||
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Health check failed: {}", e).into(),
|
||||
@@ -338,11 +338,11 @@ fn rfs_health_check() -> Result<String, Box<EvalAltResult>> {
|
||||
// =============================================================================
|
||||
|
||||
/// List all blocks with optional filtering
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `page` - Optional page number (1-based)
|
||||
/// * `per_page` - Optional number of items per page
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing block information
|
||||
fn rfs_list_blocks_impl(
|
||||
@@ -368,20 +368,18 @@ fn rfs_list_blocks_impl(
|
||||
|
||||
// Create ListBlocksParams with optional page and per_page
|
||||
let mut params = openapi::models::ListBlocksParams::new();
|
||||
|
||||
|
||||
// Convert Rhai INT to i32 for the API and set the parameters
|
||||
if let Some(p) = page.and_then(|p| p.try_into().ok()) {
|
||||
params.page = Some(Some(p));
|
||||
}
|
||||
|
||||
|
||||
if let Some(pp) = per_page.and_then(|p| p.try_into().ok()) {
|
||||
params.per_page = Some(Some(pp));
|
||||
}
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.list_blocks(Some(params)).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.list_blocks(Some(params)).await });
|
||||
|
||||
match result {
|
||||
Ok(blocks) => {
|
||||
// Convert blocks to JSON string for Rhai
|
||||
@@ -400,10 +398,10 @@ fn rfs_list_blocks_impl(
|
||||
}
|
||||
|
||||
/// Check if a block exists
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hash` - The hash of the block to check
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// `true` if the block exists, `false` otherwise
|
||||
fn rfs_check_block(hash: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||
@@ -424,10 +422,8 @@ fn rfs_check_block(hash: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.check_block(hash).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.check_block(hash).await });
|
||||
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to check block: {}", e).into(),
|
||||
@@ -437,10 +433,10 @@ fn rfs_check_block(hash: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Get block download statistics
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hash` - The hash of the block
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing download statistics
|
||||
fn rfs_get_block_downloads(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -461,10 +457,8 @@ fn rfs_get_block_downloads(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.get_block_downloads(hash).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.get_block_downloads(hash).await });
|
||||
|
||||
match result {
|
||||
Ok(stats) => {
|
||||
// Convert stats to JSON string for Rhai
|
||||
@@ -483,10 +477,10 @@ fn rfs_get_block_downloads(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Verify blocks
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hashes` - JSON array of block hashes to verify
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing verification results
|
||||
fn rfs_verify_blocks(hashes: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -525,16 +519,14 @@ fn rfs_verify_blocks(hashes: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
.into_iter()
|
||||
.map(|block_hash| openapi::models::VerifyBlock {
|
||||
block_hash: block_hash.clone(),
|
||||
block_index: 0, // Default to 0 if not specified
|
||||
file_hash: block_hash, // Using the same hash as file_hash for now
|
||||
block_index: 0, // Default to 0 if not specified
|
||||
file_hash: block_hash, // Using the same hash as file_hash for now
|
||||
})
|
||||
.collect();
|
||||
|
||||
let request = openapi::models::VerifyBlocksRequest::new(verify_blocks);
|
||||
let result = runtime.block_on(async {
|
||||
client.verify_blocks(request).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.verify_blocks(request).await });
|
||||
|
||||
match result {
|
||||
Ok(verification) => {
|
||||
// Convert verification to JSON string for Rhai
|
||||
@@ -553,10 +545,10 @@ fn rfs_verify_blocks(hashes: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Get a block by hash
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hash` - The hash of the block to retrieve
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// The block data as a byte array
|
||||
fn rfs_get_block(hash: &str) -> Result<rhai::Blob, Box<EvalAltResult>> {
|
||||
@@ -577,12 +569,10 @@ fn rfs_get_block(hash: &str) -> Result<rhai::Blob, Box<EvalAltResult>> {
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.get_block(hash).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.get_block(hash).await });
|
||||
|
||||
match result {
|
||||
Ok(bytes) => Ok(bytes.to_vec().into()),
|
||||
Ok(bytes) => Ok(bytes.to_vec()),
|
||||
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get block: {}", e).into(),
|
||||
rhai::Position::NONE,
|
||||
@@ -591,10 +581,10 @@ fn rfs_get_block(hash: &str) -> Result<rhai::Blob, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Get blocks by file hash or block hash
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hash` - The file hash or block hash to look up
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing block information
|
||||
fn rfs_get_blocks_by_hash(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -615,10 +605,8 @@ fn rfs_get_blocks_by_hash(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.get_blocks_by_hash(hash).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.get_blocks_by_hash(hash).await });
|
||||
|
||||
match result {
|
||||
Ok(blocks) => {
|
||||
// Convert blocks to JSON string for Rhai
|
||||
@@ -637,11 +625,11 @@ fn rfs_get_blocks_by_hash(hash: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Get blocks uploaded by the current user
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `page` - Optional page number (1-based)
|
||||
/// * `per_page` - Optional number of items per page
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing user's blocks information
|
||||
fn rfs_get_user_blocks_impl(
|
||||
@@ -669,10 +657,8 @@ fn rfs_get_user_blocks_impl(
|
||||
let page_i32 = page.and_then(|p| p.try_into().ok());
|
||||
let per_page_i32 = per_page.and_then(|p| p.try_into().ok());
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.get_user_blocks(page_i32, per_page_i32).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.get_user_blocks(page_i32, per_page_i32).await });
|
||||
|
||||
match result {
|
||||
Ok(user_blocks) => {
|
||||
// Convert user blocks to JSON string for Rhai
|
||||
@@ -691,15 +677,19 @@ fn rfs_get_user_blocks_impl(
|
||||
}
|
||||
|
||||
/// Upload a block to the RFS server
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `file_hash` - The hash of the file this block belongs to
|
||||
/// * `index` - The index of the block in the file
|
||||
/// * `data` - The block data as a byte array
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// The hash of the uploaded block
|
||||
fn rfs_upload_block(file_hash: &str, index: rhai::INT, data: rhai::Blob) -> Result<String, Box<EvalAltResult>> {
|
||||
fn rfs_upload_block(
|
||||
file_hash: &str,
|
||||
index: rhai::INT,
|
||||
data: rhai::Blob,
|
||||
) -> Result<String, Box<EvalAltResult>> {
|
||||
let runtime_mutex = get_runtime()?;
|
||||
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
@@ -718,15 +708,14 @@ fn rfs_upload_block(file_hash: &str, index: rhai::INT, data: rhai::Blob) -> Resu
|
||||
})?;
|
||||
|
||||
// Convert index to i64 for the API
|
||||
let index_i64 = index as i64;
|
||||
|
||||
let index_i64 = index;
|
||||
|
||||
// Convert the blob to Vec<u8>
|
||||
let data_vec = data.to_vec();
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.upload_block(file_hash, index_i64, data_vec).await
|
||||
});
|
||||
|
||||
let result =
|
||||
runtime.block_on(async { client.upload_block(file_hash, index_i64, data_vec).await });
|
||||
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to upload block: {}", e).into(),
|
||||
@@ -747,7 +736,6 @@ fn rfs_get_user_blocks(params: Map) -> Result<String, Box<EvalAltResult>> {
|
||||
rfs_get_user_blocks_impl(page, per_page)
|
||||
}
|
||||
|
||||
|
||||
/// Rhai-facing adapter: accept params map with optional keys: page, per_page
|
||||
fn rfs_list_blocks(params: Map) -> Result<String, Box<EvalAltResult>> {
|
||||
// Extract optional page and per_page from the map
|
||||
@@ -776,7 +764,11 @@ fn rfs_list_blocks(params: Map) -> Result<String, Box<EvalAltResult>> {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), Box<EvalAltResult>>` - Ok(()) if download was successful, error otherwise
|
||||
fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<(), Box<EvalAltResult>> {
|
||||
fn rfs_download_file(
|
||||
file_id: &str,
|
||||
output_path: &str,
|
||||
verify: bool,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let runtime_mutex = get_runtime()?;
|
||||
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
@@ -795,8 +787,10 @@ fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<(
|
||||
})?;
|
||||
|
||||
let download_options = Some(DownloadOptions { verify });
|
||||
let result = runtime.block_on(async {
|
||||
client.download_file(file_id, output_path, download_options).await
|
||||
let result = runtime.block_on(async {
|
||||
client
|
||||
.download_file(file_id, output_path, download_options)
|
||||
.await
|
||||
});
|
||||
|
||||
result.map_err(|e| {
|
||||
@@ -818,7 +812,11 @@ fn rfs_download_file(file_id: &str, output_path: &str, verify: bool) -> Result<(
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<String, Box<EvalAltResult>>` - File ID of the uploaded file
|
||||
pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) -> Result<String, Box<EvalAltResult>> {
|
||||
pub fn rfs_upload_file(
|
||||
file_path: &str,
|
||||
chunk_size: rhai::INT,
|
||||
verify: bool,
|
||||
) -> Result<String, Box<EvalAltResult>> {
|
||||
let runtime_mutex = get_runtime()?;
|
||||
let runtime_guard = runtime_mutex.lock().map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
@@ -838,7 +836,11 @@ pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) ->
|
||||
})?;
|
||||
|
||||
let upload_options = Some(UploadOptions {
|
||||
chunk_size: if chunk_size > 0 { Some(chunk_size as usize) } else { None },
|
||||
chunk_size: if chunk_size > 0 {
|
||||
Some(chunk_size as usize)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
verify,
|
||||
});
|
||||
|
||||
@@ -857,11 +859,11 @@ pub fn rfs_upload_file(file_path: &str, chunk_size: rhai::INT, verify: bool) ->
|
||||
// =============================================================================
|
||||
|
||||
/// Get website content from the RFS server
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `website_id` - The ID of the website
|
||||
/// * `path` - The path to the content within the website
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// The website content as a string
|
||||
fn rfs_get_website(website_id: &str, path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -882,11 +884,14 @@ fn rfs_get_website(website_id: &str, path: &str) -> Result<String, Box<EvalAltRe
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
let result = runtime.block_on(async {
|
||||
let response = client.get_website(website_id, path).await?;
|
||||
response.text().await.map_err(|e| RfsError::RequestError(e.into()))
|
||||
response
|
||||
.text()
|
||||
.await
|
||||
.map_err(RfsError::RequestError)
|
||||
});
|
||||
|
||||
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Failed to get website content: {}", e).into(),
|
||||
@@ -900,13 +905,13 @@ fn rfs_get_website(website_id: &str, path: &str) -> Result<String, Box<EvalAltRe
|
||||
// =============================================================================
|
||||
|
||||
/// Create an FList from a Docker image
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `image_name` - Docker image name (e.g., "ubuntu:20.04")
|
||||
/// * `server_address` - Optional server address (empty string if not needed)
|
||||
/// * `identity_token` - Optional identity token (empty string if not needed)
|
||||
/// * `registry_token` - Optional registry token (empty string if not needed)
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// Job ID for tracking FList creation progress
|
||||
fn rfs_create_flist(
|
||||
@@ -932,7 +937,7 @@ fn rfs_create_flist(
|
||||
rhai::Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
// Build FList options
|
||||
let mut options = crate::types::FlistOptions::default();
|
||||
if !server_address.is_empty() {
|
||||
@@ -944,9 +949,9 @@ fn rfs_create_flist(
|
||||
if !registry_token.is_empty() {
|
||||
options.registry_token = Some(registry_token.to_string());
|
||||
}
|
||||
|
||||
|
||||
let result = runtime.block_on(async { client.create_flist(image_name, Some(options)).await });
|
||||
|
||||
|
||||
result.map_err(|e| {
|
||||
Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("FList creation failed: {}", e).into(),
|
||||
@@ -956,7 +961,7 @@ fn rfs_create_flist(
|
||||
}
|
||||
|
||||
/// List all available FLists
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing FList information
|
||||
fn rfs_list_flists() -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -977,9 +982,9 @@ fn rfs_list_flists() -> Result<String, Box<EvalAltResult>> {
|
||||
rhai::Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
let result = runtime.block_on(async { client.list_flists().await });
|
||||
|
||||
|
||||
match result {
|
||||
Ok(flists) => {
|
||||
// Convert HashMap to JSON string for Rhai
|
||||
@@ -998,10 +1003,10 @@ fn rfs_list_flists() -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Get FList creation state by job ID
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `job_id` - Job ID returned from create_flist
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing FList state information
|
||||
fn rfs_get_flist_state(job_id: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -1022,9 +1027,9 @@ fn rfs_get_flist_state(job_id: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
rhai::Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
let result = runtime.block_on(async { client.get_flist_state(job_id).await });
|
||||
|
||||
|
||||
match result {
|
||||
Ok(state) => {
|
||||
// Convert state to JSON string for Rhai
|
||||
@@ -1043,10 +1048,10 @@ fn rfs_get_flist_state(job_id: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Preview an FList's contents
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `flist_path` - Path to the FList
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing FList preview information
|
||||
fn rfs_preview_flist(flist_path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -1067,9 +1072,9 @@ fn rfs_preview_flist(flist_path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
rhai::Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
let result = runtime.block_on(async { client.preview_flist(flist_path).await });
|
||||
|
||||
|
||||
match result {
|
||||
Ok(preview) => {
|
||||
// Convert preview to JSON string for Rhai
|
||||
@@ -1088,11 +1093,11 @@ fn rfs_preview_flist(flist_path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
/// Download an FList file from the RFS server
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `flist_path` - Path to the FList to download (e.g., "flists/user/example.fl")
|
||||
/// * `output_path` - Local path where the FList will be saved
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// Empty string on success, error on failure
|
||||
fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -1113,10 +1118,8 @@ fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result<String, Box
|
||||
))
|
||||
})?;
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.download_flist(flist_path, output_path).await
|
||||
});
|
||||
|
||||
let result = runtime.block_on(async { client.download_flist(flist_path, output_path).await });
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(String::new()),
|
||||
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
@@ -1127,16 +1130,16 @@ fn rfs_download_flist(flist_path: &str, output_path: &str) -> Result<String, Box
|
||||
}
|
||||
|
||||
/// Wait for an FList to be created
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `job_id` - The job ID returned by rfs_create_flist
|
||||
/// * `timeout_seconds` - Maximum time to wait in seconds (default: 300)
|
||||
/// * `poll_interval_ms` - Polling interval in milliseconds (default: 1000)
|
||||
///
|
||||
///
|
||||
/// # Returns
|
||||
/// JSON string containing the final FList state
|
||||
fn rfs_wait_for_flist_creation_impl(
|
||||
job_id: &str,
|
||||
job_id: &str,
|
||||
timeout_seconds: Option<rhai::INT>,
|
||||
poll_interval_ms: Option<rhai::INT>,
|
||||
) -> Result<String, Box<EvalAltResult>> {
|
||||
@@ -1163,10 +1166,9 @@ fn rfs_wait_for_flist_creation_impl(
|
||||
progress_callback: None,
|
||||
};
|
||||
|
||||
let result = runtime.block_on(async {
|
||||
client.wait_for_flist_creation(job_id, Some(options)).await
|
||||
});
|
||||
|
||||
let result =
|
||||
runtime.block_on(async { client.wait_for_flist_creation(job_id, Some(options)).await });
|
||||
|
||||
match result {
|
||||
Ok(state) => {
|
||||
// Convert state to JSON string for Rhai
|
||||
@@ -1198,4 +1200,4 @@ fn rfs_wait_for_flist_creation(job_id: &str, params: Map) -> Result<String, Box<
|
||||
.and_then(|d| d.clone().try_cast::<rhai::INT>());
|
||||
|
||||
rfs_wait_for_flist_creation_impl(job_id, timeout_seconds, poll_interval_ms)
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
// Re-export common types from OpenAPI client for convenience
|
||||
pub use openapi::models::{
|
||||
BlockDownloadsResponse, BlocksResponse, FileInfo,
|
||||
FileUploadResponse, FlistBody, FlistState, Job, ListBlocksResponse,
|
||||
PreviewResponse, ResponseResult, SignInResponse, VerifyBlocksResponse,
|
||||
BlockDownloadsResponse, BlocksResponse, FileInfo, FileUploadResponse, FlistBody, FlistState,
|
||||
Job, ListBlocksResponse, PreviewResponse, ResponseResult, SignInResponse, VerifyBlocksResponse,
|
||||
};
|
||||
|
||||
/// Authentication credentials for the RFS server
|
||||
@@ -74,10 +73,10 @@ pub struct FlistOptions {
|
||||
pub struct WaitOptions {
|
||||
/// Maximum time to wait in seconds
|
||||
pub timeout_seconds: u64,
|
||||
|
||||
|
||||
/// Polling interval in milliseconds
|
||||
pub poll_interval_ms: u64,
|
||||
|
||||
|
||||
/// Optional progress callback
|
||||
pub progress_callback: Option<Box<dyn Fn(&FlistState) + Send + Sync>>,
|
||||
}
|
||||
@@ -88,7 +87,14 @@ impl std::fmt::Debug for WaitOptions {
|
||||
f.debug_struct("WaitOptions")
|
||||
.field("timeout_seconds", &self.timeout_seconds)
|
||||
.field("poll_interval_ms", &self.poll_interval_ms)
|
||||
.field("progress_callback", &if self.progress_callback.is_some() { "Some(...)" } else { "None" })
|
||||
.field(
|
||||
"progress_callback",
|
||||
&if self.progress_callback.is_some() {
|
||||
"Some(...)"
|
||||
} else {
|
||||
"None"
|
||||
},
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -107,7 +113,7 @@ impl Clone for WaitOptions {
|
||||
impl Default for WaitOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
timeout_seconds: 300, // 5 minutes default timeout
|
||||
timeout_seconds: 300, // 5 minutes default timeout
|
||||
poll_interval_ms: 1000, // 1 second default polling interval
|
||||
progress_callback: None,
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
//! Integration tests for RFS client Rhai wrappers
|
||||
//!
|
||||
//! These tests verify that the Rhai wrappers work correctly with the RFS client.
|
||||
//!
|
||||
//!
|
||||
//! Test Categories:
|
||||
//! - Unit tests: Test wrapper logic without requiring a running server
|
||||
//! - Integration tests: Test with a real RFS server (when available)
|
||||
@@ -15,7 +15,14 @@ use tempfile::NamedTempFile;
|
||||
fn is_server_running(url: &str) -> bool {
|
||||
// Try to make a simple HTTP request to check if server is available
|
||||
match std::process::Command::new("curl")
|
||||
.args(["-s", "-o", "/dev/null", "-w", "%{http_code}", &format!("{}/api/v1", url)])
|
||||
.args([
|
||||
"-s",
|
||||
"-o",
|
||||
"/dev/null",
|
||||
"-w",
|
||||
"%{http_code}",
|
||||
&format!("{}/api/v1", url),
|
||||
])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => {
|
||||
@@ -39,15 +46,15 @@ const TEST_PASSWORD: &str = "password";
|
||||
fn test_rhai_engine_setup() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Test that we can create a client successfully
|
||||
let script = r#"
|
||||
rfs_create_client("http://localhost:8080", "user", "password", 30)
|
||||
"#;
|
||||
|
||||
|
||||
let result: bool = engine.eval(script)?;
|
||||
assert!(result);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -56,15 +63,15 @@ fn test_rhai_engine_setup() -> Result<(), Box<EvalAltResult>> {
|
||||
fn test_rfs_create_client() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
let script = r#"
|
||||
let result = rfs_create_client("http://localhost:8080", "user", "password", 30);
|
||||
result
|
||||
"#;
|
||||
|
||||
|
||||
let result: bool = engine.eval(script)?;
|
||||
assert!(result);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -73,15 +80,15 @@ fn test_rfs_create_client() -> Result<(), Box<EvalAltResult>> {
|
||||
fn test_rfs_create_client_no_credentials() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
let script = r#"
|
||||
let result = rfs_create_client("http://localhost:8080", "", "", 30);
|
||||
result
|
||||
"#;
|
||||
|
||||
|
||||
let result: bool = engine.eval(script)?;
|
||||
assert!(result);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -92,36 +99,48 @@ fn test_rfs_flist_management_integration() {
|
||||
println!("Skipping FList integration test - no server detected");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).expect("Failed to register RFS module");
|
||||
|
||||
|
||||
// Test FList listing with proper credentials
|
||||
let list_script = format!(r#"
|
||||
let list_script = format!(
|
||||
r#"
|
||||
rfs_create_client("{}", "{}", "{}", 30);
|
||||
rfs_authenticate();
|
||||
rfs_list_flists()
|
||||
"#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
let result = engine.eval::<String>(&list_script);
|
||||
match result {
|
||||
Ok(flists_json) => {
|
||||
println!("FLists retrieved: {}", flists_json);
|
||||
// Should be valid JSON
|
||||
assert!(serde_json::from_str::<serde_json::Value>(&flists_json).is_ok(),
|
||||
"FList data should be valid JSON");
|
||||
assert!(
|
||||
serde_json::from_str::<serde_json::Value>(&flists_json).is_ok(),
|
||||
"FList data should be valid JSON"
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
let error_msg = e.to_string();
|
||||
println!("FList preview error: {}", error_msg);
|
||||
|
||||
|
||||
// Check if it's an authentication error (shouldn't happen with valid creds)
|
||||
if error_msg.contains("Authentication") {
|
||||
panic!("❌ Authentication should work with valid credentials: {}", error_msg);
|
||||
panic!(
|
||||
"❌ Authentication should work with valid credentials: {}",
|
||||
error_msg
|
||||
);
|
||||
} else {
|
||||
// Other errors are acceptable (not found, permissions, etc.)
|
||||
println!("Server error (may be expected): {}", error_msg);
|
||||
assert!(error_msg.contains("OpenAPI") || error_msg.contains("FList") || error_msg.contains("not found"));
|
||||
assert!(
|
||||
error_msg.contains("OpenAPI")
|
||||
|| error_msg.contains("FList")
|
||||
|| error_msg.contains("not found")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,23 +152,26 @@ fn test_rfs_create_flist_integration() {
|
||||
println!("Skipping FList creation test - no server detected");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).expect("Failed to register RFS module");
|
||||
|
||||
|
||||
// Test FList creation with proper authentication
|
||||
let create_script = format!(r#"
|
||||
let create_script = format!(
|
||||
r#"
|
||||
rfs_create_client("{}", "{}", "{}", 30);
|
||||
rfs_authenticate();
|
||||
rfs_create_flist("busybox:latest", "docker.io", "", "")
|
||||
"#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
let result = engine.eval::<String>(&create_script);
|
||||
match result {
|
||||
Ok(job_id) => {
|
||||
println!("✅ FList creation job started: {}", job_id);
|
||||
assert!(!job_id.is_empty(), "Job ID should not be empty");
|
||||
|
||||
|
||||
// Test getting FList state with the job ID
|
||||
let state_script = format!("rfs_get_flist_state(\"{}\")", job_id);
|
||||
let state_result = engine.eval::<String>(&state_script);
|
||||
@@ -166,12 +188,15 @@ fn test_rfs_create_flist_integration() {
|
||||
Err(e) => {
|
||||
let error_msg = e.to_string();
|
||||
println!("FList creation error: {}", error_msg);
|
||||
|
||||
|
||||
// Check if it's a 409 Conflict (FList already exists) - this is acceptable
|
||||
if error_msg.contains("409 Conflict") {
|
||||
println!("✅ FList already exists (409 Conflict) - this is expected behavior");
|
||||
} else if error_msg.contains("Authentication") {
|
||||
panic!("❌ Authentication should work with valid credentials: {}", error_msg);
|
||||
panic!(
|
||||
"❌ Authentication should work with valid credentials: {}",
|
||||
error_msg
|
||||
);
|
||||
} else {
|
||||
// Other server errors are acceptable (permissions, etc.)
|
||||
println!("Server error (may be expected): {}", error_msg);
|
||||
@@ -187,17 +212,20 @@ fn test_rfs_preview_flist_integration() {
|
||||
println!("Skipping FList preview test - no server detected");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).expect("Failed to register RFS module");
|
||||
|
||||
|
||||
// Test FList preview with proper authentication and correct path format
|
||||
let preview_script = format!(r#"
|
||||
let preview_script = format!(
|
||||
r#"
|
||||
rfs_create_client("{}", "{}", "{}", 30);
|
||||
rfs_authenticate();
|
||||
rfs_preview_flist("flists/user/alpine-latest.fl")
|
||||
"#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
let result = engine.eval::<String>(&preview_script);
|
||||
match result {
|
||||
Ok(preview_json) => {
|
||||
@@ -206,10 +234,17 @@ fn test_rfs_preview_flist_integration() {
|
||||
}
|
||||
Err(e) => {
|
||||
let error_msg = e.to_string();
|
||||
println!("Expected FList preview error (not found/auth): {}", error_msg);
|
||||
println!(
|
||||
"Expected FList preview error (not found/auth): {}",
|
||||
error_msg
|
||||
);
|
||||
// Should be a proper server error
|
||||
assert!(error_msg.contains("Authentication") || error_msg.contains("OpenAPI") ||
|
||||
error_msg.contains("FList") || error_msg.contains("not found"));
|
||||
assert!(
|
||||
error_msg.contains("Authentication")
|
||||
|| error_msg.contains("OpenAPI")
|
||||
|| error_msg.contains("FList")
|
||||
|| error_msg.contains("not found")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,12 +254,12 @@ fn test_rfs_preview_flist_integration() {
|
||||
fn test_rfs_get_system_info_wrapper() {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
let script = r#"
|
||||
rfs_create_client("http://localhost:8080", "", "", 30);
|
||||
rfs_get_system_info()
|
||||
"#;
|
||||
|
||||
|
||||
let result = engine.eval::<String>(script);
|
||||
match result {
|
||||
Ok(info) => {
|
||||
@@ -246,12 +281,12 @@ fn test_rfs_get_system_info_wrapper() {
|
||||
fn test_rfs_authenticate_wrapper() {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
let script = r#"
|
||||
rfs_create_client("http://localhost:8080", "user", "password", 30);
|
||||
rfs_authenticate()
|
||||
"#;
|
||||
|
||||
|
||||
let result = engine.eval::<bool>(script);
|
||||
match result {
|
||||
Ok(success) => {
|
||||
@@ -273,17 +308,20 @@ fn test_rfs_authenticate_wrapper() {
|
||||
fn test_rfs_upload_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a temporary file for testing
|
||||
let temp_file = NamedTempFile::new()?;
|
||||
fs::write(&temp_file, b"test content")?;
|
||||
let file_path = temp_file.path().to_string_lossy();
|
||||
|
||||
let script = format!(r#"
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
rfs_create_client("http://localhost:8080", "", "", 30);
|
||||
rfs_upload_file("{}", 0, false)
|
||||
"#, file_path);
|
||||
|
||||
"#,
|
||||
file_path
|
||||
);
|
||||
|
||||
let result = engine.eval::<String>(&script);
|
||||
match result {
|
||||
Ok(upload_result) => {
|
||||
@@ -298,7 +336,7 @@ fn test_rfs_upload_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
assert!(error_msg.contains("RFS error") || error_msg.contains("OpenAPI"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -307,7 +345,7 @@ fn test_rfs_upload_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_complete_rhai_script() {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
let script = r#"
|
||||
// Create client
|
||||
let client_created = rfs_create_client("http://localhost:8080", "user", "password", 60);
|
||||
@@ -315,7 +353,7 @@ fn test_complete_rhai_script() {
|
||||
// Return success if we got this far
|
||||
client_created
|
||||
"#;
|
||||
|
||||
|
||||
let result: bool = engine.eval(script).unwrap();
|
||||
assert!(result);
|
||||
}
|
||||
@@ -325,17 +363,17 @@ fn test_complete_rhai_script() {
|
||||
fn test_error_handling() {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
// Test calling a protected endpoint without authentication - should fail
|
||||
// Note: get_system_info is NOT protected, but create_flist IS protected
|
||||
let script = r#"
|
||||
rfs_create_client("http://localhost:8080", "", "", 30);
|
||||
rfs_create_flist("test:latest", "docker.io", "", "")
|
||||
"#;
|
||||
|
||||
|
||||
let result = engine.eval::<String>(script);
|
||||
assert!(result.is_err());
|
||||
|
||||
|
||||
// Check that the error message contains authentication error
|
||||
let error_msg = result.unwrap_err().to_string();
|
||||
println!("Expected authentication error: {}", error_msg);
|
||||
@@ -347,23 +385,26 @@ fn test_error_handling() {
|
||||
fn test_rfs_is_authenticated_wrapper() {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
// Test without authenticating first
|
||||
let script1 = r#"
|
||||
rfs_create_client("http://localhost:8080", "", "", 30);
|
||||
rfs_is_authenticated()
|
||||
"#;
|
||||
|
||||
|
||||
let result1 = engine.eval::<bool>(script1).unwrap();
|
||||
assert!(!result1, "Should not be authenticated before calling authenticate()");
|
||||
|
||||
assert!(
|
||||
!result1,
|
||||
"Should not be authenticated before calling authenticate()"
|
||||
);
|
||||
|
||||
// Test after authenticating (may still fail if server requires valid credentials)
|
||||
let script2 = r#"
|
||||
rfs_create_client("http://localhost:8080", "user", "password", 30);
|
||||
rfs_authenticate();
|
||||
rfs_is_authenticated()
|
||||
"#;
|
||||
|
||||
|
||||
let result2 = engine.eval::<bool>(script2);
|
||||
match result2 {
|
||||
Ok(auth_status) => {
|
||||
@@ -382,12 +423,12 @@ fn test_rfs_is_authenticated_wrapper() {
|
||||
fn test_rfs_health_check_wrapper() {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
let script = r#"
|
||||
rfs_create_client("http://localhost:8080", "", "", 30);
|
||||
rfs_health_check()
|
||||
"#;
|
||||
|
||||
|
||||
let result = engine.eval::<String>(script);
|
||||
match result {
|
||||
Ok(health_status) => {
|
||||
@@ -400,9 +441,9 @@ fn test_rfs_health_check_wrapper() {
|
||||
println!("Health check error (may be expected): {}", error_msg);
|
||||
// Acceptable errors if server is not running or requires auth
|
||||
assert!(
|
||||
error_msg.contains("RFS error") ||
|
||||
error_msg.contains("OpenAPI") ||
|
||||
error_msg.contains("failed")
|
||||
error_msg.contains("RFS error")
|
||||
|| error_msg.contains("OpenAPI")
|
||||
|| error_msg.contains("failed")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -415,17 +456,20 @@ fn test_rfs_get_website_wrapper() {
|
||||
println!("Skipping website test - no server detected");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
// Test with a non-existent website (should fail gracefully)
|
||||
let script = format!(r#"
|
||||
let script = format!(
|
||||
r#"
|
||||
rfs_create_client("{}", "{}", "{}", 30);
|
||||
rfs_authenticate();
|
||||
rfs_get_website("nonexistent-website", "index.html")
|
||||
"#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
let result = engine.eval::<String>(&script);
|
||||
match result {
|
||||
Ok(content) => {
|
||||
@@ -437,10 +481,10 @@ fn test_rfs_get_website_wrapper() {
|
||||
let error_msg = e.to_string();
|
||||
println!("Expected website error: {}", error_msg);
|
||||
assert!(
|
||||
error_msg.contains("404") ||
|
||||
error_msg.contains("not found") ||
|
||||
error_msg.contains("OpenAPI") ||
|
||||
error_msg.contains("RFS error")
|
||||
error_msg.contains("404")
|
||||
|| error_msg.contains("not found")
|
||||
|| error_msg.contains("OpenAPI")
|
||||
|| error_msg.contains("RFS error")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -455,7 +499,7 @@ fn test_rfs_get_website_wrapper() {
|
||||
fn test_rfs_list_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -463,10 +507,10 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Authenticate before invoking operations that require it
|
||||
let auth_script = r#"
|
||||
rfs_authenticate()
|
||||
@@ -481,10 +525,10 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
true
|
||||
"#;
|
||||
|
||||
|
||||
let result: bool = engine.eval(list_script)?;
|
||||
assert!(result, "Failed to list blocks");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -493,7 +537,7 @@ fn test_rfs_list_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_rfs_download_block_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -501,18 +545,18 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Authenticate before invoking operations that require it
|
||||
let authed: bool = engine.eval(r#" rfs_authenticate() "#)?;
|
||||
assert!(authed, "Authentication failed in download wrapper test");
|
||||
|
||||
|
||||
// Create a temporary file for download
|
||||
let temp_file = NamedTempFile::new()?;
|
||||
let temp_path = temp_file.path().to_str().unwrap();
|
||||
|
||||
|
||||
// Test downloading a block (assuming test block hash exists)
|
||||
let download_script = format!(
|
||||
r#"
|
||||
@@ -522,13 +566,13 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}}
|
||||
true
|
||||
"#,
|
||||
temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths
|
||||
temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths
|
||||
);
|
||||
|
||||
|
||||
// This might fail if the test block doesn't exist, but we're testing the wrapper, not the actual download
|
||||
let result: bool = engine.eval(&download_script).unwrap_or_else(|_| true);
|
||||
assert!(result, "Failed to execute download block script");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -537,7 +581,7 @@ fn test_rfs_download_block_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_rfs_verify_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -545,10 +589,10 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Test verifying blocks with a test hash
|
||||
let verify_script = r#"
|
||||
let hashes = "[\"test_block_hash\"]";
|
||||
@@ -558,10 +602,10 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}}
|
||||
true
|
||||
"#;
|
||||
|
||||
|
||||
let result: bool = engine.eval(verify_script)?;
|
||||
assert!(result, "Failed to verify blocks");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -570,7 +614,7 @@ fn test_rfs_verify_blocks_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_rfs_get_block_info_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -578,10 +622,10 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Test getting block info with a test hash
|
||||
let info_script = r#"
|
||||
let result = rfs_get_blocks_by_hash("test_block_hash");
|
||||
@@ -590,7 +634,7 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
true
|
||||
"#;
|
||||
|
||||
|
||||
match engine.eval::<bool>(info_script) {
|
||||
Ok(result) => {
|
||||
assert!(result, "Failed to get block info");
|
||||
@@ -600,10 +644,10 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let error_msg = e.to_string();
|
||||
println!("Block info error (may be expected): {}", error_msg);
|
||||
assert!(
|
||||
error_msg.contains("404") ||
|
||||
error_msg.contains("not found") ||
|
||||
error_msg.contains("OpenAPI") ||
|
||||
error_msg.contains("RFS error")
|
||||
error_msg.contains("404")
|
||||
|| error_msg.contains("not found")
|
||||
|| error_msg.contains("OpenAPI")
|
||||
|| error_msg.contains("RFS error")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -619,7 +663,7 @@ fn test_rfs_get_block_info_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_rfs_download_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -627,14 +671,14 @@ fn test_rfs_download_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Create a temporary file for download
|
||||
let temp_file = NamedTempFile::new()?;
|
||||
let temp_path = temp_file.path().to_str().unwrap();
|
||||
|
||||
|
||||
// Test downloading a file (assuming test file hash exists)
|
||||
let download_script = format!(
|
||||
r#"
|
||||
@@ -645,13 +689,13 @@ fn test_rfs_download_file_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}}
|
||||
true
|
||||
"#,
|
||||
temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths
|
||||
temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths
|
||||
);
|
||||
|
||||
|
||||
// This might fail if the test file doesn't exist, but we're testing the wrapper
|
||||
let result: bool = engine.eval(&download_script).unwrap_or_else(|_| true);
|
||||
assert!(result, "Failed to execute download file script");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -678,7 +722,7 @@ fn test_flist_operations_workflow() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let temp_dir = tempfile::tempdir()?;
|
||||
let output_path = temp_dir.path().join("downloaded_flist.fl");
|
||||
let output_path_str = output_path.to_str().unwrap();
|
||||
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).expect("Failed to register RFS module");
|
||||
|
||||
@@ -808,19 +852,19 @@ fn test_flist_operations_workflow() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
// Add a helper function to parse JSON in Rhai
|
||||
engine.register_fn("parse_json", |json_str: &str| -> String {
|
||||
// Just return the JSON string as is - Rhai can work with it directly
|
||||
json_str.to_string()
|
||||
});
|
||||
|
||||
|
||||
// Execute the script
|
||||
match engine.eval::<bool>(&script) {
|
||||
Ok(success) => {
|
||||
assert!(success, "FList operations workflow test failed");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error in FList operations workflow test: {}", e);
|
||||
// Don't fail the test if the server doesn't have the expected data
|
||||
@@ -843,7 +887,7 @@ fn test_flist_operations_workflow() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_rfs_download_flist_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -851,14 +895,14 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Create a temporary file for download
|
||||
let temp_file = NamedTempFile::new()?;
|
||||
let temp_path = temp_file.path().to_str().unwrap();
|
||||
|
||||
|
||||
// Test downloading an FList (assuming test flist exists)
|
||||
let download_script = format!(
|
||||
r#"
|
||||
@@ -868,13 +912,13 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}}
|
||||
true
|
||||
"#,
|
||||
temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths
|
||||
temp_path.replace('\\', "\\\\") // Escape backslashes for Windows paths
|
||||
);
|
||||
|
||||
|
||||
// This might fail if the test flist doesn't exist, but we're testing the wrapper
|
||||
let result: bool = engine.eval(&download_script).unwrap_or_else(|_| true);
|
||||
assert!(result, "Failed to execute download flist script");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -883,7 +927,7 @@ fn test_rfs_download_flist_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine)?;
|
||||
|
||||
|
||||
// Create a client first
|
||||
let create_script = format!(
|
||||
r#"
|
||||
@@ -891,14 +935,14 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box<dyn std::error::
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
|
||||
let result: bool = engine.eval(&create_script)?;
|
||||
assert!(result, "Failed to create RFS client");
|
||||
|
||||
|
||||
// Authenticate before invoking operations that require it
|
||||
let authed: bool = engine.eval(r#" rfs_authenticate() "#)?;
|
||||
assert!(authed, "Authentication failed in wait wrapper test");
|
||||
|
||||
|
||||
// Intentionally use a dummy job id and assert the wrapper returns a meaningful error
|
||||
let wait_script = r#"
|
||||
// This call should fail because the job id is dummy; we want to see the error path
|
||||
@@ -907,13 +951,20 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box<dyn std::error::
|
||||
|
||||
let eval_res = engine.eval::<String>(wait_script);
|
||||
match eval_res {
|
||||
Ok(s) => panic!("Expected failure for dummy job id, but got success with result: {}", s),
|
||||
Ok(s) => panic!(
|
||||
"Expected failure for dummy job id, but got success with result: {}",
|
||||
s
|
||||
),
|
||||
Err(e) => {
|
||||
let msg = e.to_string();
|
||||
assert!(msg.contains("Operation timed out"), "Unexpected error message: {}", msg);
|
||||
assert!(
|
||||
msg.contains("Operation timed out"),
|
||||
"Unexpected error message: {}",
|
||||
msg
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -925,18 +976,24 @@ fn test_rfs_wait_for_flist_creation_wrapper() -> Result<(), Box<dyn std::error::
|
||||
#[test]
|
||||
fn test_rfs_get_system_info_with_server() {
|
||||
if !is_server_running(TEST_SERVER_URL) {
|
||||
println!("Skipping integration test - no RFS server running at {}", TEST_SERVER_URL);
|
||||
println!(
|
||||
"Skipping integration test - no RFS server running at {}",
|
||||
TEST_SERVER_URL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
let script = format!(r#"
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
rfs_create_client("{}", "", "", 30);
|
||||
rfs_get_system_info()
|
||||
"#, TEST_SERVER_URL);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL
|
||||
);
|
||||
|
||||
let result = engine.eval::<String>(&script);
|
||||
match result {
|
||||
Ok(info) => {
|
||||
@@ -954,19 +1011,25 @@ fn test_rfs_get_system_info_with_server() {
|
||||
#[test]
|
||||
fn test_rfs_authenticate_with_server() {
|
||||
if !is_server_running(TEST_SERVER_URL) {
|
||||
println!("Skipping integration test - no RFS server running at {}", TEST_SERVER_URL);
|
||||
println!(
|
||||
"Skipping integration test - no RFS server running at {}",
|
||||
TEST_SERVER_URL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
|
||||
// Test with dummy credentials (will likely fail, but tests the flow)
|
||||
let script = format!(r#"
|
||||
let script = format!(
|
||||
r#"
|
||||
rfs_create_client("{}", "{}", "{}", 30);
|
||||
rfs_authenticate()
|
||||
"#, TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL, TEST_USERNAME, TEST_PASSWORD
|
||||
);
|
||||
|
||||
let result = engine.eval::<bool>(&script);
|
||||
match result {
|
||||
Ok(success) => {
|
||||
@@ -974,7 +1037,10 @@ fn test_rfs_authenticate_with_server() {
|
||||
assert!(success);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Expected authentication failure with dummy credentials: {}", e);
|
||||
println!(
|
||||
"Expected authentication failure with dummy credentials: {}",
|
||||
e
|
||||
);
|
||||
// This is expected with dummy credentials
|
||||
assert!(e.to_string().contains("Authentication failed"));
|
||||
}
|
||||
@@ -985,14 +1051,18 @@ fn test_rfs_authenticate_with_server() {
|
||||
#[test]
|
||||
fn test_complete_workflow_with_server() {
|
||||
if !is_server_running(TEST_SERVER_URL) {
|
||||
println!("Skipping integration test - no RFS server running at {}", TEST_SERVER_URL);
|
||||
println!(
|
||||
"Skipping integration test - no RFS server running at {}",
|
||||
TEST_SERVER_URL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_rfs_module(&mut engine).unwrap();
|
||||
|
||||
let script = format!(r#"
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
// Create client
|
||||
let client_created = rfs_create_client("{}", "", "", 60);
|
||||
print("Client created: " + client_created);
|
||||
@@ -1003,8 +1073,10 @@ fn test_complete_workflow_with_server() {
|
||||
|
||||
// Return success
|
||||
client_created && info_result.len() > 0
|
||||
"#, TEST_SERVER_URL);
|
||||
|
||||
"#,
|
||||
TEST_SERVER_URL
|
||||
);
|
||||
|
||||
let result = engine.eval::<bool>(&script);
|
||||
match result {
|
||||
Ok(success) => {
|
||||
|
Reference in New Issue
Block a user