get_specific_entity.rs
1 //! Example: Get Specific Entity 2 //! 3 //! This example demonstrates how to retrieve a specific entity from a Backstage catalog 4 //! using its kind, namespace, and name. 5 //! 6 //! Usage: 7 //! BACKSTAGE_URL=https://your-backstage.com BACKSTAGE_TOKEN=your-token cargo run --example get_specific_entity 8 9 use backstage_client::{entities::Entity, BackstageClient, ClientError}; 10 use log::{error, info, warn}; 11 use std::env; 12 13 #[tokio::main] 14 async fn main() -> Result<(), Box<dyn std::error::Error>> { 15 // Initialize logging 16 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("INFO")).init(); 17 18 // Get configuration from environment variables 19 let base_url = 20 env::var("BACKSTAGE_URL").unwrap_or_else(|_| "https://demo.backstage.io".to_string()); 21 let token = 22 env::var("BACKSTAGE_TOKEN").expect("BACKSTAGE_TOKEN environment variable must be set"); 23 24 info!("Connecting to Backstage at: {}", base_url); 25 26 // Create a new Backstage client 27 let client = BackstageClient::new(&base_url, &token)?; 28 29 // Example 1: Get a specific component by name (using default namespace) 30 info!("\n=== Getting specific component ==="); 31 let entity_name = env::var("ENTITY_NAME").unwrap_or_else(|_| "my-service".to_string()); 32 33 match client 34 .get_entity::<Entity>("Component", None, &entity_name) 35 .await 36 { 37 Ok(entity) => { 38 display_entity_details(&entity); 39 } 40 Err(ClientError::ApiError { status: 404, .. }) => { 41 warn!("Component '{}' not found in default namespace", entity_name); 42 info!("Try setting ENTITY_NAME environment variable to an existing component"); 43 } 44 Err(e) => { 45 error!("Error fetching component '{}': {}", entity_name, e); 46 } 47 } 48 49 // Example 2: Get a specific API with explicit namespace 50 info!("\n=== Getting specific API with namespace ==="); 51 match client 52 .get_entity::<Entity>("API", Some("default"), "petstore-api") 53 .await 54 { 55 Ok(entity) => { 56 display_entity_details(&entity); 57 } 58 Err(ClientError::ApiError { status: 404, .. }) => { 59 warn!("API 'petstore-api' not found in 'default' namespace"); 60 } 61 Err(e) => { 62 error!("Error fetching API: {}", e); 63 } 64 } 65 66 // Example 3: Get a user entity 67 info!("\n=== Getting specific user ==="); 68 let username = env::var("USERNAME").unwrap_or_else(|_| "guest".to_string()); 69 70 match client 71 .get_entity::<Entity>("User", Some("default"), &username) 72 .await 73 { 74 Ok(entity) => { 75 display_entity_details(&entity); 76 } 77 Err(ClientError::ApiError { status: 404, .. }) => { 78 warn!("User '{}' not found", username); 79 info!("Try setting USERNAME environment variable to an existing user"); 80 } 81 Err(e) => { 82 error!("Error fetching user '{}': {}", username, e); 83 } 84 } 85 86 // Example 4: Get a system entity 87 info!("\n=== Getting specific system ==="); 88 match client 89 .get_entity::<Entity>("System", Some("default"), "payment-system") 90 .await 91 { 92 Ok(entity) => { 93 display_entity_details(&entity); 94 } 95 Err(ClientError::ApiError { status: 404, .. }) => { 96 warn!("System 'payment-system' not found"); 97 } 98 Err(e) => { 99 error!("Error fetching system: {}", e); 100 } 101 } 102 103 // Example 5: Demonstrate error handling for invalid entity kinds 104 info!("\n=== Testing error handling ==="); 105 match client 106 .get_entity::<Entity>("", Some("default"), "test") 107 .await 108 { 109 Ok(_) => { 110 warn!("Unexpected success with empty kind"); 111 } 112 Err(ClientError::InvalidFilter(msg)) => { 113 info!("Correctly caught validation error: {}", msg); 114 } 115 Err(e) => { 116 error!("Unexpected error: {}", e); 117 } 118 } 119 120 info!("\n=== Get Specific Entity Examples Complete ==="); 121 info!("You can customize the examples by setting these environment variables:"); 122 info!(" ENTITY_NAME - Name of component to fetch (default: my-service)"); 123 info!(" USERNAME - Name of user to fetch (default: guest)"); 124 125 Ok(()) 126 } 127 128 /// Display detailed information about an entity 129 fn display_entity_details(entity: &Entity) { 130 info!("📋 Entity Details:"); 131 info!(" Kind: {}", entity.kind()); 132 info!(" Name: {}", entity.name()); 133 info!(" Namespace: {}", entity.namespace()); 134 info!(" Entity Ref: {}", entity.entity_ref()); 135 136 if let Some(description) = entity.description() { 137 info!(" Description: {}", description); 138 } 139 140 // Display metadata 141 let metadata = entity.metadata(); 142 if let Some(uid) = &metadata.uid { 143 info!(" UID: {}", uid); 144 } 145 146 // Display tags 147 if let Some(tags) = entity.tags() { 148 if !tags.is_empty() { 149 info!(" Tags: {}", tags.join(", ")); 150 } 151 } 152 153 // Display annotations 154 if let Some(annotations) = entity.annotations() { 155 if !annotations.is_empty() { 156 info!(" Annotations:"); 157 for (key, value) in annotations.iter().take(5) { 158 info!(" {}: {}", key, value); 159 } 160 if annotations.len() > 5 { 161 info!(" ... and {} more annotations", annotations.len() - 5); 162 } 163 } 164 } 165 166 // Display relations 167 if let Some(relations) = entity.relations() { 168 if !relations.is_empty() { 169 info!(" Relations:"); 170 for relation in relations.iter().take(3) { 171 info!( 172 " {} -> {}:{}/{}", 173 relation.r#type, 174 relation.target.kind, 175 relation.target.namespace, 176 relation.target.name 177 ); 178 } 179 if relations.len() > 3 { 180 info!(" ... and {} more relations", relations.len() - 3); 181 } 182 } 183 } 184 185 // Show some useful annotations if they exist 186 if let Some(view_url) = entity.get_annotation("backstage.io/view-url") { 187 info!(" 🔗 View URL: {}", view_url); 188 } 189 190 if let Some(source_location) = entity.get_annotation("backstage.io/managed-by-location") { 191 info!(" 📍 Source: {}", source_location); 192 } 193 194 info!(""); // Empty line for readability 195 }