remove list_hier, retrieve_object retrieves attrs of attrs, rework /api/hier
parent
1194a4439e
commit
e0a603d154
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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>>(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
Loading…
Reference in New Issue