03_service_layer_intro/
03_service_layer_intro.rs

1//! Service Layer Introduction
2//!
3//! This example demonstrates:
4//! - Using the service layer instead of direct store access
5//! - Benefits of the service abstraction
6//! - Thread-safe concurrent access with Arc<RwLock<Store>>
7//!
8//! Scenario: Same operations as example 02, but using the service layer.
9//!
10//! Run with: `cargo run --example 03_service_layer_intro`
11
12use std::sync::{Arc, RwLock};
13use wami::provider::AwsProvider;
14use wami::service::{GroupService, RoleService, UserService};
15use wami::store::memory::InMemoryWamiStore;
16use wami::wami::identity::group::requests::CreateGroupRequest;
17use wami::wami::identity::role::requests::CreateRoleRequest;
18use wami::wami::identity::user::requests::{CreateUserRequest, ListUsersRequest};
19
20#[tokio::main]
21async fn main() -> Result<(), Box<dyn std::error::Error>> {
22    println!("=== Service Layer Introduction ===\n");
23
24    // Step 1: Initialize store with Arc<RwLock> for thread-safe access
25    println!("Step 1: Initializing services...\n");
26    let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
27    let _provider = Arc::new(AwsProvider::new());
28    let account_id = "123456789012";
29
30    // Create services
31    let user_service = UserService::new(store.clone(), account_id.to_string());
32    let group_service = GroupService::new(store.clone(), account_id.to_string());
33    let role_service = RoleService::new(store.clone(), account_id.to_string());
34
35    println!("✓ Services initialized");
36
37    // === CREATE Operations via Services ===
38    println!("\nStep 2: Creating resources via services...\n");
39
40    // Create users
41    println!("Creating users...");
42    let alice_req = CreateUserRequest {
43        user_name: "alice".to_string(),
44        path: Some("/users/".to_string()),
45        permissions_boundary: None,
46        tags: None,
47    };
48    let alice = user_service.create_user(alice_req).await?;
49    println!("✓ Created user: {}", alice.user_name);
50
51    let bob_req = CreateUserRequest {
52        user_name: "bob".to_string(),
53        path: Some("/users/".to_string()),
54        permissions_boundary: None,
55        tags: None,
56    };
57    user_service.create_user(bob_req).await?;
58    println!("✓ Created user: bob");
59
60    // Create groups
61    println!("\nCreating groups...");
62    let dev_group_req = CreateGroupRequest {
63        group_name: "developers".to_string(),
64        path: Some("/groups/".to_string()),
65        tags: None,
66    };
67    let dev_group = group_service.create_group(dev_group_req).await?;
68    println!("✓ Created group: {}", dev_group.group_name);
69
70    // Create role
71    println!("\nCreating role...");
72    let role_req = CreateRoleRequest {
73        role_name: "deploy-role".to_string(),
74        path: Some("/roles/".to_string()),
75        assume_role_policy_document: r#"{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"sts:AssumeRole"}]}"#.to_string(),
76        description: Some("Role for deployment".to_string()),
77        max_session_duration: Some(3600),
78        permissions_boundary: None,
79        tags: None,
80    };
81    let role = role_service.create_role(role_req).await?;
82    println!("✓ Created role: {}", role.role_name);
83
84    // === READ Operations via Services ===
85    println!("\n\nStep 3: Reading resources via services...\n");
86
87    // Get specific user
88    let alice_retrieved = user_service.get_user("alice").await?;
89    if let Some(user) = alice_retrieved {
90        println!("✓ Retrieved user 'alice':");
91        println!("  - User ID: {}", user.user_id);
92        println!("  - ARN: {}", user.arn);
93    }
94
95    // List users
96    let users = user_service
97        .list_users(ListUsersRequest {
98            path_prefix: None,
99            pagination: None,
100        })
101        .await?;
102    println!("\n✓ Found {} users via service:", users.0.len());
103    for user in &users.0 {
104        println!("  - {}", user.user_name);
105    }
106
107    // === UPDATE Operations via Services ===
108    println!("\n\nStep 4: Updating resources via services...\n");
109
110    use wami::wami::identity::user::requests::UpdateUserRequest;
111    let update_req = UpdateUserRequest {
112        user_name: "alice".to_string(),
113        new_user_name: None,
114        new_path: Some("/admin-users/".to_string()),
115    };
116    user_service.update_user(update_req).await?;
117    println!("✓ Updated alice's path to '/admin-users/'");
118
119    // === DELETE Operations via Services ===
120    println!("\n\nStep 5: Deleting resources via services...\n");
121
122    user_service.delete_user("bob").await?;
123    println!("✓ Deleted user 'bob'");
124
125    // Verify deletion
126    let bob_check = user_service.get_user("bob").await?;
127    if bob_check.is_none() {
128        println!("  Verified: bob no longer exists");
129    }
130
131    println!("\n✅ Example completed successfully!");
132    println!("Key takeaways:");
133    println!("- Services provide a higher-level API than raw store access");
134    println!("- Arc<RwLock<Store>> enables thread-safe concurrent operations");
135    println!("- Services use request/response DTOs for clean API contracts");
136    println!("- Services encapsulate business logic and validation");
137
138    Ok(())
139}