Schema aware create now works!

This commit is contained in:
William Brown 2018-11-14 14:54:59 +13:00
parent 56264b5b7b
commit dddd04898c
7 changed files with 796 additions and 167 deletions

View file

@ -274,9 +274,7 @@ mod tests {
assert_eq!(empty_result, Err(BackendError::EmptyRequest)); assert_eq!(empty_result, Err(BackendError::EmptyRequest));
let mut e: Entry = Entry::new(); let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william")) e.add_ava(String::from("userid"), String::from("william"));
.unwrap();
assert!(e.validate());
let single_result = be.create(audit, &vec![e]); let single_result = be.create(audit, &vec![e]);

View file

@ -88,27 +88,48 @@ impl Entry {
// This should always work? It's only on validate that we'll build // This should always work? It's only on validate that we'll build
// a list of syntax violations ... // a list of syntax violations ...
pub fn add_ava(&mut self, attr: String, value: String) -> Result<(), ()> { // If this already exists, we silently drop the event? Is that an
// acceptable interface?
// Should value here actually be a &str?
pub fn add_ava(&mut self, attr: String, value: String) {
// get_mut to access value // get_mut to access value
// How do we make this turn into an ok / err?
self.attrs self.attrs
.entry(attr) .entry(attr)
.and_modify(|v| v.push(value.clone())) .and_modify(|v| {
// Here we need to actually do a check/binary search ...
v.binary_search(&value).map_err(|idx| {
// This cloning is to fix a borrow issue ...
v.insert(idx, value.clone())
});
})
.or_insert(vec![value]); .or_insert(vec![value]);
Ok(())
} }
pub fn get_ava(&self, attr: &String) -> Option<&Vec<String>> { pub fn get_ava(&self, attr: &String) -> Option<&Vec<String>> {
self.attrs.get(attr) self.attrs.get(attr)
} }
pub fn validate(&self) -> bool { pub fn attribute_pres(&self, attr: &str) -> bool {
// We need access to the current system schema here now ... // FIXME: Do we need to normalise attr name?
true self.attrs.contains_key(attr)
} }
pub fn pres(&self, attr: &str) -> bool { pub fn attribute_equality(&self, attr: &str, value: &str) -> bool {
self.attrs.contains_key(attr) // Do a schema aware equality?
// Either we get schema passed in.
// OR we assume based on schema normalisation on the way in
// that the equality here of the raw values MUST be correct.
// If we do this, we likely need a DB normalise function ...
// The other issue is we have to normalise what's in the filter
// but that could be done *before* we get here?
// FIXME: Make this binary_search
self.attrs.get(attr).map_or(false, |v| {
v.iter()
.fold(false, |acc, av| if acc { acc } else { value == av })
})
} }
pub fn classes(&self) -> EntryClasses { pub fn classes(&self) -> EntryClasses {
@ -229,42 +250,55 @@ mod tests {
#[test] #[test]
fn test_user_basic() { fn test_user_basic() {
let u: User = User::new("william", "William Brown"); let u: User = User::new("william", "William Brown");
println!("u: {:?}", u);
let d = serde_json::to_string_pretty(&u).unwrap(); let d = serde_json::to_string_pretty(&u).unwrap();
println!("d: {}", d.as_str());
let u2: User = serde_json::from_str(d.as_str()).unwrap(); let u2: User = serde_json::from_str(d.as_str()).unwrap();
println!("u2: {:?}", u2);
} }
#[test] #[test]
fn test_entry_basic() { fn test_entry_basic() {
let mut e: Entry = Entry::new(); let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william")) e.add_ava(String::from("userid"), String::from("william"));
.unwrap();
assert!(e.validate());
let d = serde_json::to_string_pretty(&e).unwrap(); let d = serde_json::to_string_pretty(&e).unwrap();
}
println!("d: {}", d.as_str()); #[test]
fn test_entry_dup_value() {
// Schema doesn't matter here because we are duplicating a value
// it should fail!
// We still probably need schema here anyway to validate what we
// are adding ... Or do we validate after the changes are made in
// total?
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"));
e.add_ava(String::from("userid"), String::from("william"));
let values = e.get_ava(&String::from("userid")).unwrap();
// Should only be one value!
assert_eq!(values.len(), 1)
} }
#[test] #[test]
fn test_entry_pres() { fn test_entry_pres() {
let mut e: Entry = Entry::new(); let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william")) e.add_ava(String::from("userid"), String::from("william"));
.unwrap();
assert!(e.validate()); assert!(e.attribute_pres("userid"));
assert!(!e.attribute_pres("name"));
}
assert!(e.pres("userid")); #[test]
assert!(!e.pres("name")); fn test_entry_equality() {
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"));
assert!(e.attribute_equality("userid", "william"));
assert!(!e.attribute_equality("userid", "test"));
assert!(!e.attribute_equality("nonexist", "william"));
} }
} }

View file

@ -6,4 +6,5 @@ pub enum SchemaError {
MISSING_MUST_ATTRIBUTE, MISSING_MUST_ATTRIBUTE,
INVALID_ATTRIBUTE, INVALID_ATTRIBUTE,
INVALID_ATTRIBUTE_SYNTAX, INVALID_ATTRIBUTE_SYNTAX,
EMPTY_FILTER,
} }

View file

@ -3,6 +3,7 @@
// entry to assert it matches. // entry to assert it matches.
use super::entry::Entry; use super::entry::Entry;
use super::schema::Schema;
use std::cmp::{Ordering, PartialOrd}; use std::cmp::{Ordering, PartialOrd};
// Perhaps make these json serialisable. Certainly would make parsing // Perhaps make these json serialisable. Certainly would make parsing
@ -20,7 +21,9 @@ pub enum Filter {
} }
impl Filter { impl Filter {
fn optimise(mut self) -> Self { // Does this need mut self? Aren't we returning
// a new copied filter?
fn optimise(&self) -> Self {
// Apply optimisations to the filter // Apply optimisations to the filter
// An easy way would be imple partialOrd // An easy way would be imple partialOrd
// then do sort on the or/and/not // then do sort on the or/and/not
@ -33,12 +36,7 @@ impl Filter {
// If an or/not/and condition has no items, remove it // If an or/not/and condition has no items, remove it
// //
// If its the root item? // If its the root item?
self self.clone()
}
// In the future this will probably be used with schema ...
fn validate(&self) -> Result<(), ()> {
Ok(())
} }
// This is probably not safe, so it's for internal test cases // This is probably not safe, so it's for internal test cases
@ -62,7 +60,7 @@ impl Filter {
Filter::Sub(_, _) => false, Filter::Sub(_, _) => false,
Filter::Pres(attr) => { Filter::Pres(attr) => {
// Given attr, is is present in the entry? // Given attr, is is present in the entry?
e.pres(attr.as_str()) e.attribute_pres(attr.as_str())
} }
Filter::Or(_) => false, Filter::Or(_) => false,
Filter::And(_) => false, Filter::And(_) => false,
@ -130,6 +128,7 @@ impl PartialOrd for Filter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::schema::Schema;
use super::Filter; use super::Filter;
use serde_json; use serde_json;
use std::cmp::{Ordering, PartialOrd}; use std::cmp::{Ordering, PartialOrd};

View file

@ -1,7 +1,5 @@
#![feature(try_from)] #![feature(try_from)]
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
#[macro_use] #[macro_use]

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@ use be::Backend;
use entry::Entry; use entry::Entry;
use event::{CreateEvent, EventResult, SearchEvent}; use event::{CreateEvent, EventResult, SearchEvent};
use log::EventLog; use log::EventLog;
use schema::Schema;
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> { pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
let mut audit = AuditEvent::new(); let mut audit = AuditEvent::new();
@ -12,12 +13,16 @@ pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::A
// Create the BE connection // Create the BE connection
// probably need a config type soon .... // probably need a config type soon ....
let be = Backend::new(&mut audit, path); let be = Backend::new(&mut audit, path);
let mut schema = Schema::new();
schema.bootstrap_core();
// now we clone it out in the startup I think // now we clone it out in the startup I think
// Should the be need a log clone ref? or pass it around? // Should the be need a log clone ref? or pass it around?
// it probably needs it ... // it probably needs it ...
audit.end_event("server_new"); audit.end_event("server_new");
log.do_send(audit); log.do_send(audit);
SyncArbiter::start(threads, move || QueryServer::new(log.clone(), be.clone())) SyncArbiter::start(threads, move || {
QueryServer::new(log.clone(), be.clone(), schema.clone())
})
} }
// This is the core of the server. It implements all // This is the core of the server. It implements all
@ -35,12 +40,17 @@ pub struct QueryServer {
// I think the BE is build, configured and cloned? Maybe Backend // I think the BE is build, configured and cloned? Maybe Backend
// is a wrapper type to Arc<BackendInner> or something. // is a wrapper type to Arc<BackendInner> or something.
be: Backend, be: Backend,
schema: Schema,
} }
impl QueryServer { impl QueryServer {
pub fn new(log: actix::Addr<EventLog>, be: Backend) -> Self { pub fn new(log: actix::Addr<EventLog>, be: Backend, schema: Schema) -> Self {
log_event!(log, "Starting query worker ..."); log_event!(log, "Starting query worker ...");
QueryServer { log: log, be: be } QueryServer {
log: log,
be: be,
schema: schema,
}
} }
// Actually conduct a search request // Actually conduct a search request
@ -59,6 +69,18 @@ impl QueryServer {
pub fn create(&mut self, au: &mut AuditEvent, ce: &CreateEvent) -> Result<(), ()> { pub fn create(&mut self, au: &mut AuditEvent, ce: &CreateEvent) -> Result<(), ()> {
// Start a txn // Start a txn
// Run any pre checks // Run any pre checks
let r = ce.entries.iter().fold(Ok(()), |acc, e| {
if acc.is_ok() {
self.schema.validate_entry(e).map_err(|_| ())
} else {
acc
}
});
if r.is_err() {
return r;
}
// We may change from ce.entries later to something else? // We may change from ce.entries later to something else?
match self.be.create(au, &ce.entries) { match self.be.create(au, &ce.entries) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@ -146,6 +168,7 @@ mod tests {
use super::super::event::{CreateEvent, SearchEvent}; use super::super::event::{CreateEvent, SearchEvent};
use super::super::filter::Filter; use super::super::filter::Filter;
use super::super::log; use super::super::log;
use super::super::schema::Schema;
use super::super::server::QueryServer; use super::super::server::QueryServer;
macro_rules! run_test { macro_rules! run_test {
@ -155,7 +178,9 @@ mod tests {
let test_log = log::start(); let test_log = log::start();
let be = Backend::new(&mut audit, ""); let be = Backend::new(&mut audit, "");
let test_server = QueryServer::new(test_log.clone(), be); let mut schema = Schema::new();
schema.bootstrap_core();
let test_server = QueryServer::new(test_log.clone(), be, schema);
// Could wrap another future here for the future::ok bit... // Could wrap another future here for the future::ok bit...
let fut = $test_fn(test_log.clone(), test_server, &mut audit); let fut = $test_fn(test_log.clone(), test_server, &mut audit);
@ -174,14 +199,22 @@ mod tests {
#[test] #[test]
fn test_be_create_user() { fn test_be_create_user() {
run_test!(|_log, mut server: QueryServer, audit: &mut AuditEvent| { run_test!(|_log, mut server: QueryServer, audit: &mut AuditEvent| {
let filt = Filter::Pres(String::from("userid")); let filt = Filter::Pres(String::from("name"));
let se1 = SearchEvent::new(filt.clone()); let se1 = SearchEvent::new(filt.clone());
let se2 = SearchEvent::new(filt); let se2 = SearchEvent::new(filt);
let mut e: Entry = Entry::new(); let e: Entry = serde_json::from_str(
e.add_ava(String::from("userid"), String::from("william")) r#"{
.unwrap(); "attrs": {
"class": ["person"],
"name": ["testperson"],
"description": ["testperson"],
"displayname": ["testperson"]
}
}"#,
)
.unwrap();
let expected = vec![e]; let expected = vec![e];
@ -194,6 +227,7 @@ mod tests {
assert!(cr.is_ok()); assert!(cr.is_ok());
let r2 = server.search(audit, &se2).unwrap(); let r2 = server.search(audit, &se2).unwrap();
println!("--> {:?}", r2);
assert!(r2.len() == 1); assert!(r2.len() == 1);
assert_eq!(r2, expected); assert_eq!(r2, expected);