Skip to content

Commit dcaaa96

Browse files
committed
Move read_blob to ExposureCtrl.
- To avoid the instantiation cost whenever possible. - Also move pmrapp to use the helper instead.
1 parent 3ed7e71 commit dcaaa96

File tree

9 files changed

+56
-39
lines changed

9 files changed

+56
-39
lines changed

pmrapp/src/exposure/api.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ pub async fn read_blob(
211211
path: String,
212212
efvid: i64,
213213
key: String,
214-
) -> Result<Box<[u8]>, AppError> {
214+
) -> Result<Vec<u8>, AppError> {
215215
session().await?
216216
.enforcer(format!("/exposure/{id}/"), "").await?;
217217
let platform = platform().await?;
@@ -245,8 +245,7 @@ pub async fn read_safe_index_html(
245245

246246
let blob = read_blob(id, path.clone(), efvid, "index.html".to_string())
247247
.await
248-
.map_err(|_| AppError::InternalServerError)?
249-
.into_vec();
248+
.map_err(|_| AppError::InternalServerError)?;
250249
Ok(Builder::new()
251250
.url_relative(UrlRelative::Custom(Box::new(evaluate)))
252251
.clean(&String::from_utf8_lossy(&blob))

pmrapp/src/server/exposure.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,25 +113,11 @@ pub async fn exposure_file_data(
113113
session: Extension<AuthSession<ACPlatform>>,
114114
Path((e_id, ef_id, view_key, path)): Path<(i64, i64, String, String)>,
115115
) -> Result<Vec<u8>, AppError> {
116-
use std::path::{Component, Path};
117-
118116
Session::from(session)
119117
.enforcer(format!("/exposure/{e_id}/"), "").await?;
120118
let ec = platform.get_exposure(e_id).await
121119
.map_err(|_| AppError::InternalServerError)?;
122-
// TODO should really have the ExposureCtrl offer the base function
123-
// of providing the blob to bypass the more work involving getting
124-
// the other Ctrl types instantiated.
125-
let mut target = ec.data_root();
126-
target.push(ef_id.to_string());
127-
target.push(view_key);
128-
target.push("work");
129-
Path::new(&path).components()
130-
.for_each(|p| if let Component::Normal(s) = p {
131-
target.push(s)
132-
});
133-
134-
Ok(tokio::fs::read(target).await
135-
.map_err(|_| AppError::InternalServerError)?)
120+
ec.read_blob(ef_id, &view_key, &path).await
121+
.map_err(|_| AppError::NotFound)
136122

137123
}

pmrapp/src/view/cellml_codegen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub fn CellMLCodegen() -> impl IntoView {
6060
}.to_string();
6161
match code.await {
6262
Some(Ok(code)) => {
63-
let code = String::from_utf8(code.into_vec())
63+
let code = String::from_utf8(code)
6464
.map_err(|_| AppError::InternalServerError)?;
6565
Ok(view! {
6666
<div><a href="../cellml_codegen">Back</a></div>

pmrctrl/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ pub enum CtrlError {
4141
/// Path that isn't known under the associated resource
4242
#[error("unknown path: {0}")]
4343
UnknownPath(String),
44+
/// Path that isn't known under the associated resource
45+
#[error("invalid view_key: {0}")]
46+
InvalidViewKey(String),
4447
/// Path is valid, but is not associated with a ExposureFileCtrl
4548
#[error("exposure file not found: {0}")]
4649
EFCNotFound(String),

pmrctrl/src/handle/exposure/impls.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ use pmrrepo::handle::GitHandle;
2020
use std::{
2121
collections::HashMap,
2222
ops::Deref,
23-
path::PathBuf,
23+
path::{
24+
Component,
25+
Path,
26+
PathBuf,
27+
},
2428
sync::{
2529
Arc,
2630
OnceLock,
@@ -294,6 +298,24 @@ impl<'p> ExposureCtrl<'p> {
294298
result
295299
}
296300

301+
pub async fn read_blob(&self, file_id: i64, view_key: &str, path: &str) -> Result<Vec<u8>, CtrlError> {
302+
let mut target = self.data_root();
303+
target.push(file_id.to_string());
304+
// FIXME should check against `/` in view_key
305+
if view_key == "." || view_key == ".." {
306+
return Err(CtrlError::InvalidViewKey(view_key.to_string()))
307+
}
308+
target.push(view_key);
309+
target.push("work");
310+
Path::new(path).components()
311+
.for_each(|p| if let Component::Normal(s) = p {
312+
target.push(<&str>::try_from(s)
313+
.expect("this started as a valid str"))
314+
});
315+
tokio::fs::read(target).await
316+
.map_err(|_| CtrlError::EFVCBlobNotFound(path.to_string()))
317+
}
318+
297319
/// This ensures there is filesystem level access to the underlying
298320
/// files for this exposure (backed by the relevant workspace at the
299321
/// specified commit_id).

pmrctrl/src/handle/exposure_file/impls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,8 @@ impl<'p> ExposureFileCtrl<'p> {
263263
data_root.push(self.0.exposure_file.id().to_string());
264264
data_root
265265
}
266+
267+
pub async fn read_blob(&self, view_key: &str, path: &str) -> Result<Vec<u8>, CtrlError> {
268+
self.0.exposure.read_blob(self.0.exposure_file.id(), view_key, path).await
269+
}
266270
}

pmrctrl/src/handle/exposure_file_view/impls.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ use pmrcore::{
1414
};
1515
use std::{
1616
fmt,
17-
path::{
18-
Component,
19-
Path,
20-
PathBuf,
21-
},
17+
path::PathBuf,
2218
};
2319

2420
use super::ExposureFileViewCtrl;
@@ -94,16 +90,11 @@ impl<'p> ExposureFileViewCtrl<'p> {
9490
Ok((efv_id, task.id))
9591
}
9692

97-
pub async fn read_blob(&self, path: &str) -> Result<Box<[u8]>, CtrlError> {
98-
let mut target = self.working_dir()?;
99-
Path::new(path).components()
100-
.for_each(|p| if let Component::Normal(s) = p {
101-
target.push(<&str>::try_from(s)
102-
.expect("this started as a valid str"))
103-
});
104-
tokio::fs::read(target).await
105-
.map(Vec::into_boxed_slice)
106-
.map_err(|_| CtrlError::EFVCBlobNotFound(path.to_string()))
93+
pub async fn read_blob(&self, path: &str) -> Result<Vec<u8>, CtrlError> {
94+
let view_key = self.exposure_file_view
95+
.view_key()
96+
.ok_or(CtrlError::EFVCIncomplete)?;
97+
self.exposure_file.read_blob(view_key, path).await
10798
}
10899

109100
pub fn exposure_file_ctrl(&self) -> &ExposureFileCtrl<'p> {

pmrctrl/src/platform/impls/task.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl<'p> Platform {
5353
false
5454
}
5555
None => {
56+
// TODO we've somehow triggered this with sqlite.
5657
log::warn!("Task:{task_id} ran but it failed to produce results?");
5758
false
5859
}

pmrctrl/tests/pmrctrl_test.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,11 +1690,22 @@ async fn test_resolve_exposure_file_view_read_blob() -> anyhow::Result<()> {
16901690
.join("size");
16911691
assert!(target.exists());
16921692

1693+
// TODO should check against `/` in view_key
1694+
let err = exposure.read_blob(1, "..", "1/iorw/data/size").await;
1695+
assert_eq!(err, Err(CtrlError::InvalidViewKey("..".to_string())));
1696+
assert_eq!(
1697+
exposure.read_blob(1, "iorw", "../work/size").await,
1698+
Err(CtrlError::EFVCBlobNotFound("../work/size".to_string())),
1699+
);
1700+
1701+
let e_size = exposure.read_blob(1, "iorw", "size").await?;
1702+
assert_eq!(e_size, "13".as_bytes().to_vec());
1703+
16931704
let size = efvc.read_blob("size").await?;
1694-
assert_eq!(size, "13".as_bytes().into());
1705+
assert_eq!(size, "13".as_bytes().to_vec());
16951706
// pardir tokens and absolute paths should all be ignored
1696-
assert_eq!(efvc.read_blob("../../size").await?, "13".as_bytes().into());
1697-
assert_eq!(efvc.read_blob("/../../size").await?, "13".as_bytes().into());
1707+
assert_eq!(efvc.read_blob("../../size").await?, "13".as_bytes().to_vec());
1708+
assert_eq!(efvc.read_blob("/../../size").await?, "13".as_bytes().to_vec());
16981709

16991710
assert_eq!(
17001711
efvc.read_blob("../1/size").await,

0 commit comments

Comments
 (0)