diff --git a/CHANGELOG b/CHANGELOG index 7b38e8b..6be3a2c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,12 @@ ## Unreleased +### Added +- Added `links` objects per json:api specification +- Added configuration option for pagination + ### Changed - CibouletteId::build_id moved to CibouletteIdSelector::build - +- Better performance when building documents and responses +- Changed how resource are built, using a new structure holding more options ## [0.1.4] - 2021-05-13 ### Fixed diff --git a/src/body/mod.rs b/src/body/mod.rs index 1495139..c948410 100644 --- a/src/body/mod.rs +++ b/src/body/mod.rs @@ -9,8 +9,6 @@ pub mod request_body_data; pub mod resource_identifier; pub mod resource_obj; pub mod resource_obj_selector; -pub mod resource_type; -pub mod resource_type_builder; #[cfg(test)] mod tests; diff --git a/src/errors.rs b/src/errors.rs index ff30920..c8acda9 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -108,6 +108,8 @@ pub enum CibouletteError { ResponseTooManyMainData(String), #[error("Wrong number of id, got {0}, expected {1}")] WrongIdNumber(usize, usize), + #[error("No pagination field {1} for type {0}.")] + UnknownPaginationField(String, String), #[error(transparent)] CibouletteSelectorError(#[from] selector::CibouletteSelectorError), /// When there is a failure while deserializing the JSON diff --git a/src/id.rs b/src/id.rs index 2173c8c..02277f7 100644 --- a/src/id.rs +++ b/src/id.rs @@ -140,6 +140,16 @@ pub enum CibouletteIdType { Uuid(ArcStr), } +impl CibouletteIdType { + pub fn name(&self) -> &ArcStr { + match self { + CibouletteIdType::Text(x) => x, + CibouletteIdType::Number(x) => x, + CibouletteIdType::Uuid(x) => x, + } + } +} + impl<'request> std::fmt::Display for CibouletteIdType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/src/lib.rs b/src/lib.rs index f3f6490..9722982 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,8 +168,6 @@ pub use body::resource_obj::{CibouletteResource, CibouletteResourceBuilder}; pub use body::resource_obj_selector::{ CibouletteResourceSelector, CibouletteResourceSelectorBuilder, }; -pub use body::resource_type::{CibouletteResourceRelationshipDetails, CibouletteResourceType}; -pub use body::resource_type_builder::CibouletteResourceTypeBuilder; pub use id::{CibouletteId, CibouletteIdSelector, CibouletteIdType, CibouletteIdTypeSelector}; pub use intention::CibouletteIntention; pub use responses::request::CibouletteResponse; @@ -209,12 +207,24 @@ pub use responses::type_::{CibouletteResponseQuantity, CibouletteResponseRequire pub use path::path_container::{CiboulettePath, CiboulettePathBuilder}; pub use selector::{CibouletteSelector, CibouletteSelectorError, CibouletteSelectorIterator}; -pub use store::{ - CibouletteRelationshipManyToManyOption, CibouletteRelationshipManyToManyOptionBuilder, - CibouletteRelationshipOneToManyOption, CibouletteRelationshipOneToManyOptionBuilder, - CibouletteRelationshipOption, CibouletteRelationshipOptionBuilder, CibouletteStore, - CibouletteStoreBuilder, +pub use store::builder::CibouletteStoreBuilder; +pub use store::relationships_options::{ + CibouletteRelationshipManyToManyOption, CibouletteRelationshipOneToManyOption, + CibouletteRelationshipOption, }; +pub use store::relationships_options_builder::{ + CibouletteRelationshipManyToManyOptionBuilder, CibouletteRelationshipOneToManyOptionBuilder, + CibouletteRelationshipOptionBuilder, +}; +pub use store::resource_type::{CibouletteResourceRelationshipDetails, CibouletteResourceType}; +pub use store::resource_type_builder::CibouletteResourceTypeBuilder; +pub use store::resource_type_configuration::{ + CiboulettePaginationEncoding, CibouletteResourceTypeConfiguration, + CibouletteResourceTypePaginationConfiguration, + CibouletteResourceTypePaginationConfigurationBuilder, +}; + +pub use store::CibouletteStore; #[cfg(test)] pub mod test_helper; diff --git a/src/store/builder.rs b/src/store/builder.rs index 73f0b1e..6845048 100644 --- a/src/store/builder.rs +++ b/src/store/builder.rs @@ -111,8 +111,7 @@ impl CibouletteStoreBuilder { pub fn add_type( &mut self, name: &str, - ids: CibouletteIdTypeSelector, - schema: MessyJsonObject, + config: CibouletteResourceTypeConfiguration, ) -> Result<(), CibouletteError> { let name = name.to_string(); if self.map.contains_key(name.as_str()) @@ -120,7 +119,7 @@ impl CibouletteStoreBuilder { { return Err(CibouletteError::UniqType(name)); } - let t = CibouletteResourceTypeBuilder::new(name.clone(), ids, schema).build()?; + let t = CibouletteResourceTypeBuilder::new(name.clone(), config).build()?; let index = self.graph.add_node(t); // Add the node self.map.insert(name, index); // Save the index to the map Ok(()) diff --git a/src/store/mod.rs b/src/store/mod.rs index 3e2f6cc..854abfc 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -1,20 +1,14 @@ use super::*; use getset::CopyGetters; -mod builder; -mod many_to_many; -mod one_to_many; -mod relationships_options; -mod relationships_options_builder; -pub use builder::CibouletteStoreBuilder; -pub use relationships_options::{ - CibouletteRelationshipManyToManyOption, CibouletteRelationshipOneToManyOption, - CibouletteRelationshipOption, -}; -pub use relationships_options_builder::{ - CibouletteRelationshipManyToManyOptionBuilder, CibouletteRelationshipOneToManyOptionBuilder, - CibouletteRelationshipOptionBuilder, -}; +pub mod builder; +pub mod many_to_many; +pub mod one_to_many; +pub mod relationships_options; +pub mod relationships_options_builder; +pub mod resource_type; +pub mod resource_type_builder; +pub mod resource_type_configuration; #[cfg(test)] mod tests; diff --git a/src/body/resource_type.rs b/src/store/resource_type.rs similarity index 97% rename from src/body/resource_type.rs rename to src/store/resource_type.rs index 16c748a..9f77e00 100644 --- a/src/body/resource_type.rs +++ b/src/store/resource_type.rs @@ -9,6 +9,7 @@ pub struct CibouletteResourceType { relationships_type_to_alias: BTreeMap, schema: MessyJsonObject, ids: CibouletteIdTypeSelector, + pagination: Option, name: ArcStr, } @@ -27,12 +28,14 @@ impl CibouletteResourceType { name: ArcStr, ids: CibouletteIdTypeSelector, schema: MessyJsonObject, + pagination: Option, ) -> Self { CibouletteResourceType { relationships: BTreeMap::new(), relationships_type_to_alias: BTreeMap::new(), schema, ids, + pagination, name, } } diff --git a/src/body/resource_type_builder.rs b/src/store/resource_type_builder.rs similarity index 80% rename from src/body/resource_type_builder.rs rename to src/store/resource_type_builder.rs index 54c1315..d58b2e1 100644 --- a/src/body/resource_type_builder.rs +++ b/src/store/resource_type_builder.rs @@ -10,17 +10,19 @@ pub struct CibouletteResourceTypeBuilder { relationships_type_to_alias: BTreeMap, schema: MessyJsonObject, ids: CibouletteIdTypeSelector, + pagination: Option, name: ArcStr, } impl CibouletteResourceTypeBuilder { /// Create a new type from a schema and a list of relationships - pub fn new(name: String, id_type: CibouletteIdTypeSelector, schema: MessyJsonObject) -> Self { + pub fn new(name: String, config: CibouletteResourceTypeConfiguration) -> Self { CibouletteResourceTypeBuilder { relationships: BTreeMap::new(), relationships_type_to_alias: BTreeMap::new(), - schema, - ids: id_type, + schema: config.schema, + ids: config.ids, + pagination: config.pagination, name: ArcStr::from(name), } } @@ -56,11 +58,16 @@ impl CibouletteResourceTypeBuilder { if let Some(x) = Self::check_member_name_obj(self.schema()) { return Err(CibouletteError::InvalidMemberName(x)); } - Ok(CibouletteResourceType::new( - self.name, - self.ids, - self.schema, - )) + let name = self.name; + let schema = self.schema; + let ids = self.ids; + + let pagination = match self.pagination { + Some(pagination) => Some(pagination.build(name.as_str(), &ids, &schema)?), + None => None, + }; + + Ok(CibouletteResourceType::new(name, ids, schema, pagination)) } } diff --git a/src/store/resource_type_configuration.rs b/src/store/resource_type_configuration.rs new file mode 100644 index 0000000..5c5dd22 --- /dev/null +++ b/src/store/resource_type_configuration.rs @@ -0,0 +1,121 @@ +use super::*; + +/// Configuration needed to add a new type to the ciboulette store +#[derive(Clone, Debug, Getters, MutGetters)] +#[getset(get = "pub", get_mut = "pub(crate)")] +pub struct CibouletteResourceTypeConfiguration { + pub ids: CibouletteIdTypeSelector, + pub schema: MessyJsonObject, + pub pagination: Option, +} + +impl CibouletteResourceTypeConfiguration { + pub fn new( + ids: CibouletteIdTypeSelector, + schema: MessyJsonObject, + pagination: Option, + ) -> Self { + CibouletteResourceTypeConfiguration { + ids, + schema, + pagination, + } + } +} + +/// Pagination option builder for new ciboulette type +#[derive(Clone, Debug, Getters, MutGetters)] +#[getset(get = "pub", get_mut = "pub(crate)")] +pub struct CibouletteResourceTypePaginationConfigurationBuilder { + /// A list of fields ot sort by. i.e `["created_at", "id"]` + pub sort_by: Vec, + /// The discriminant keys. i.e. `["id"]` + pub discriminants: Vec, + /// encoding when generating the links. + pub encoding: CiboulettePaginationEncoding, +} + +impl CibouletteResourceTypePaginationConfigurationBuilder { + fn find_field_name( + name: &str, + resource_name: &str, + ids: &CibouletteIdTypeSelector, + schema: &MessyJsonObject, + ) -> Result { + schema + .properties() + .get_key_value(name) + .map(|x| x.0.clone()) + .or_else(|| { + ids.iter() + .find(|x| x.name().as_str() == name) + .map(|x| x.name().clone()) + }) + .ok_or_else(|| { + CibouletteError::UnknownField(resource_name.to_string(), name.to_string()) + }) + } + + pub fn build( + self, + type_name: &str, + ids: &CibouletteIdTypeSelector, + schema: &MessyJsonObject, + ) -> Result { + let mut sort_by: Vec = Vec::with_capacity(self.sort_by.len()); + let mut discriminants: Vec = Vec::with_capacity(self.discriminants.len()); + let mut sort_set = BTreeSet::new(); + let mut discriminant_set = BTreeSet::new(); + + for sort_el in self.sort_by() { + if !sort_set.insert(sort_el.as_str()) { + continue; + } + sort_by.push(Self::find_field_name( + sort_el.as_str(), + type_name, + ids, + schema, + )?); + } + for discriminant in self.discriminants() { + if !sort_set.contains(discriminant.as_str()) { + return Err(CibouletteError::UnknownPaginationField( + type_name.to_string(), + discriminant.clone(), + )); + } + let field = Self::find_field_name(discriminant.as_str(), type_name, ids, schema)?; + if !discriminant_set.insert(field.clone()) { + continue; + } + discriminants.push(field.clone()); + } + Ok(CibouletteResourceTypePaginationConfiguration { + sort_by, + discriminants, + encoding: self.encoding, + }) + } +} + +/// Pagination option for new ciboulette type +#[derive(Clone, Debug, Getters, MutGetters, Hash)] +#[getset(get = "pub", get_mut = "pub(crate)")] +pub struct CibouletteResourceTypePaginationConfiguration { + /// A list of fields ot sort by. i.e `["created_at", "id"]` + pub sort_by: Vec, + /// The discriminant keys. i.e. `["id"]` + pub discriminants: Vec, + /// encoding when generating the links. + pub encoding: CiboulettePaginationEncoding, +} + +/// Encoding option for pagination links +#[derive(Clone, Debug, Copy, Hash)] +pub enum CiboulettePaginationEncoding { + /// Comma separated + Raw, + /// Bincode -> base64 encoded + Base64, +} diff --git a/src/store/tests/many_to_many_rel.rs b/src/store/tests/many_to_many_rel.rs index 15fdfae..3283997 100644 --- a/src/store/tests/many_to_many_rel.rs +++ b/src/store/tests/many_to_many_rel.rs @@ -7,28 +7,37 @@ fn ok() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "people-article", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_people_article(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_people_article(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipManyToManyOptionBuilder::new( @@ -87,28 +96,37 @@ fn ok_reverse() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "people-article", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_people_article(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_people_article(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipManyToManyOptionBuilder::new( @@ -141,28 +159,37 @@ fn no_reverse() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "people-article", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_people_article(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_people_article(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipManyToManyOptionBuilder::new( @@ -196,28 +223,37 @@ fn duplicate() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "people-article", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_people_article(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_people_article(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipManyToManyOptionBuilder::new( @@ -252,28 +288,37 @@ fn alias() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "people-article", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_people_article(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_people_article(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipManyToManyOptionBuilder::new( diff --git a/src/store/tests/many_to_one_rel.rs b/src/store/tests/many_to_one_rel.rs index aa8e3b7..492efe8 100644 --- a/src/store/tests/many_to_one_rel.rs +++ b/src/store/tests/many_to_one_rel.rs @@ -7,19 +7,25 @@ fn ok() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "favorite_color", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_favorite_color(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_favorite_color(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( @@ -44,19 +50,25 @@ fn ok_reverse() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "favorite_color", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_favorite_color(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_favorite_color(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( @@ -81,19 +93,25 @@ fn no_reverse() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "favorite_color", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_favorite_color(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_favorite_color(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( @@ -119,19 +137,25 @@ fn duplicate() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "favorite_color", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_favorite_color(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_favorite_color(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( diff --git a/src/store/tests/mod.rs b/src/store/tests/mod.rs index de78771..bc9b415 100644 --- a/src/store/tests/mod.rs +++ b/src/store/tests/mod.rs @@ -3,4 +3,5 @@ use super::*; mod many_to_many_rel; mod many_to_one_rel; mod one_to_many_rel; +mod pagination_settings; mod types; diff --git a/src/store/tests/one_to_many_rel.rs b/src/store/tests/one_to_many_rel.rs index 134cd68..b677db2 100644 --- a/src/store/tests/one_to_many_rel.rs +++ b/src/store/tests/one_to_many_rel.rs @@ -7,28 +7,37 @@ fn ok() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "comments", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_comments(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_comments(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( @@ -55,28 +64,37 @@ fn ok_reverse() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "comments", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_comments(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_comments(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( @@ -103,28 +121,37 @@ fn no_reverse() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "comments", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_comments(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_comments(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( @@ -150,28 +177,37 @@ fn duplicate() { store .add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); store .add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); store .add_type( "comments", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - crate::test_helper::gen_messy_json_schema_comments(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + crate::test_helper::gen_messy_json_schema_comments(), + None, + ), ) .unwrap(); let opt = CibouletteRelationshipOneToManyOptionBuilder::new( diff --git a/src/store/tests/pagination_settings.rs b/src/store/tests/pagination_settings.rs new file mode 100644 index 0000000..d66766c --- /dev/null +++ b/src/store/tests/pagination_settings.rs @@ -0,0 +1,113 @@ +use super::*; + +#[test] +fn mismatching_discriminant() { + let builder = CibouletteResourceTypePaginationConfigurationBuilder { + sort_by: vec!["title".to_string(), "id".to_string()], + discriminants: vec!["author".to_string()], + encoding: CiboulettePaginationEncoding::Base64, + }; + + let res = builder + .build( + "toto", + &CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + &crate::test_helper::gen_messy_json_schema_articles(), + ) + .unwrap_err(); + assert_eq!( + matches!(res, CibouletteError::UnknownPaginationField(_, x) if x == "author"), + true + ); +} + +#[test] +fn unknown_sorting_field() { + let builder = CibouletteResourceTypePaginationConfigurationBuilder { + sort_by: vec!["title".to_string(), "aaaa".to_string()], + discriminants: vec!["author".to_string()], + encoding: CiboulettePaginationEncoding::Base64, + }; + + let res = builder + .build( + "toto", + &CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + &crate::test_helper::gen_messy_json_schema_articles(), + ) + .unwrap_err(); + assert_eq!( + matches!(res, CibouletteError::UnknownField(_, x) if x == "aaaa"), + true + ); +} + +#[test] +fn unknown_discriminant_field() { + let builder = CibouletteResourceTypePaginationConfigurationBuilder { + sort_by: vec!["title".to_string(), "id".to_string()], + discriminants: vec!["aaaa".to_string()], + encoding: CiboulettePaginationEncoding::Base64, + }; + + let res = builder + .build( + "toto", + &CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + &crate::test_helper::gen_messy_json_schema_articles(), + ) + .unwrap_err(); + assert_eq!( + matches!(res, CibouletteError::UnknownPaginationField(_, x) if x == "aaaa"), + true + ); +} + +#[test] +fn multiple_same_sort_fields() { + let builder = CibouletteResourceTypePaginationConfigurationBuilder { + sort_by: vec![ + "title".to_string(), + "title".to_string(), + "title".to_string(), + "id".to_string(), + ], + discriminants: vec!["id".to_string()], + encoding: CiboulettePaginationEncoding::Base64, + }; + + builder + .build( + "toto", + &CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + &crate::test_helper::gen_messy_json_schema_articles(), + ) + .unwrap(); +} + +#[test] +fn multiple_same_discriminant_fields() { + let builder = CibouletteResourceTypePaginationConfigurationBuilder { + sort_by: vec!["title".to_string(), "id".to_string()], + discriminants: vec!["id".to_string(), "id".to_string(), "id".to_string()], + encoding: CiboulettePaginationEncoding::Base64, + }; + + builder + .build( + "toto", + &CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + &crate::test_helper::gen_messy_json_schema_articles(), + ) + .unwrap(); +} diff --git a/src/store/tests/types.rs b/src/store/tests/types.rs index 11b744c..3ad75d0 100644 --- a/src/store/tests/types.rs +++ b/src/store/tests/types.rs @@ -7,10 +7,13 @@ fn add_ok() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); } @@ -22,19 +25,25 @@ fn add_duplicate() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let err = store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap_err(); assert_eq!( @@ -50,10 +59,13 @@ fn get_ok() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let type_ = store.get_type("toto"); @@ -67,10 +79,13 @@ fn get_not_found() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let err = store.get_type("aaaaaaaaaaaaaaaaaaa").unwrap_err(); @@ -87,10 +102,13 @@ fn get_optional_found() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let val = store.get_type_if_exists("toto"); @@ -104,10 +122,13 @@ fn get_optional_not_found() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let val = store.get_type_if_exists("aaaaaaaaaaa"); @@ -121,10 +142,13 @@ fn get_index() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let val = store.get_type_index("toto"); @@ -139,10 +163,13 @@ fn get_index_not_found() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let val = store.get_type_index("aaaa"); @@ -156,10 +183,13 @@ fn get_type_with_index() { store .add_type( "toto", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - MessyJsonObject::default(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + MessyJsonObject::default(), + None, + ), ) .unwrap(); let (index, type_) = store.get_type_with_index("toto").unwrap(); diff --git a/src/test_helper/mod.rs b/src/test_helper/mod.rs index 1b4a28a..8e6da51 100644 --- a/src/test_helper/mod.rs +++ b/src/test_helper/mod.rs @@ -128,44 +128,59 @@ pub fn gen_bag() -> CibouletteStore { res.add_type( "articles", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - gen_messy_json_schema_articles(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + gen_messy_json_schema_articles(), + None, + ), ) .unwrap(); res.add_type( "comments", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - gen_messy_json_schema_comments(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + gen_messy_json_schema_comments(), + None, + ), ) .unwrap(); res.add_type( "peoples", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - gen_messy_json_schema_peoples(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + gen_messy_json_schema_peoples(), + None, + ), ) .unwrap(); res.add_type( "favorite_color", - CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( - arcstr::literal!("id"), - ))), - gen_messy_json_schema_favorite_color(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Single(CibouletteIdType::Uuid( + arcstr::literal!("id"), + ))), + gen_messy_json_schema_favorite_color(), + None, + ), ) .unwrap(); res.add_type( "people-article", - CibouletteIdTypeSelector::new(CibouletteSelector::Multi(vec![ - CibouletteIdType::Uuid(arcstr::literal!("people_id")), - CibouletteIdType::Uuid(arcstr::literal!("article_id")), - ])), - gen_messy_json_schema_people_article(), + CibouletteResourceTypeConfiguration::new( + CibouletteIdTypeSelector::new(CibouletteSelector::Multi(vec![ + CibouletteIdType::Uuid(arcstr::literal!("people_id")), + CibouletteIdType::Uuid(arcstr::literal!("article_id")), + ])), + gen_messy_json_schema_people_article(), + None, + ), ) .unwrap();