Fixed a large number of error cases

This commit is contained in:
William Brown 2018-11-20 16:51:10 +10:00
parent 8ff46dd4c1
commit f40fdee9cd
6 changed files with 94 additions and 119 deletions

View file

@ -1,7 +1,6 @@
// use serde_json::{Error, Value};
use std::collections::btree_map::{Iter as BTreeIter, IterMut as BTreeIterMut};
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::slice::Iter as SliceIter;
// make a trait entry for everything to adhere to?
@ -116,10 +115,15 @@ impl Entry {
.entry(attr)
.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 ...
// FIXME: Because map_err is lazy, this won't do anything on release
match v.binary_search(&value) {
Ok(_) => {}
Err(idx) => {
// This cloning is to fix a borrow issue with the or_insert below.
// Is there a better way?
v.insert(idx, value.clone())
});
}
}
})
.or_insert(vec![value]);
}
@ -281,7 +285,7 @@ mod tests {
let u: User = User::new("william", "William Brown");
let d = serde_json::to_string_pretty(&u).unwrap();
let u2: User = serde_json::from_str(d.as_str()).unwrap();
let _u2: User = serde_json::from_str(d.as_str()).unwrap();
}
#[test]
@ -290,7 +294,7 @@ mod tests {
e.add_ava(String::from("userid"), String::from("william"));
let d = serde_json::to_string_pretty(&e).unwrap();
let _d = serde_json::to_string_pretty(&e).unwrap();
}
#[test]

View file

@ -1,10 +1,10 @@
#[derive(Debug, PartialEq)]
pub enum SchemaError {
NOT_IMPLEMENTED,
INVALID_CLASS,
NotImplemented,
InvalidClass,
// FIXME: Is there a way to say what we are missing on error?
MISSING_MUST_ATTRIBUTE,
INVALID_ATTRIBUTE,
INVALID_ATTRIBUTE_SYNTAX,
EMPTY_FILTER,
MissingMustAttribute,
InvalidAttribute,
InvalidAttributeSyntax,
EmptyFilter,
}

View file

@ -3,7 +3,6 @@
// 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
@ -128,7 +127,6 @@ 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

@ -19,7 +19,6 @@ macro_rules! log_event {
})
}
// We need to pass in config for this later
// Or we need to pass in the settings for it IE level and dest?
// Is there an efficent way to set a log level filter in the macros

View file

@ -12,7 +12,7 @@ extern crate uuid;
// use actix::prelude::*;
use actix_web::{
error, http, middleware, App, AsyncResponder, Error, FutureResponse, HttpMessage, HttpRequest,
HttpResponse, Json, Path, State
HttpResponse, Path, State,
};
use bytes::BytesMut;
@ -72,68 +72,55 @@ fn class_list((_name, state): (Path<String>, State<AppState>)) -> FutureResponse
}
fn search(
(item, req): (Json<SearchRequest>, HttpRequest<AppState>),
) -> Box<Future<Item = HttpResponse, Error = Error>> {
Box::new(
req.state()
.qe
.send(
// FIXME: Item is BORROWED, so we have to .clone it.
// We should treat this as immutable and let the caller
// clone when mutation is required ...
// however, that involves lifetime complexities.
event::SearchEvent::new(item.filter.clone()),
)
.from_err()
.and_then(|res| match res {
// FIXME: entries should not be EventResult type
Ok(entries) => Ok(HttpResponse::Ok().json(entries)),
Err(_) => Ok(HttpResponse::InternalServerError().into()),
// Err(_) => Ok(error::ErrorInternalServerError("Test error").into()),
}),
)
}
// Based on actix web example json
fn search2(req: &HttpRequest<AppState>) -> Box<Future<Item = HttpResponse, Error = Error>> {
println!("{:?}", req);
(req, state): (HttpRequest<AppState>, State<AppState>),
) -> impl Future<Item = HttpResponse, Error = Error> {
// HttpRequest::payload() is stream of Bytes objects
req.payload()
// `Future::from_err` acts like `?` in that it coerces the error type from
// the future into the final error type
.from_err()
// `fold` will asynchronously read each chunk of the request body and
// call supplied closure, then it resolves to result of closure
.fold(BytesMut::new(), move |mut body, chunk| {
// limit max size of in-memory payload
if (body.len() + chunk.len()) > MAX_SIZE {
Err(error::ErrorBadRequest("Request size too large."))
Err(error::ErrorBadRequest("overflow"))
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(|body| {
// `Future::and_then` can be used to merge an asynchronous workflow with a
// synchronous workflow
.and_then(
move |body| -> Box<Future<Item = HttpResponse, Error = Error>> {
// body is loaded, now we can deserialize serde-json
// FIXME: THIS IS FUCKING AWFUL
let obj = serde_json::from_slice::<SearchRequest>(&body).unwrap();
// Dispatch a search
println!("{:?}", obj);
// We have to resolve this NOW else we break everything :(
/*
req.state().qe.send(
event::SearchEvent::new(obj.filter)
let r_obj = serde_json::from_slice::<SearchRequest>(&body);
// Send to the db for create
match r_obj {
Ok(obj) => {
let res = state
.qe
.send(
// Could make this a .into_inner() and move?
event::SearchEvent::new(obj.filter),
)
.from_err()
.and_then(|res| future::result(match res {
// What type is entry?
Ok(event::EventResult::Search { entries }) => Ok(HttpResponse::Ok().json(entries)),
Ok(_) => Ok(HttpResponse::Ok().into()),
// Can we properly report this?
.and_then(|res| match res {
Ok(entries) => Ok(HttpResponse::Ok().json(entries)),
Err(_) => Ok(HttpResponse::InternalServerError().into()),
}))
*/
Ok(HttpResponse::InternalServerError().into())
})
.responder()
});
Box::new(res)
}
Err(e) => Box::new(future::err(error::ErrorBadRequest(format!(
"Json Decode Failed: {:?}",
e
)))),
}
},
)
}
fn main() {
@ -171,15 +158,8 @@ fn main() {
*/
// curl --header "Content-Type: application/json" --request POST --data '{ "filter" : { "Eq": ["class", "user"] }}' http://127.0.0.1:8080/search
.resource("/search", |r| {
r.method(http::Method::POST)
/*
.with_config(extract_item_limit, |cfg| {
cfg.0.limit(4096); // <- limit size of the payload
r.method(http::Method::POST).with_async(search)
})
*/
.with(search)
})
.resource("/search2", |r| r.method(http::Method::POST).a(search2))
// Add an ldap compat search function type?
.resource("/list/{class_list}", |r| {
r.method(http::Method::GET).with(class_list)

View file

@ -21,6 +21,7 @@ enum Ternary {
False,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, PartialEq)]
pub enum IndexType {
EQUALITY,
@ -44,6 +45,7 @@ impl TryFrom<&str> for IndexType {
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, PartialEq)]
pub enum SyntaxType {
// We need an insensitive string type too ...
@ -94,19 +96,19 @@ impl SchemaAttribute {
// Validation.
fn validate_bool(&self, v: &String) -> Result<(), SchemaError> {
bool::from_str(v.as_str())
.map_err(|_| SchemaError::INVALID_ATTRIBUTE_SYNTAX)
.map_err(|_| SchemaError::InvalidAttributeSyntax)
.map(|_| ())
}
fn validate_syntax(&self, v: &String) -> Result<(), SchemaError> {
SyntaxType::try_from(v.as_str())
.map_err(|_| SchemaError::INVALID_ATTRIBUTE_SYNTAX)
.map_err(|_| SchemaError::InvalidAttributeSyntax)
.map(|_| ())
}
fn validate_index(&self, v: &String) -> Result<(), SchemaError> {
IndexType::try_from(v.as_str())
.map_err(|_| SchemaError::INVALID_ATTRIBUTE_SYNTAX)
.map_err(|_| SchemaError::InvalidAttributeSyntax)
.map(|_| ())
}
@ -116,7 +118,7 @@ impl SchemaAttribute {
if &t == v {
Ok(())
} else {
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
}
}
@ -133,7 +135,7 @@ impl SchemaAttribute {
pub fn validate_ava(&self, ava: &Vec<String>) -> Result<(), SchemaError> {
// Check multivalue
if self.multivalue == false && ava.len() > 1 {
return Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX);
return Err(SchemaError::InvalidAttributeSyntax);
};
// If syntax, check the type is correct
match self.syntax {
@ -639,7 +641,7 @@ impl Schema {
});
if c_valid != Ternary::True {
return Err(SchemaError::INVALID_CLASS);
return Err(SchemaError::InvalidClass);
};
let classes: HashMap<String, &SchemaClass> = entry
@ -666,6 +668,7 @@ impl Schema {
.map(|s| (s.clone(), self.attributes.get(s).unwrap()))
.collect();
/*
let may: HashMap<String, &SchemaAttribute> = classes
.iter()
// Join our class systemmmust + must into one iter
@ -678,16 +681,18 @@ impl Schema {
})
.map(|s| (s.clone(), self.attributes.get(s).unwrap()))
.collect();
*/
// FIXME: Error needs to say what is missing
// We need to return *all* missing attributes.
// Check that all must are inplace
// for each attr in must, check it's present on our ent
for (attr_name, attr) in must {
// FIXME: Could we iter over only the attr_name
for (attr_name, _attr) in must {
let avas = entry.get_ava(&attr_name);
if avas.is_none() {
return Err(SchemaError::MISSING_MUST_ATTRIBUTE);
return Err(SchemaError::MissingMustAttribute);
}
}
@ -706,7 +711,7 @@ impl Schema {
}
None => {
if !extensible {
return Err(SchemaError::INVALID_ATTRIBUTE);
return Err(SchemaError::InvalidAttribute);
}
}
}
@ -755,25 +760,25 @@ impl Schema {
match filt {
Filter::Eq(attr, value) => match self.attributes.get(attr) {
Some(schema_a) => schema_a.validate_value(value),
None => Err(SchemaError::INVALID_ATTRIBUTE),
None => Err(SchemaError::InvalidAttribute),
},
Filter::Sub(attr, value) => match self.attributes.get(attr) {
Some(schema_a) => schema_a.validate_value(value),
None => Err(SchemaError::INVALID_ATTRIBUTE),
None => Err(SchemaError::InvalidAttribute),
},
Filter::Pres(attr) => {
// This could be better as a contains_key
// because we never use the value
match self.attributes.get(attr) {
Some(_) => Ok(()),
None => Err(SchemaError::INVALID_ATTRIBUTE),
None => Err(SchemaError::InvalidAttribute),
}
}
Filter::Or(filters) => {
// This should never happen because
// optimising should remove them as invalid parts?
if filters.len() == 0 {
return Err(SchemaError::EMPTY_FILTER);
return Err(SchemaError::EmptyFilter);
};
filters.iter().fold(Ok(()), |acc, filt| {
if acc.is_ok() {
@ -787,7 +792,7 @@ impl Schema {
// This should never happen because
// optimising should remove them as invalid parts?
if filters.len() == 0 {
return Err(SchemaError::EMPTY_FILTER);
return Err(SchemaError::EmptyFilter);
};
filters.iter().fold(Ok(()), |acc, filt| {
if acc.is_ok() {
@ -801,7 +806,7 @@ impl Schema {
// This should never happen because
// optimising should remove them as invalid parts?
if filters.len() == 0 {
return Err(SchemaError::EMPTY_FILTER);
return Err(SchemaError::EmptyFilter);
};
filters.iter().fold(Ok(()), |acc, filt| {
if acc.is_ok() {
@ -867,17 +872,6 @@ mod tests {
#[test]
fn test_schema_attribute_simple() {
let class_attribute = SchemaAttribute {
// class: vec![String::from("attributetype")],
name: String::from("class"),
description: String::from("The set of classes defining an object"),
system: true,
secret: false,
multivalue: true,
index: vec![IndexType::EQUALITY],
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
};
// Test schemaAttribute validation of types.
// Test single value string
@ -897,7 +891,7 @@ mod tests {
let r2 =
single_value_string.validate_ava(&vec![String::from("test1"), String::from("test2")]);
assert_eq!(r2, Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX));
assert_eq!(r2, Err(SchemaError::InvalidAttributeSyntax));
// test multivalue string, boolean
@ -929,7 +923,7 @@ mod tests {
let r3 =
multi_value_boolean.validate_ava(&vec![String::from("test1"), String::from("test2")]);
assert_eq!(r3, Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX));
assert_eq!(r3, Err(SchemaError::InvalidAttributeSyntax));
let r4 =
multi_value_boolean.validate_ava(&vec![String::from("true"), String::from("false")]);
@ -951,7 +945,7 @@ mod tests {
assert_eq!(r6, Ok(()));
let r7 = single_value_syntax.validate_ava(&vec![String::from("thaeountaheu")]);
assert_eq!(r7, Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX));
assert_eq!(r7, Err(SchemaError::InvalidAttributeSyntax));
let single_value_index = SchemaAttribute {
// class: vec![String::from("attributetype")],
@ -968,7 +962,7 @@ mod tests {
assert_eq!(r8, Ok(()));
let r9 = single_value_index.validate_ava(&vec![String::from("thaeountaheu")]);
assert_eq!(r9, Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX));
assert_eq!(r9, Err(SchemaError::InvalidAttributeSyntax));
}
#[test]
@ -1002,7 +996,7 @@ mod tests {
assert_eq!(
schema.validate_entry(&e_no_class),
Err(SchemaError::INVALID_CLASS)
Err(SchemaError::InvalidClass)
);
let e_bad_class: Entry = serde_json::from_str(
@ -1015,7 +1009,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_entry(&e_bad_class),
Err(SchemaError::INVALID_CLASS)
Err(SchemaError::InvalidClass)
);
let e_attr_invalid: Entry = serde_json::from_str(
@ -1029,7 +1023,7 @@ mod tests {
assert_eq!(
schema.validate_entry(&e_attr_invalid),
Err(SchemaError::MISSING_MUST_ATTRIBUTE)
Err(SchemaError::MissingMustAttribute)
);
let e_attr_invalid_may: Entry = serde_json::from_str(
@ -1050,7 +1044,7 @@ mod tests {
assert_eq!(
schema.validate_entry(&e_attr_invalid_may),
Err(SchemaError::INVALID_ATTRIBUTE)
Err(SchemaError::InvalidAttribute)
);
let e_attr_invalid_syn: Entry = serde_json::from_str(
@ -1070,7 +1064,7 @@ mod tests {
assert_eq!(
schema.validate_entry(&e_attr_invalid_syn),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
let e_ok: Entry = serde_json::from_str(
@ -1114,7 +1108,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_entry(&e_test),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
let e_expect: Entry = serde_json::from_str(
@ -1154,7 +1148,7 @@ mod tests {
assert_eq!(
schema.validate_entry(&e_extensible_bad),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
let e_extensible: Entry = serde_json::from_str(
@ -1210,7 +1204,7 @@ mod tests {
#[test]
fn test_schema_filter_validation() {
let mut schema = Schema::new();
let schema = Schema::new();
// Test mixed case attr name
let f_mixed: Filter = serde_json::from_str(
r#"{
@ -1222,7 +1216,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_filter(&f_mixed),
Err(SchemaError::INVALID_ATTRIBUTE)
Err(SchemaError::InvalidAttribute)
);
// test syntax of bool
let f_bool: Filter = serde_json::from_str(
@ -1235,7 +1229,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_filter(&f_bool),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
// test insensitise values
let f_insense: Filter = serde_json::from_str(
@ -1248,7 +1242,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_filter(&f_insense),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
// Test the recursive structures validate
let f_or_empty: Filter = serde_json::from_str(
@ -1259,7 +1253,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_filter(&f_or_empty),
Err(SchemaError::EMPTY_FILTER)
Err(SchemaError::EmptyFilter)
);
let f_or: Filter = serde_json::from_str(
r#"{
@ -1271,7 +1265,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_filter(&f_or),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
let f_or_mult: Filter = serde_json::from_str(
r#"{
@ -1284,7 +1278,7 @@ mod tests {
.unwrap();
assert_eq!(
schema.validate_filter(&f_or_mult),
Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX)
Err(SchemaError::InvalidAttributeSyntax)
);
let f_or_ok: Filter = serde_json::from_str(
r#"{