api: add filtering by keys

This commit is contained in:
Dmitriy Pleshevskiy 2022-05-12 00:16:25 +03:00
parent 8c367615a1
commit e2ae40b685
3 changed files with 49 additions and 6 deletions

View File

@ -1,13 +1,14 @@
use super::types; use super::types;
use crate::repo::ingredient::{GetIngredientOpts, IngredientRepo}; use crate::repo::ingredient::IngredientRepo;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct RequestOpts { pub struct RequestOpts {
pub lang: Option<types::Lang>, pub lang: Option<types::Lang>,
pub keys: Option<Vec<String>>,
} }
pub fn execute(repo: &impl IngredientRepo, opts: RequestOpts) -> Vec<types::Ingredient> { pub fn execute(repo: &impl IngredientRepo, opts: RequestOpts) -> Vec<types::Ingredient> {
repo.get_ingredients(GetIngredientOpts { lang: opts.lang }) repo.get_ingredients(opts.into())
} }
#[cfg(test)] #[cfg(test)]
@ -66,4 +67,25 @@ mod tests {
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
#[test]
fn should_return_filtered_by_keys() {
let repo = crate::repo::ingredient::InMemoryIngredientRepo::new();
let res = execute(
&repo,
RequestOpts {
keys: Some(vec![String::from("apple")]),
..RequestOpts::default()
},
);
match res.as_slice() {
[apple] => {
assert_eq!(apple.key, String::from("apple"));
assert_eq!(apple.lang, Lang::Rus);
assert_eq!(apple.name, String::from("Яблоко"));
}
_ => unimplemented!(),
}
}
} }

View File

@ -1,8 +1,18 @@
use crate::domain::ingredient::types; use crate::domain::ingredient::{fetch_list, types};
#[derive(Default)] #[derive(Default)]
pub struct GetIngredientOpts { pub struct GetIngredientOpts {
pub lang: Option<types::Lang>, pub lang: Option<types::Lang>,
pub keys: Option<Vec<String>>,
}
impl From<fetch_list::RequestOpts> for GetIngredientOpts {
fn from(app: fetch_list::RequestOpts) -> Self {
Self {
lang: app.lang,
keys: app.keys,
}
}
} }
pub trait IngredientRepo { pub trait IngredientRepo {
@ -17,7 +27,8 @@ impl IngredientRepo for StaticIngredientRepo {
db::INGREDIENTS db::INGREDIENTS
.iter() .iter()
.zip(langs) .zip(langs)
.filter_map(|tup| TryFrom::try_from(tup).ok()) .filter_map(|tup| types::Ingredient::try_from(tup).ok())
.filter(|ing| opts.keys.is_none() || opts.keys.as_ref().unwrap().contains(&ing.key))
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
} }
@ -72,7 +83,8 @@ impl IngredientRepo for InMemoryIngredientRepo {
self.ingredients self.ingredients
.iter() .iter()
.zip(langs) .zip(langs)
.filter_map(|tup| TryFrom::try_from(tup).ok()) .filter_map(|tup| types::Ingredient::try_from(tup).ok())
.filter(|ing| opts.keys.is_none() || opts.keys.as_ref().unwrap().contains(&ing.key))
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
} }

View File

@ -47,6 +47,7 @@ pub fn start() {
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct FetchIngredientsOpts<'a> { struct FetchIngredientsOpts<'a> {
lang: Option<&'a str>, lang: Option<&'a str>,
keys: Option<Vec<&'a str>>,
} }
impl<'a> From<QueryParams<'a>> for FetchIngredientsOpts<'a> { impl<'a> From<QueryParams<'a>> for FetchIngredientsOpts<'a> {
@ -56,6 +57,11 @@ impl<'a> From<QueryParams<'a>> for FetchIngredientsOpts<'a> {
.fold(FetchIngredientsOpts::default(), |mut opts, p| { .fold(FetchIngredientsOpts::default(), |mut opts, p| {
match p { match p {
("lang", val) => opts.lang = Some(val), ("lang", val) => opts.lang = Some(val),
("key", val) => {
let mut keys = opts.keys.unwrap_or_default();
keys.push(val);
opts.keys = Some(keys)
}
_ => {} _ => {}
}; };
@ -67,7 +73,10 @@ impl<'a> From<QueryParams<'a>> for FetchIngredientsOpts<'a> {
impl From<FetchIngredientsOpts<'_>> for domain::ingredient::fetch_list::RequestOpts { impl From<FetchIngredientsOpts<'_>> for domain::ingredient::fetch_list::RequestOpts {
fn from(rest: FetchIngredientsOpts) -> Self { fn from(rest: FetchIngredientsOpts) -> Self {
let lang = rest.lang.and_then(|l| Lang::from_str(l).ok()); let lang = rest.lang.and_then(|l| Lang::from_str(l).ok());
Self { lang } let keys = rest
.keys
.map(|keys| keys.into_iter().map(String::from).collect());
Self { lang, keys }
} }
} }