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));
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"))
.unwrap();
assert!(e.validate());
e.add_ava(String::from("userid"), String::from("william"));
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
// 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
// How do we make this turn into an ok / err?
self.attrs
.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]);
Ok(())
}
pub fn get_ava(&self, attr: &String) -> Option<&Vec<String>> {
self.attrs.get(attr)
}
pub fn validate(&self) -> bool {
// We need access to the current system schema here now ...
true
pub fn attribute_pres(&self, attr: &str) -> bool {
// FIXME: Do we need to normalise attr name?
self.attrs.contains_key(attr)
}
pub fn pres(&self, attr: &str) -> bool {
self.attrs.contains_key(attr)
pub fn attribute_equality(&self, attr: &str, value: &str) -> bool {
// 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 {
@ -229,42 +250,55 @@ mod tests {
#[test]
fn test_user_basic() {
let u: User = User::new("william", "William Brown");
println!("u: {:?}", u);
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();
println!("u2: {:?}", u2);
}
#[test]
fn test_entry_basic() {
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"))
.unwrap();
assert!(e.validate());
e.add_ava(String::from("userid"), String::from("william"));
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]
fn test_entry_pres() {
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"))
.unwrap();
e.add_ava(String::from("userid"), String::from("william"));
assert!(e.validate());
assert!(e.attribute_pres("userid"));
assert!(!e.attribute_pres("name"));
}
assert!(e.pres("userid"));
assert!(!e.pres("name"));
#[test]
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,
INVALID_ATTRIBUTE,
INVALID_ATTRIBUTE_SYNTAX,
EMPTY_FILTER,
}

View file

@ -3,6 +3,7 @@
// entry to assert it matches.
use super::entry::Entry;
use super::schema::Schema;
use std::cmp::{Ordering, PartialOrd};
// Perhaps make these json serialisable. Certainly would make parsing
@ -20,7 +21,9 @@ pub enum 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
// An easy way would be imple partialOrd
// 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 its the root item?
self
}
// In the future this will probably be used with schema ...
fn validate(&self) -> Result<(), ()> {
Ok(())
self.clone()
}
// This is probably not safe, so it's for internal test cases
@ -62,7 +60,7 @@ impl Filter {
Filter::Sub(_, _) => false,
Filter::Pres(attr) => {
// Given attr, is is present in the entry?
e.pres(attr.as_str())
e.attribute_pres(attr.as_str())
}
Filter::Or(_) => false,
Filter::And(_) => false,
@ -130,6 +128,7 @@ impl PartialOrd for Filter {
#[cfg(test)]
mod tests {
use super::super::schema::Schema;
use super::Filter;
use serde_json;
use std::cmp::{Ordering, PartialOrd};

View file

@ -1,7 +1,5 @@
#![feature(try_from)]
extern crate serde;
extern crate serde_json;
#[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 event::{CreateEvent, EventResult, SearchEvent};
use log::EventLog;
use schema::Schema;
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
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
// probably need a config type soon ....
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
// Should the be need a log clone ref? or pass it around?
// it probably needs it ...
audit.end_event("server_new");
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
@ -35,12 +40,17 @@ pub struct QueryServer {
// I think the BE is build, configured and cloned? Maybe Backend
// is a wrapper type to Arc<BackendInner> or something.
be: Backend,
schema: Schema,
}
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 ...");
QueryServer { log: log, be: be }
QueryServer {
log: log,
be: be,
schema: schema,
}
}
// Actually conduct a search request
@ -59,6 +69,18 @@ impl QueryServer {
pub fn create(&mut self, au: &mut AuditEvent, ce: &CreateEvent) -> Result<(), ()> {
// Start a txn
// 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?
match self.be.create(au, &ce.entries) {
Ok(_) => Ok(()),
@ -146,6 +168,7 @@ mod tests {
use super::super::event::{CreateEvent, SearchEvent};
use super::super::filter::Filter;
use super::super::log;
use super::super::schema::Schema;
use super::super::server::QueryServer;
macro_rules! run_test {
@ -155,7 +178,9 @@ mod tests {
let test_log = log::start();
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...
let fut = $test_fn(test_log.clone(), test_server, &mut audit);
@ -174,14 +199,22 @@ mod tests {
#[test]
fn test_be_create_user() {
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 se2 = SearchEvent::new(filt);
let mut e: Entry = Entry::new();
e.add_ava(String::from("userid"), String::from("william"))
.unwrap();
let e: Entry = serde_json::from_str(
r#"{
"attrs": {
"class": ["person"],
"name": ["testperson"],
"description": ["testperson"],
"displayname": ["testperson"]
}
}"#,
)
.unwrap();
let expected = vec![e];
@ -194,6 +227,7 @@ mod tests {
assert!(cr.is_ok());
let r2 = server.search(audit, &se2).unwrap();
println!("--> {:?}", r2);
assert!(r2.len() == 1);
assert_eq!(r2, expected);