hierarchy listing endpoint, fix trailing slash upath parsing

feat/vaults
Tomáš Mládek 2020-09-13 20:10:18 +02:00
parent 2b92a1ad67
commit ff457faca3
4 changed files with 133 additions and 33 deletions

View File

@ -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),

View File

@ -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(),

View File

@ -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(

View File

@ -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,