Implement rfs-client
This commit is contained in:
		
							
								
								
									
										42
									
								
								rfs-client/examples/authentication.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								rfs-client/examples/authentication.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| use rfs_client::RfsClient; | ||||
| use rfs_client::types::{ClientConfig, Credentials}; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // Create a client with authentication credentials | ||||
|     let config = ClientConfig { | ||||
|         base_url: "http://localhost:8080".to_string(), | ||||
|         credentials: Some(Credentials { | ||||
|             username: "user".to_string(), | ||||
|             password: "password".to_string(), | ||||
|         }), | ||||
|         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() { | ||||
|         println!("Authentication successful"); | ||||
|     } else { | ||||
|         println!("Authentication failed"); | ||||
|     } | ||||
|  | ||||
|     // Create a client without authentication | ||||
|     let config_no_auth = ClientConfig { | ||||
|         base_url: "http://localhost:8080".to_string(), | ||||
|         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(()) | ||||
| } | ||||
							
								
								
									
										128
									
								
								rfs-client/examples/block_management.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								rfs-client/examples/block_management.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| use rfs_client::RfsClient; | ||||
| use rfs_client::types::{ClientConfig, Credentials}; | ||||
| use openapi::models::{VerifyBlock, VerifyBlocksRequest}; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // Create a client with authentication | ||||
|     let config = ClientConfig { | ||||
|         base_url: "http://localhost:8080".to_string(), | ||||
|         credentials: Some(Credentials { | ||||
|             username: "user".to_string(), | ||||
|             password: "password".to_string(), | ||||
|         }), | ||||
|         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); | ||||
|     } | ||||
|      | ||||
|     // 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<_>>(); | ||||
|  | ||||
|     // Create the request with the complete block information | ||||
|     for block in verify_blocks.iter() { | ||||
|         println!("Block: {}", block.block_hash); | ||||
|         println!("Block index: {}", block.block_index); | ||||
|         println!("File hash: {}", block.file_hash); | ||||
|     } | ||||
|     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()); | ||||
|     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) | ||||
|             .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; | ||||
|         println!("\n2. Checking if block exists: {}", block_to_check); | ||||
|         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); | ||||
|         let downloads = client.get_block_downloads(block_to_check).await?; | ||||
|         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; | ||||
|         println!("\n4. Getting content for block: {}", block_to_get); | ||||
|         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); | ||||
|     for block in user_blocks.blocks.iter().take(3) { | ||||
|         println!("  - Block: {}, Index: {}", block.hash, block.index); | ||||
|     } | ||||
|      | ||||
|     // 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?; | ||||
|     println!("Uploaded block with hash: {}", block_hash); | ||||
|      | ||||
|     // Clean up | ||||
|     std::fs::remove_file(test_file_path)?; | ||||
|     println!("Test file cleaned up"); | ||||
|      | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										64
									
								
								rfs-client/examples/file_management.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								rfs-client/examples/file_management.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| use rfs_client::RfsClient; | ||||
| use rfs_client::types::{ClientConfig, Credentials, UploadOptions, DownloadOptions}; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // Create a client with authentication | ||||
|     let config = ClientConfig { | ||||
|         base_url: "http://localhost:8080".to_string(), | ||||
|         credentials: Some(Credentials { | ||||
|             username: "user".to_string(), | ||||
|             password: "password".to_string(), | ||||
|         }), | ||||
|         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?; | ||||
|     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?; | ||||
|     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(()) | ||||
| } | ||||
							
								
								
									
										171
									
								
								rfs-client/examples/flist_operations.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								rfs-client/examples/flist_operations.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| use rfs_client::RfsClient; | ||||
| use rfs_client::types::{ClientConfig, Credentials, FlistOptions, WaitOptions}; | ||||
| use std::path::Path; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let parent_dir = "flists"; | ||||
|     // Create a client with authentication | ||||
|     let config = ClientConfig { | ||||
|         base_url: "http://localhost:8080".to_string(), | ||||
|         credentials: Some(Credentials { | ||||
|             username: "user".to_string(), | ||||
|             password: "password".to_string(), | ||||
|         }), | ||||
|         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, | ||||
|         username: None, | ||||
|         password: None, | ||||
|         email: None, | ||||
|         server_address: Some("docker.io".to_string()), | ||||
|         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"); | ||||
|                 None | ||||
|             } else { | ||||
|                 return Err(e.into()); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     // 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 | ||||
|             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 { | ||||
|             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 | ||||
|                     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); | ||||
|                         flist_path_for_preview = Some(path); | ||||
|                     } | ||||
|                 } | ||||
|                 if files.len() > 2 { | ||||
|                     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); | ||||
|                 match client.preview_flist(flist_path).await { | ||||
|                     Ok(preview) => { | ||||
|                         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); | ||||
|                             } | ||||
|                             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!("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(()) | ||||
| } | ||||
							
								
								
									
										61
									
								
								rfs-client/examples/wait_for_flist.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								rfs-client/examples/wait_for_flist.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| use rfs_client::RfsClient; | ||||
| use rfs_client::types::{ClientConfig, Credentials, WaitOptions}; | ||||
| use openapi::models::FlistState; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     // Create a client with authentication | ||||
|     let config = ClientConfig { | ||||
|         base_url: "http://localhost:8080".to_string(), | ||||
|         credentials: Some(Credentials { | ||||
|             username: "user".to_string(), | ||||
|             password: "password".to_string(), | ||||
|         }), | ||||
|         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 | ||||
|         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), | ||||
|             } | ||||
|         })), | ||||
|     }; | ||||
|      | ||||
|     // 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 | ||||
|         .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?; | ||||
|      | ||||
|     println!("FList created successfully!"); | ||||
|     println!("Final state: {:?}", state); | ||||
|      | ||||
|     Ok(()) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user