Commit Diff


commit - b2da4a0ec679e2071c2a3a981b5e8c5caef83235
commit + 57116925c93ae23cd81054de46b6197baf1d87d7
blob - 3249cffaf63d913464f8b8e339debdce16f38121
blob + 8fdc1f7149fecb0a57c4d26bfcd3a6590e3188ea
--- src/main.rs
+++ src/main.rs
@@ -196,7 +196,7 @@ async fn main() -> Result<()> {
             run_sync(&common_args, &args).await?;
         }
         Commands::Add(args) => {
-            // run_add_groups(args).await?;
+            run_add_groups(&common_args, args).await?;
             println!("add");
         } // Commands::Del(args) => {
           //     let common = CommonOperationArgs {
@@ -292,29 +292,35 @@ async fn run_sync(
     Ok(())
 }
 
-// async fn run_add_groups(args: AddArgs) -> Result<()> {
-//     // info!(
-//     //     pool_id = %args.pool_id,
-//     //     emails_file = ?args.emails_file,
-//     //     concurrency = ?args.concurrency,
-//     //     timeout = ?args.timeout,
-//     //     "add groups operation requested (not implemented yet)"
-//     // );
+async fn run_add_groups(
+    common_args: &CommonOperationArgs,
+    cmd_args: AddArgs,
+) -> Result<()> {
+    info!(
+        pool_id = %common_args.pool_id,
+        emails_file = ?cmd_args.emails_file,
+        sync_file = ?cmd_args.sync_file,
+        groups = ?cmd_args.groups,
+        concurrency = ?common_args.concurrency,
+        timeout = ?common_args.timeout,
+        "add groups operation requested"
+    );
 
-//     let config = aws_config::load_from_env().await;
-//     let client = CognitoClient::new(&config);
+    let config = aws_config::load_from_env().await;
+    let client = CognitoClient::new(&config);
 
-//     add_users_to_groups_from_files(
-//         &args.pool_id,
-//         &args.sync_file,
-//         &args.emails_file,
-//         &args.groups,
-//         args.concurrency,
-//     )
-//     .await?;
+    add_users_to_groups_from_files(
+        &client,
+        &common_args.pool_id,
+        &cmd_args.sync_file,
+        &cmd_args.emails_file,
+        &cmd_args.groups,
+        common_args.concurrency,
+    )
+    .await?;
 
-//     Ok(())
-// }
+    Ok(())
+}
 
 // #[allow(dead_code)]
 // async fn run_remove_groups(args: CommonOperationArgs) -> Result<()> {
@@ -511,141 +517,141 @@ pub async fn load_email_list<P: AsRef<Path>>(path: P) 
     Ok(emails)
 }
 
-// /// Add a Cognito user to one or more groups using the Admin API.
-// ///
-// /// This function assumes AWS credentials and permissions allow admin operations.
-// pub async fn admin_add_user_to_groups(
-//     client: &CognitoClient,
-//     pool_id: &str,
-//     username: &str,
-//     groups: &[String],
-// ) -> Result<()> {
-//     for group in groups {
-//         info!(%username, %group, %pool_id, "adding user to Cognito group");
+/// Add a Cognito user to one or more groups using the Admin API.
+///
+/// This function assumes AWS credentials and permissions allow admin operations.
+pub async fn admin_add_user_to_groups(
+    client: &CognitoClient,
+    pool_id: &str,
+    username: &str,
+    groups: &[String],
+) -> Result<()> {
+    for group in groups {
+        info!(%username, %group, %pool_id, "adding user to Cognito group");
 
-//         client
-//             .admin_add_user_to_group()
-//             .user_pool_id(pool_id)
-//             .username(username)
-//             .group_name(group)
-//             .send()
-//             .await
-//             .with_context(|| {
-//                 format!(
-//                     "failed to add user '{}' to group '{}'",
-//                     username, group
-//                 )
-//             })?;
+        client
+            .admin_add_user_to_group()
+            .user_pool_id(pool_id)
+            .username(username)
+            .group_name(group)
+            .send()
+            .await
+            .with_context(|| {
+                format!(
+                    "failed to add user '{}' to group '{}'",
+                    username, group
+                )
+            })?;
 
-//         info!(%username, %group, "user successfully added to group");
-//     }
+        info!(%username, %group, "user successfully added to group");
+    }
 
-//     Ok(())
-// }
+    Ok(())
+}
 
-// /// High-level flow:
-// /// 1. Load sync CSV into HashMap<email, username>.
-// /// 2. Load target e-mails (one per line).
-// /// 3. For each email, find username in HashMap.
-// /// 4. Call `admin_add_user_to_groups` with concurrency limit.
-// /// 5. Log success or failure for each email.
-// ///
-// /// `sync_csv_path`: CSV generated by `sync` (username,email).
-// /// `emails_list_path`: TXT file with one e-mail per line (users to be added).
-// pub async fn add_users_to_groups_from_files(
-//     client: &CognitoClient,
-//     pool_id: &str,
-//     sync_csv_path: &Path,
-//     emails_list_path: &Path,
-//     groups: &[String],
-//     concurrency: usize,
-// ) -> Result<()> {
-//     let concurrency = std::cmp::max(1, concurrency);
+/// High-level flow:
+/// 1. Load sync CSV into HashMap<email, username>.
+/// 2. Load target e-mails (one per line).
+/// 3. For each email, find username in HashMap.
+/// 4. Call `admin_add_user_to_groups` with concurrency limit.
+/// 5. Log success or failure for each email.
+///
+/// `sync_csv_path`: CSV generated by `sync` (username,email).
+/// `emails_list_path`: TXT file with one e-mail per line (users to be added).
+pub async fn add_users_to_groups_from_files(
+    client: &CognitoClient,
+    pool_id: &str,
+    sync_csv_path: &Path,
+    emails_list_path: &Path,
+    groups: &[String],
+    concurrency: usize,
+) -> Result<()> {
+    let concurrency = std::cmp::max(1, concurrency);
 
-//     // 1. Load sync CSV into map: email -> username
-//     let email_to_username = read_sync_file_to_map(sync_csv_path).await?;
-//     info!(
-//         total_entries = email_to_username.len(),
-//         "loaded sync map (email -> username)"
-//     );
+    // 1. Load sync CSV into map: email -> username
+    let email_to_username = read_sync_file_to_map(sync_csv_path).await?;
+    info!(
+        total_entries = email_to_username.len(),
+        "loaded sync map (email -> username)"
+    );
 
-//     // 2. Load target e-mails
-//     let emails = load_email_list(emails_list_path).await?;
-//     let total_emails = emails.len();
-//     info!(total_emails, "loaded target e-mail list");
+    // 2. Load target e-mails
+    let emails = load_email_list(emails_list_path).await?;
+    let total_emails = emails.len();
+    info!(total_emails, "loaded target e-mail list");
 
-//     let pb = Arc::new({
-//         let bar = ProgressBar::new(total_emails as u64);
-//         bar.set_style(
-//             ProgressStyle::with_template(
-//                 "[{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} {msg}",
-//             )
-//             .unwrap_or_else(|_| ProgressStyle::default_bar()),
-//         );
-//         bar.set_message("processing users");
-//         bar
-//     });
+    let pb = Arc::new({
+        let bar = ProgressBar::new(total_emails as u64);
+        bar.set_style(
+            ProgressStyle::with_template(
+                "[{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} {msg}",
+            )
+            .unwrap_or_else(|_| ProgressStyle::default_bar()),
+        );
+        bar.set_message("processing users");
+        bar
+    });
 
-//     let semaphore = Arc::new(Semaphore::new(concurrency));
-//     let mut handles: Vec<JoinHandle<()>> = Vec::with_capacity(emails.len());
+    let semaphore = Arc::new(Semaphore::new(concurrency));
+    let mut handles: Vec<JoinHandle<()>> = Vec::with_capacity(emails.len());
 
-//     for email in emails {
-//         let username = match email_to_username.get(&email) {
-//             Some(u) => u.clone(),
-//             None => {
-//                 error!(%email, "e-mail not found in sync map, skipping");
-//                 continue;
-//             }
-//         };
+    for email in emails {
+        let username = match email_to_username.get(&email) {
+            Some(u) => u.clone(),
+            None => {
+                error!(%email, "e-mail not found in sync map, skipping");
+                continue;
+            }
+        };
 
-//         let permit = semaphore.clone().acquire_owned().await?;
-//         let client_clone = client.clone();
-//         let pool_id = pool_id.to_string();
-//         let groups = groups.to_vec();
-//         let email_clone = email.clone();
-//         let pb_clone = pb.clone();
+        let permit = semaphore.clone().acquire_owned().await?;
+        let client_clone = client.clone();
+        let pool_id = pool_id.to_string();
+        let groups = groups.to_vec();
+        let email_clone = email.clone();
+        let pb_clone = pb.clone();
 
-//         let handle = tokio::spawn(async move {
-//             let _permit = permit; // keep permit alive for the duration of this task
+        let handle = tokio::spawn(async move {
+            let _permit = permit; // keep permit alive for the duration of this task
 
-//             if let Err(err) = admin_add_user_to_groups(
-//                 &client_clone,
-//                 &pool_id,
-//                 &username,
-//                 &groups,
-//             )
-//             .await
-//             {
-//                 error!(
-//                     email = %email_clone,
-//                     %username,
-//                     error = ?err,
-//                     "failed to add user to one or more groups"
-//                 );
-//             } else {
-//                 info!(
-//                     email = %email_clone,
-//                     %username,
-//                     groups = ?groups,
-//                     "user successfully processed for all groups"
-//                 );
-//             }
+            if let Err(err) = admin_add_user_to_groups(
+                &client_clone,
+                &pool_id,
+                &username,
+                &groups,
+            )
+            .await
+            {
+                error!(
+                    email = %email_clone,
+                    %username,
+                    error = ?err,
+                    "failed to add user to one or more groups"
+                );
+            } else {
+                info!(
+                    email = %email_clone,
+                    %username,
+                    groups = ?groups,
+                    "user successfully processed for all groups"
+                );
+            }
 
-//             pb_clone.inc(1);
-//         });
+            pb_clone.inc(1);
+        });
 
-//         handles.push(handle);
-//     }
+        handles.push(handle);
+    }
 
-//     // Wait for all tasks to complete
-//     for handle in handles {
-//         // Ignore panics here, just surface as error logs.
-//         if let Err(join_err) = handle.await {
-//             error!(error = ?join_err, "join error while processing a user");
-//         }
-//     }
+    // Wait for all tasks to complete
+    for handle in handles {
+        // Ignore panics here, just surface as error logs.
+        if let Err(join_err) = handle.await {
+            error!(error = ?join_err, "join error while processing a user");
+        }
+    }
 
-//     pb.finish_with_message("done");
-//     info!("finished processing all users for add-groups operation");
-//     Ok(())
-// }
+    pb.finish_with_message("done");
+    info!("finished processing all users for add-groups operation");
+    Ok(())
+}