/ examples / get_specific_entity.rs
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  }