From 93a4b54d4226f357272d2ac141d986ee382d312d Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Thu, 9 Apr 2026 10:40:14 -0400 Subject: [PATCH] feat: create file name apis --- src/pages/api/students.rs | 62 ++++++++++++++++++++++++++++++++ src/pages/provider_applicants.rs | 8 ++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/pages/api/students.rs b/src/pages/api/students.rs index 7a4ae1d..88f08cc 100644 --- a/src/pages/api/students.rs +++ b/src/pages/api/students.rs @@ -261,3 +261,65 @@ pub async fn get_student_file( Ok(bytes.to_vec()) } + +/// # Batch Get Student Files API +/// Gets all indicated files and returns a single `.zip` file containing all of them. They will be +/// organized based on their file key. +/// +/// This is more efficient than calling the [`get_student_file`] function as it makes use of S3's +/// `batch_get_object` function. +#[server] +pub async fn batch_get_student_files() -> Result, ServerFnError> { + todo!() +} + +/// # Get Student File Names API +/// Gets a list of file names given a student's ID and the corresponding input ID from the forms. +/// These file names can be used to construct a file key for S3. +/// +/// This API is only accessible by provider-level users. +#[server] +pub async fn get_student_file_names( + access_token: String, + student_id: String, + form_name: String, + question_id: String, +) -> Result, ServerFnError> { + use imports::*; + + let claims = validate_and_get_token_info(access_token).await?; + + // Check if the user has permission to get these files. + if !claims.groups.contains(&"ScholarshipProviders".to_string()) { + return Err(ServerFnError::new("Access denied: user is not a provider")); + } + + let client = create_dynamo_client().await; + + client + .query() + .table_name(MAIN_TABLE_NAME) + .expression_attribute_values(":hk", AttributeValue::S(format!("STUDENT#{student_id}"))) + .expression_attribute_values( + ":sk", + AttributeValue::S(format!("FILE#{form_name}#{question_id}")), + ) + .key_condition_expression("HK = :hk and begins_with(SK, :sk)") + .send() + .await + .map_err(|e| { + let msg = e.message().unwrap_or("Unknown error occurred"); + error!("{}", msg); + ServerFnError::new(msg) + }) + .map(|output| { + let Some(items) = output.items else { + return Vec::new(); + }; + + items + .into_iter() + .filter_map(|item| item.get("SK")?.as_s().ok().cloned()) + .collect::>() + }) +} diff --git a/src/pages/provider_applicants.rs b/src/pages/provider_applicants.rs index 2eb78c1..de9610a 100644 --- a/src/pages/provider_applicants.rs +++ b/src/pages/provider_applicants.rs @@ -1,5 +1,5 @@ use crate::common::{ComparisonData, ExpandableInfo, SchemaNode, SchemaType, ValueType}; -use crate::components::{Banner, Loading, ValueDisplay}; +use crate::components::{ActionButton, Banner, Loading, ValueDisplay}; use crate::pages::UnauthenticatedPage; use crate::pages::api::get_comparison_info; use crate::pages::api::students::{get_all_student_data, get_student_data}; @@ -530,6 +530,11 @@ fn StudentInformationDialog( } }); + let on_click_fafsa = move |_| { + let fafsa_key = format!("financial_info/user_id/fafsa/file_name"); + debug_log!("Getting FAFSA file with key {fafsa_key}"); + }; + view! {
+ "Get FAFSA" {move || Suspend::new(async move { get_student_data( student_id.get().unwrap_or_default(),