remove list_hier, retrieve_object retrieves attrs of attrs, rework /api/hier

feat/vaults
Tomáš Mládek 2021-12-17 23:04:35 +01:00
parent 1194a4439e
commit e0a603d154
4 changed files with 82 additions and 94 deletions

View File

@ -15,7 +15,7 @@ use crate::database::constants::{
}; };
use crate::database::entry::{Entry, EntryValue}; use crate::database::entry::{Entry, EntryValue};
use crate::database::lang::{EntryQuery, Query, QueryComponent, QueryPart}; use crate::database::lang::{EntryQuery, Query, QueryComponent, QueryPart};
use crate::database::{bulk_retrieve_objects, insert_entry, query, DbPool}; use crate::database::{insert_entry, query, DbPool};
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct UNode(String); pub struct UNode(String);
@ -37,14 +37,14 @@ impl UNode {
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct UPath(pub Vec<UNode>); pub struct UHierPath(pub Vec<UNode>);
impl std::str::FromStr for UPath { impl std::str::FromStr for UHierPath {
type Err = anyhow::Error; type Err = anyhow::Error;
fn from_str(string: &str) -> Result<Self, Self::Err> { fn from_str(string: &str) -> Result<Self, Self::Err> {
if string.is_empty() { if string.is_empty() {
Ok(UPath(vec![])) Ok(UHierPath(vec![]))
} else { } else {
let result: Result<Vec<UNode>> = string let result: Result<Vec<UNode>> = string
.trim_end_matches('/') .trim_end_matches('/')
@ -52,7 +52,7 @@ impl std::str::FromStr for UPath {
.map(|part| UNode::new(String::from(part))) .map(|part| UNode::new(String::from(part)))
.collect(); .collect();
Ok(UPath(result?)) Ok(UHierPath(result?))
} }
} }
} }
@ -63,7 +63,7 @@ impl std::fmt::Display for UNode {
} }
} }
impl std::fmt::Display for UPath { impl std::fmt::Display for UHierPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@ -77,11 +77,11 @@ impl std::fmt::Display for UPath {
} }
} }
trait EntryList { trait PointerEntries {
fn extract_pointers(&self) -> Vec<(Address, Address)>; fn extract_pointers(&self) -> Vec<(Address, Address)>;
} }
impl EntryList for Vec<Entry> { impl PointerEntries for Vec<Entry> {
fn extract_pointers(&self) -> Vec<(Address, Address)> { fn extract_pointers(&self) -> Vec<(Address, Address)> {
self.iter() self.iter()
.filter_map(|e| { .filter_map(|e| {
@ -95,7 +95,7 @@ impl EntryList for Vec<Entry> {
} }
} }
pub fn list_orphans<C: Connection<Backend = Sqlite>>(connection: &C) -> Result<Vec<Address>> { pub fn list_roots<C: Connection<Backend = Sqlite>>(connection: &C) -> Result<Vec<Address>> {
let all_directories: Vec<Entry> = query( let all_directories: Vec<Entry> = query(
connection, connection,
Query::SingleQuery(QueryPart::Matches(EntryQuery { Query::SingleQuery(QueryPart::Matches(EntryQuery {
@ -125,34 +125,6 @@ pub fn list_orphans<C: Connection<Backend = Sqlite>>(connection: &C) -> Result<V
.collect()) .collect())
} }
pub async fn list_path<C: Connection<Backend = Sqlite>>(
connection: &C,
path: &UPath,
) -> Result<Vec<Entry>> {
let resolved_path: Vec<Address> = resolve_path(connection, path, false)?;
let entry_addresses = match resolved_path.last() {
Some(last) => query(
connection,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Exact(last.clone()),
attribute: QueryComponent::Exact(HIER_HAS_ATTR.to_string()),
value: QueryComponent::Any,
})),
)?
.extract_pointers()
.into_iter()
.map(|(addr, val)| [addr, val])
.flatten()
.collect(),
None => list_orphans(connection)?,
};
Ok(bulk_retrieve_objects(connection, entry_addresses)?
.into_iter()
// .filter(|e| [DIR_KEY, FILENAME_KEY, FILE_IDENTITY_KEY].contains(&e.attribute.as_str()))
.collect::<Vec<Entry>>())
}
lazy_static! { lazy_static! {
static ref FETCH_CREATE_LOCK: Mutex<()> = Mutex::new(()); static ref FETCH_CREATE_LOCK: Mutex<()> = Mutex::new(());
} }
@ -199,7 +171,7 @@ pub fn fetch_or_create_dir<C: Connection<Backend = Sqlite>>(
.into_iter() .into_iter()
.map(|(_, val)| val) .map(|(_, val)| val)
.collect(), .collect(),
None => list_orphans(connection)?, None => list_roots(connection)?,
}; };
let valid_directories: Vec<Address> = matching_directories let valid_directories: Vec<Address> = matching_directories
@ -248,7 +220,7 @@ pub fn fetch_or_create_dir<C: Connection<Backend = Sqlite>>(
pub fn resolve_path<C: Connection<Backend = Sqlite>>( pub fn resolve_path<C: Connection<Backend = Sqlite>>(
connection: &C, connection: &C,
path: &UPath, path: &UHierPath,
create: bool, create: bool,
) -> Result<Vec<Address>> { ) -> Result<Vec<Address>> {
let mut result: Vec<Address> = vec![]; let mut result: Vec<Address> = vec![];
@ -272,7 +244,7 @@ pub type ResolveCache = LruCache<(Option<Address>, UNode), Address>;
pub fn resolve_path_cached<C: Connection<Backend = Sqlite>>( pub fn resolve_path_cached<C: Connection<Backend = Sqlite>>(
connection: &C, connection: &C,
path: &UPath, path: &UHierPath,
create: bool, create: bool,
cache: &Arc<Mutex<ResolveCache>>, cache: &Arc<Mutex<ResolveCache>>,
) -> Result<Vec<Address>> { ) -> Result<Vec<Address>> {
@ -326,7 +298,7 @@ mod tests {
#[test] #[test]
fn test_path_codec() { fn test_path_codec() {
let path = UPath(vec![ let path = UHierPath(vec![
UNode("top".to_string()), UNode("top".to_string()),
UNode("foo".to_string()), UNode("foo".to_string()),
UNode("bar".to_string()), UNode("bar".to_string()),
@ -336,7 +308,7 @@ mod tests {
let str_path = path.to_string(); let str_path = path.to_string();
assert!(!str_path.is_empty()); assert!(!str_path.is_empty());
let decoded_path: Result<UPath> = str_path.parse(); let decoded_path: Result<UHierPath> = str_path.parse();
assert!(decoded_path.is_ok()); assert!(decoded_path.is_ok());
assert_eq!(path, decoded_path.unwrap()); assert_eq!(path, decoded_path.unwrap());
@ -344,13 +316,13 @@ mod tests {
#[test] #[test]
fn test_path_validation() { fn test_path_validation() {
let valid_path: Result<UPath> = "a/b/c/d/e/f/g".parse(); let valid_path: Result<UHierPath> = "a/b/c/d/e/f/g".parse();
assert!(valid_path.is_ok()); assert!(valid_path.is_ok());
let invalid_path: Result<UPath> = "a/b/c//d/e/f/g".parse(); let invalid_path: Result<UHierPath> = "a/b/c//d/e/f/g".parse();
assert!(invalid_path.is_err()); assert!(invalid_path.is_err());
let invalid_path: Result<UPath> = "a//b/c//d/e/f///g".parse(); let invalid_path: Result<UHierPath> = "a//b/c//d/e/f///g".parse();
assert!(invalid_path.is_err()); assert!(invalid_path.is_err());
} }
@ -386,7 +358,7 @@ mod tests {
assert!(baz_result.is_ok()); assert!(baz_result.is_ok());
let baz_result = baz_result.unwrap(); let baz_result = baz_result.unwrap();
let orphans = list_orphans(&open_result.pool.get().unwrap()); let orphans = list_roots(&open_result.pool.get().unwrap());
assert!(orphans.is_ok()); assert!(orphans.is_ok());
assert_eq!(orphans.unwrap().len(), 2); assert_eq!(orphans.unwrap().len(), 2);

View File

@ -9,7 +9,7 @@ pub mod hierarchies;
pub mod inner; pub mod inner;
pub mod lang; pub mod lang;
use crate::addressing::Address; use crate::addressing::{Address, Addressable};
use crate::database::constants::{IS_OF_TYPE_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_INVARIANT}; use crate::database::constants::{IS_OF_TYPE_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_INVARIANT};
use crate::database::entry::{Entry, EntryValue}; use crate::database::entry::{Entry, EntryValue};
use crate::database::inner::models; use crate::database::inner::models;
@ -118,43 +118,35 @@ pub fn retrieve_object<C: Connection<Backend = Sqlite>>(
) -> Result<Vec<Entry>> { ) -> Result<Vec<Entry>> {
use crate::database::inner::schema::data::dsl::*; use crate::database::inner::schema::data::dsl::*;
let matches = data let primary = data
.filter(entity.eq(object_address.encode()?)) .filter(entity.eq(object_address.encode()?))
.or_filter(value.eq(EntryValue::Address(object_address).to_string()?)) .or_filter(value.eq(EntryValue::Address(object_address).to_string()?))
.load::<models::Entry>(connection)?; .load::<models::Entry>(connection)?;
let entries = matches
let entries = primary
.iter() .iter()
.map(Entry::try_from) .map(Entry::try_from)
.filter_map(Result::ok) .collect::<Result<Vec<Entry>>>()?;
.collect();
Ok(entries) let secondary = data
}
pub fn bulk_retrieve_objects<C: Connection<Backend = Sqlite>>(
connection: &C,
object_addresses: Vec<Address>,
) -> Result<Vec<Entry>> {
use crate::database::inner::schema::data::dsl::*;
let matches = data
.filter( .filter(
entity.eq_any( entity.eq_any(
object_addresses entries
.iter() .iter()
.filter_map(|addr| addr.encode().ok()), .map(|e| e.address())
.filter_map(Result::ok)
.map(|addr| addr.encode())
.collect::<Result<Vec<Vec<u8>>>>()?,
), ),
) )
// .or_filter(value.eq(EntryValue::Address(object_address).to_str()?))
.load::<models::Entry>(connection)?; .load::<models::Entry>(connection)?;
let entries = matches let secondary_entries = secondary
.iter() .iter()
.map(Entry::try_from) .map(Entry::try_from)
.filter_map(Result::ok) .collect::<Result<Vec<Entry>>>()?;
.collect();
Ok(entries) Ok([entries, secondary_entries].concat())
} }
pub fn remove_object<C: Connection<Backend = Sqlite>>( pub fn remove_object<C: Connection<Backend = Sqlite>>(

View File

@ -9,7 +9,7 @@ use crate::database::constants::{
HIER_HAS_ATTR, IS_OF_TYPE_ATTR, TYPE_ADDR, TYPE_BASE_ATTR, TYPE_HAS_ATTR, HIER_HAS_ATTR, IS_OF_TYPE_ATTR, TYPE_ADDR, TYPE_BASE_ATTR, TYPE_HAS_ATTR,
}; };
use crate::database::entry::{Entry, EntryValue, InvariantEntry}; use crate::database::entry::{Entry, EntryValue, InvariantEntry};
use crate::database::hierarchies::{resolve_path_cached, ResolveCache, UNode, UPath}; use crate::database::hierarchies::{resolve_path_cached, ResolveCache, UNode, UHierPath};
use crate::database::inner::models; use crate::database::inner::models;
use crate::database::{ use crate::database::{
file_set_valid, file_update_mtime, insert_entry, insert_file, retrieve_all_files, DbPool, file_set_valid, file_update_mtime, insert_entry, insert_file, retrieve_all_files, DbPool,
@ -350,7 +350,7 @@ fn _process_directory_entry<P: AsRef<Path>>(
let components = normalized_path.components().collect::<Vec<Component>>(); let components = normalized_path.components().collect::<Vec<Component>>();
let (filename, dir_path) = components.split_last().unwrap(); let (filename, dir_path) = components.split_last().unwrap();
let upath = UPath( let upath = UHierPath(
iter::once(UNode::new("NATIVE").unwrap()) iter::once(UNode::new("NATIVE").unwrap())
.chain(dir_path.iter().map(|component| { .chain(dir_path.iter().map(|component| {
UNode::new(component.as_os_str().to_string_lossy().to_string()).unwrap() UNode::new(component.as_os_str().to_string_lossy().to_string()).unwrap()

View File

@ -1,6 +1,6 @@
use crate::addressing::{Address, Addressable}; use crate::addressing::{Address, Addressable};
use crate::database::entry::{Entry, InEntry}; use crate::database::entry::{Entry, InEntry};
use crate::database::hierarchies::{list_path, UPath}; use crate::database::hierarchies::{list_roots, resolve_path, UHierPath};
use crate::database::lang::Query; use crate::database::lang::Query;
use crate::database::{ use crate::database::{
get_latest_files, insert_entry, query, remove_object, retrieve_file, retrieve_object, DbPool, get_latest_files, insert_entry, query, remove_object, retrieve_file, retrieve_object, DbPool,
@ -84,34 +84,36 @@ pub async fn get_query(
Ok(HttpResponse::Ok().json(&result)) Ok(HttpResponse::Ok().json(&result))
} }
trait EntriesAsHash {
fn as_hash(&self) -> Result<HashMap<String, &Entry>>;
}
impl EntriesAsHash for Vec<Entry> {
fn as_hash(&self) -> Result<HashMap<String, &Entry>> {
let mut result: HashMap<String, &Entry> = HashMap::new();
for entry in self {
result.insert(encode(entry.address()?.encode()?), entry);
}
Ok(result)
}
}
#[get("/api/obj/{address_str}")] #[get("/api/obj/{address_str}")]
pub async fn get_object( pub async fn get_object(
state: web::Data<State>, state: web::Data<State>,
address_str: web::Path<String>, address_str: web::Path<String>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let connection = state.db_pool.get().map_err(ErrorInternalServerError)?; let connection = state.db_pool.get().map_err(ErrorInternalServerError)?;
let response: Result<Vec<Entry>> = retrieve_object( let result: Vec<Entry> = retrieve_object(
&connection, &connection,
Address::decode(&decode(address_str.into_inner()).map_err(ErrorBadRequest)?) Address::decode(&decode(address_str.into_inner()).map_err(ErrorBadRequest)?)
.map_err(ErrorBadRequest)?, .map_err(ErrorBadRequest)?,
); )
.map_err(ErrorInternalServerError)?;
debug!("{:?}", response); debug!("{:?}", result);
Ok(HttpResponse::Ok().json(result.as_hash().map_err(ErrorInternalServerError)?))
let mut result: HashMap<String, Entry> = HashMap::new();
for entry in response.map_err(error::ErrorInternalServerError)? {
result.insert(
encode(
entry
.address()
.map_err(ErrorInternalServerError)?
.encode()
.map_err(ErrorInternalServerError)?,
),
entry,
);
}
Ok(HttpResponse::Ok().json(result))
} }
const MAX_SIZE: usize = 1_000_000; const MAX_SIZE: usize = 1_000_000;
@ -172,13 +174,35 @@ pub async fn list_hier(
path: web::Path<String>, path: web::Path<String>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let connection = state.db_pool.get().map_err(ErrorInternalServerError)?; let connection = state.db_pool.get().map_err(ErrorInternalServerError)?;
let upath: UPath = path.into_inner().parse().map_err(ErrorBadRequest)?; let upath: UHierPath = path.into_inner().parse().map_err(ErrorBadRequest)?;
trace!("Listing path \"{}\"", upath); trace!("Listing path \"{}\"", upath);
let entries: Vec<Entry> = list_path(&connection, &upath)
.await
.map_err(ErrorNotFound)?; // todo: 500 if actual error occurs
Ok(HttpResponse::Ok().json(entries)) let (addr, data) = if upath.0.is_empty() {
(
None,
list_roots(&connection)
.map_err(ErrorInternalServerError)?
.into_iter()
.map(|root| {
retrieve_object(&connection, root)
})
.collect::<Result<Vec<Vec<Entry>>>>().map_err(ErrorInternalServerError)?
.concat(),
)
} else {
// todo: 500 if actual error occurs
let path = resolve_path(&connection, &upath, false).map_err(ErrorNotFound)?;
let last = path.last().unwrap().clone();
(
Some(last.clone()),
retrieve_object(&connection, last).map_err(ErrorInternalServerError)?,
)
};
Ok(HttpResponse::Ok().json(json!({
"addr": addr,
"data": data
})))
} }
#[post("/api/refresh")] #[post("/api/refresh")]