|
1 | 1 | use crate::utils::DisplayVec; |
2 | 2 | use goose::goose::{GooseUser, TransactionError, TransactionResult}; |
3 | 3 | use serde_json::json; |
| 4 | +use std::collections::HashMap; |
4 | 5 | use std::sync::{ |
5 | 6 | Arc, |
6 | 7 | atomic::{AtomicUsize, Ordering}, |
7 | 8 | }; |
| 9 | +use tokio::sync::{OnceCell, RwLock}; |
8 | 10 | use urlencoding::encode; |
9 | 11 |
|
| 12 | +/// Cache for advisory files |
| 13 | +static FILE_CACHE: OnceCell<RwLock<HashMap<String, Vec<u8>>>> = OnceCell::const_new(); |
| 14 | + |
| 15 | +/// Get advisory file cache |
| 16 | +async fn get_file_cache() -> &'static RwLock<HashMap<String, Vec<u8>>> { |
| 17 | + FILE_CACHE |
| 18 | + .get_or_init(|| async { RwLock::new(HashMap::new()) }) |
| 19 | + .await |
| 20 | +} |
| 21 | + |
| 22 | +/// Upload advisory data and extract advisory ID from response |
| 23 | +async fn upload_advisory_and_extract_id( |
| 24 | + file_bytes: Vec<u8>, |
| 25 | + user: &mut GooseUser, |
| 26 | +) -> Result<String, Box<TransactionError>> { |
| 27 | + let response = user.post("/api/v2/advisory", file_bytes).await?; |
| 28 | + let v = response.response?.json::<serde_json::Value>().await?; |
| 29 | + let advisory_id = v["id"] |
| 30 | + .as_str() |
| 31 | + .ok_or_else(|| { |
| 32 | + Box::new(TransactionError::Custom( |
| 33 | + "Missing advisory ID in response".to_string(), |
| 34 | + )) |
| 35 | + })? |
| 36 | + .to_string(); |
| 37 | + |
| 38 | + Ok(advisory_id) |
| 39 | +} |
| 40 | + |
10 | 41 | pub async fn get_advisory(id: String, user: &mut GooseUser) -> TransactionResult { |
11 | 42 | let uri = format!("/api/v2/advisory/{}", encode(&format!("urn:uuid:{}", id))); |
12 | 43 |
|
@@ -43,26 +74,33 @@ pub async fn upload_advisory_and_get_id( |
43 | 74 | advisory_file: String, |
44 | 75 | user: &mut GooseUser, |
45 | 76 | ) -> Result<String, Box<TransactionError>> { |
| 77 | + // Check cache first |
| 78 | + let cache = get_file_cache().await; |
| 79 | + |
| 80 | + { |
| 81 | + let read_guard = cache.read().await; |
| 82 | + if let Some(cached_bytes) = read_guard.get(&advisory_file) { |
| 83 | + let file_bytes = cached_bytes.clone(); |
| 84 | + // Found in cache, upload directly |
| 85 | + drop(read_guard); |
| 86 | + |
| 87 | + return upload_advisory_and_extract_id(file_bytes, user).await; |
| 88 | + } |
| 89 | + } |
| 90 | + |
46 | 91 | let file_bytes = tokio::fs::read(&advisory_file).await.map_err(|e| { |
47 | 92 | Box::new(TransactionError::Custom(format!( |
48 | 93 | "Failed to read file {}: {}", |
49 | 94 | advisory_file, e |
50 | 95 | ))) |
51 | 96 | })?; |
52 | 97 |
|
53 | | - let response = user.post("/api/v2/advisory", file_bytes).await?; |
54 | | - let v = response.response?.json::<serde_json::Value>().await?; |
55 | | - |
56 | | - let advisory_id = v["id"] |
57 | | - .as_str() |
58 | | - .ok_or_else(|| { |
59 | | - Box::new(TransactionError::Custom( |
60 | | - "Missing advisory ID in response".to_string(), |
61 | | - )) |
62 | | - })? |
63 | | - .to_string(); |
| 98 | + { |
| 99 | + let mut write_guard = cache.write().await; |
| 100 | + write_guard.insert(advisory_file.clone(), file_bytes.clone()); |
| 101 | + } |
64 | 102 |
|
65 | | - Ok(advisory_id) |
| 103 | + upload_advisory_and_extract_id(file_bytes, user).await |
66 | 104 | } |
67 | 105 |
|
68 | 106 | /// Delete advisory by ID |
|
0 commit comments