hierarchy listing endpoint, fix trailing slash upath parsing
This commit is contained in:
parent
2b92a1ad67
commit
ff457faca3
4 changed files with 133 additions and 33 deletions
|
@ -31,7 +31,7 @@ pub struct InnerEntry {
|
|||
pub value: EntryValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum EntryValue {
|
||||
Value(serde_json::Value),
|
||||
Address(Address),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::database::{
|
||||
DbExecutor, Entry, EntryValue, InnerEntry, InsertEntry, QueryEntries, RetrieveByHash,
|
||||
RetrieveObject,
|
||||
};
|
||||
use crate::hash::{ComputeHash, HasherWorker};
|
||||
use crate::models;
|
||||
|
@ -48,6 +49,7 @@ impl std::str::FromStr for UPath {
|
|||
});
|
||||
result.append(
|
||||
rest[TOP_SEPARATOR.len()..rest.len()]
|
||||
.trim_end_matches('/')
|
||||
.split("/")
|
||||
.map(|part| UDirectory {
|
||||
name: String::from(part),
|
||||
|
@ -59,6 +61,7 @@ impl std::str::FromStr for UPath {
|
|||
result
|
||||
}
|
||||
None => string
|
||||
.trim_end_matches('/')
|
||||
.split("/")
|
||||
.map(|part| UDirectory {
|
||||
name: String::from(part),
|
||||
|
@ -104,10 +107,89 @@ impl std::fmt::Display for UPath {
|
|||
}
|
||||
}
|
||||
|
||||
trait EntryList {
|
||||
fn extract_addresses(&self) -> Vec<Address>;
|
||||
}
|
||||
|
||||
impl EntryList for Vec<Entry> {
|
||||
fn extract_addresses(&self) -> Vec<Address> {
|
||||
self.into_iter()
|
||||
.filter_map(|e| {
|
||||
if let EntryValue::Address(address) = &e.value {
|
||||
Some(address.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_roots(db_executor: &Addr<crate::database::DbExecutor>) -> Result<Vec<Entry>> {
|
||||
let all_directories: Vec<Entry> = db_executor
|
||||
.send(QueryEntries {
|
||||
target: None,
|
||||
key: Some(DIR_KEY.to_string()),
|
||||
value: None,
|
||||
})
|
||||
.await??;
|
||||
|
||||
let directories_with_parents: Vec<Address> = db_executor
|
||||
.send(QueryEntries {
|
||||
target: None,
|
||||
key: Some(DIR_HAS_KEY.to_string()),
|
||||
value: None,
|
||||
})
|
||||
.await??
|
||||
.extract_addresses();
|
||||
|
||||
Ok(all_directories
|
||||
.into_iter()
|
||||
.filter(|entry| !directories_with_parents.contains(&entry.target))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn list_directory(db_executor: &Addr<DbExecutor>, path: &UPath) -> Result<Vec<Entry>> {
|
||||
let entry_addresses = match path.0.len() {
|
||||
0 => list_roots(db_executor)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|e| e.target)
|
||||
.collect(),
|
||||
_ => {
|
||||
let resolved_path: Vec<Address> = resolve_path(db_executor, path, false).await?;
|
||||
let last = resolved_path.last().unwrap();
|
||||
|
||||
db_executor
|
||||
.send(QueryEntries {
|
||||
target: Some(last.clone()),
|
||||
key: Some(DIR_HAS_KEY.to_string()),
|
||||
value: None,
|
||||
})
|
||||
.await??
|
||||
.extract_addresses()
|
||||
}
|
||||
};
|
||||
|
||||
let mut result: Vec<Entry> = vec![];
|
||||
for address in entry_addresses {
|
||||
result.extend(
|
||||
db_executor
|
||||
.send(RetrieveObject { target: address })
|
||||
.await??
|
||||
.into_iter()
|
||||
.filter(|e| [DIR_KEY, FILENAME_KEY, FILE_IDENTITY_KEY].contains(&e.key.as_str()))
|
||||
.collect::<Vec<Entry>>(),
|
||||
);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn fetch_or_create_dir(
|
||||
db_executor: &Addr<crate::database::DbExecutor>,
|
||||
parent: Option<Address>,
|
||||
directory: UDirectory,
|
||||
create: bool,
|
||||
) -> Result<Address> {
|
||||
match parent.clone() {
|
||||
Some(address) => trace!("FETCHING/CREATING {}/{:#}", address, directory),
|
||||
|
@ -135,15 +217,7 @@ pub async fn fetch_or_create_dir(
|
|||
value: None,
|
||||
})
|
||||
.await??
|
||||
.into_iter()
|
||||
.filter_map(|e: Entry| {
|
||||
if let EntryValue::Address(address) = e.value {
|
||||
Some(address)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
.extract_addresses();
|
||||
|
||||
let valid = directories
|
||||
.into_iter()
|
||||
|
@ -157,28 +231,32 @@ pub async fn fetch_or_create_dir(
|
|||
|
||||
match valid_directories.len() {
|
||||
0 => {
|
||||
let new_directory_address = Address::UUID(Uuid::new_v4());
|
||||
let directory_entry = InnerEntry {
|
||||
target: new_directory_address.clone(),
|
||||
key: String::from(DIR_KEY),
|
||||
value: dir_value,
|
||||
};
|
||||
let _ = db_executor
|
||||
.send(InsertEntry {
|
||||
entry: directory_entry,
|
||||
})
|
||||
.await??;
|
||||
|
||||
if parent.is_some() {
|
||||
let has_entry = InnerEntry {
|
||||
target: parent.unwrap(),
|
||||
key: String::from(DIR_HAS_KEY),
|
||||
value: EntryValue::Address(new_directory_address.clone()),
|
||||
if create {
|
||||
let new_directory_address = Address::UUID(Uuid::new_v4());
|
||||
let directory_entry = InnerEntry {
|
||||
target: new_directory_address.clone(),
|
||||
key: String::from(DIR_KEY),
|
||||
value: dir_value,
|
||||
};
|
||||
let _ = db_executor.send(InsertEntry { entry: has_entry }).await??;
|
||||
}
|
||||
let _ = db_executor
|
||||
.send(InsertEntry {
|
||||
entry: directory_entry,
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(new_directory_address)
|
||||
if parent.is_some() {
|
||||
let has_entry = InnerEntry {
|
||||
target: parent.unwrap(),
|
||||
key: String::from(DIR_HAS_KEY),
|
||||
value: EntryValue::Address(new_directory_address.clone()),
|
||||
};
|
||||
let _ = db_executor.send(InsertEntry { entry: has_entry }).await??;
|
||||
}
|
||||
|
||||
Ok(new_directory_address)
|
||||
} else {
|
||||
Err(anyhow!("Directory does not exist."))
|
||||
}
|
||||
}
|
||||
1 => Ok(valid_directories[0].clone()),
|
||||
_ => Err(anyhow!(
|
||||
|
@ -187,9 +265,10 @@ pub async fn fetch_or_create_dir(
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn resolve_path_with_parents(
|
||||
pub async fn resolve_path(
|
||||
db_executor: &Addr<DbExecutor>,
|
||||
path: &UPath,
|
||||
create: bool,
|
||||
) -> Result<Vec<Address>> {
|
||||
let mut result: Vec<Address> = vec![];
|
||||
let mut path_stack = path.0.to_vec();
|
||||
|
@ -200,6 +279,7 @@ pub async fn resolve_path_with_parents(
|
|||
db_executor,
|
||||
result.last().cloned(),
|
||||
path_stack.pop().unwrap(),
|
||||
create,
|
||||
)
|
||||
.await?;
|
||||
result.push(dir_address);
|
||||
|
@ -288,7 +368,7 @@ async fn _reimport_directory<T: AsRef<Path>>(
|
|||
}))
|
||||
.collect(),
|
||||
);
|
||||
let resolved_path = resolve_path_with_parents(db_executor, &upath).await?;
|
||||
let resolved_path = resolve_path(db_executor, &upath, true).await?;
|
||||
let parent_dir = resolved_path.last().unwrap();
|
||||
let dir_has_entry = InnerEntry {
|
||||
target: parent_dir.clone(),
|
||||
|
|
|
@ -82,6 +82,7 @@ fn main() -> std::io::Result<()> {
|
|||
.wrap(middleware::Logger::default())
|
||||
.service(routes::get_raw)
|
||||
.service(routes::get_object)
|
||||
.service(routes::list_hier)
|
||||
.service(routes::get_lookup)
|
||||
.service(routes::api_refresh)
|
||||
.service(
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::database::Entry;
|
||||
use crate::filesystem::{list_directory, UPath};
|
||||
use crate::hash::{decode, encode};
|
||||
use actix::prelude::*;
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::error::{ErrorBadRequest, ErrorInternalServerError};
|
||||
use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, ErrorNotFound};
|
||||
use actix_web::{error, get, post, web, Error, HttpResponse};
|
||||
use anyhow::Result;
|
||||
use log::debug;
|
||||
|
@ -66,6 +67,24 @@ pub async fn get_object(
|
|||
Ok(HttpResponse::Ok().json(result))
|
||||
}
|
||||
|
||||
#[get("/hier/{path:.*}")]
|
||||
pub async fn list_hier(
|
||||
state: web::Data<State>,
|
||||
path: web::Path<String>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let upath: UPath = path.into_inner().parse().map_err(ErrorBadRequest)?;
|
||||
let entries: Vec<Entry> = list_directory(&state.db, &upath)
|
||||
.await
|
||||
.map_err(ErrorNotFound)?; // todo: 500 if actual error occurs
|
||||
|
||||
Ok(HttpResponse::Ok().json(
|
||||
entries
|
||||
.iter()
|
||||
.map(Entry::as_json)
|
||||
.collect::<serde_json::Value>(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LookupRequest {
|
||||
query: String,
|
||||
|
|
Loading…
Reference in a new issue