remove list_hier, retrieve_object retrieves attrs of attrs, rework /api/hier
This commit is contained in:
parent
1194a4439e
commit
e0a603d154
4 changed files with 82 additions and 94 deletions
|
@ -15,7 +15,7 @@ use crate::database::constants::{
|
|||
};
|
||||
use crate::database::entry::{Entry, EntryValue};
|
||||
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)]
|
||||
pub struct UNode(String);
|
||||
|
@ -37,14 +37,14 @@ impl UNode {
|
|||
}
|
||||
|
||||
#[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;
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||
if string.is_empty() {
|
||||
Ok(UPath(vec![]))
|
||||
Ok(UHierPath(vec![]))
|
||||
} else {
|
||||
let result: Result<Vec<UNode>> = string
|
||||
.trim_end_matches('/')
|
||||
|
@ -52,7 +52,7 @@ impl std::str::FromStr for UPath {
|
|||
.map(|part| UNode::new(String::from(part)))
|
||||
.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 {
|
||||
write!(
|
||||
f,
|
||||
|
@ -77,11 +77,11 @@ impl std::fmt::Display for UPath {
|
|||
}
|
||||
}
|
||||
|
||||
trait EntryList {
|
||||
trait PointerEntries {
|
||||
fn extract_pointers(&self) -> Vec<(Address, Address)>;
|
||||
}
|
||||
|
||||
impl EntryList for Vec<Entry> {
|
||||
impl PointerEntries for Vec<Entry> {
|
||||
fn extract_pointers(&self) -> Vec<(Address, Address)> {
|
||||
self.iter()
|
||||
.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(
|
||||
connection,
|
||||
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
||||
|
@ -125,34 +125,6 @@ pub fn list_orphans<C: Connection<Backend = Sqlite>>(connection: &C) -> Result<V
|
|||
.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! {
|
||||
static ref FETCH_CREATE_LOCK: Mutex<()> = Mutex::new(());
|
||||
}
|
||||
|
@ -199,7 +171,7 @@ pub fn fetch_or_create_dir<C: Connection<Backend = Sqlite>>(
|
|||
.into_iter()
|
||||
.map(|(_, val)| val)
|
||||
.collect(),
|
||||
None => list_orphans(connection)?,
|
||||
None => list_roots(connection)?,
|
||||
};
|
||||
|
||||
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>>(
|
||||
connection: &C,
|
||||
path: &UPath,
|
||||
path: &UHierPath,
|
||||
create: bool,
|
||||
) -> Result<Vec<Address>> {
|
||||
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>>(
|
||||
connection: &C,
|
||||
path: &UPath,
|
||||
path: &UHierPath,
|
||||
create: bool,
|
||||
cache: &Arc<Mutex<ResolveCache>>,
|
||||
) -> Result<Vec<Address>> {
|
||||
|
@ -326,7 +298,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_path_codec() {
|
||||
let path = UPath(vec![
|
||||
let path = UHierPath(vec![
|
||||
UNode("top".to_string()),
|
||||
UNode("foo".to_string()),
|
||||
UNode("bar".to_string()),
|
||||
|
@ -336,7 +308,7 @@ mod tests {
|
|||
let str_path = path.to_string();
|
||||
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_eq!(path, decoded_path.unwrap());
|
||||
|
@ -344,13 +316,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -386,7 +358,7 @@ mod tests {
|
|||
assert!(baz_result.is_ok());
|
||||
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_eq!(orphans.unwrap().len(), 2);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ pub mod hierarchies;
|
|||
pub mod inner;
|
||||
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::entry::{Entry, EntryValue};
|
||||
use crate::database::inner::models;
|
||||
|
@ -118,43 +118,35 @@ pub fn retrieve_object<C: Connection<Backend = Sqlite>>(
|
|||
) -> Result<Vec<Entry>> {
|
||||
use crate::database::inner::schema::data::dsl::*;
|
||||
|
||||
let matches = data
|
||||
let primary = data
|
||||
.filter(entity.eq(object_address.encode()?))
|
||||
.or_filter(value.eq(EntryValue::Address(object_address).to_string()?))
|
||||
.load::<models::Entry>(connection)?;
|
||||
let entries = matches
|
||||
|
||||
let entries = primary
|
||||
.iter()
|
||||
.map(Entry::try_from)
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
.collect::<Result<Vec<Entry>>>()?;
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
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
|
||||
let secondary = data
|
||||
.filter(
|
||||
entity.eq_any(
|
||||
object_addresses
|
||||
entries
|
||||
.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)?;
|
||||
|
||||
let entries = matches
|
||||
let secondary_entries = secondary
|
||||
.iter()
|
||||
.map(Entry::try_from)
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
.collect::<Result<Vec<Entry>>>()?;
|
||||
|
||||
Ok(entries)
|
||||
Ok([entries, secondary_entries].concat())
|
||||
}
|
||||
|
||||
pub fn remove_object<C: Connection<Backend = Sqlite>>(
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::database::constants::{
|
|||
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::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::{
|
||||
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 (filename, dir_path) = components.split_last().unwrap();
|
||||
|
||||
let upath = UPath(
|
||||
let upath = UHierPath(
|
||||
iter::once(UNode::new("NATIVE").unwrap())
|
||||
.chain(dir_path.iter().map(|component| {
|
||||
UNode::new(component.as_os_str().to_string_lossy().to_string()).unwrap()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::addressing::{Address, Addressable};
|
||||
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::{
|
||||
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))
|
||||
}
|
||||
|
||||
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}")]
|
||||
pub async fn get_object(
|
||||
state: web::Data<State>,
|
||||
address_str: web::Path<String>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let connection = state.db_pool.get().map_err(ErrorInternalServerError)?;
|
||||
let response: Result<Vec<Entry>> = retrieve_object(
|
||||
let result: Vec<Entry> = retrieve_object(
|
||||
&connection,
|
||||
Address::decode(&decode(address_str.into_inner()).map_err(ErrorBadRequest)?)
|
||||
.map_err(ErrorBadRequest)?,
|
||||
);
|
||||
)
|
||||
.map_err(ErrorInternalServerError)?;
|
||||
|
||||
debug!("{:?}", response);
|
||||
|
||||
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))
|
||||
debug!("{:?}", result);
|
||||
Ok(HttpResponse::Ok().json(result.as_hash().map_err(ErrorInternalServerError)?))
|
||||
}
|
||||
|
||||
const MAX_SIZE: usize = 1_000_000;
|
||||
|
@ -172,13 +174,35 @@ pub async fn list_hier(
|
|||
path: web::Path<String>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
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);
|
||||
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")]
|
||||
|
|
Loading…
Reference in a new issue