From 6f42eda4b69c3a0394dee94bfa8c5fecdd6c077c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Roll=C3=A9n?= <38324289+SebRollen@users.noreply.github.com> Date: Tue, 11 Jan 2022 10:10:16 -0500 Subject: [PATCH] refactor!: remove multirequest methods (#20) Multirequest methods like `send_all` and `send_all_paginated` probably make more sense as a user-implementation as there are conflicting decisions in what the return type should be (single stream of results, iterator of streams, etc). Single request queries are more unambiguous, so we remove the multirequest feature and focus instead on great single-request ergonomics, leaving the decision in how to send multiple requests to the user. --- examples/multiple_pagination.rs | 102 -------------------------- src/client.rs | 28 ------- src/pagination.rs | 25 +++++++ src/request.rs | 1 + tests/integration/main.rs | 1 - tests/integration/multiple_queries.rs | 54 -------------- 6 files changed, 26 insertions(+), 185 deletions(-) delete mode 100644 examples/multiple_pagination.rs delete mode 100644 tests/integration/multiple_queries.rs diff --git a/examples/multiple_pagination.rs b/examples/multiple_pagination.rs deleted file mode 100644 index 623df2e..0000000 --- a/examples/multiple_pagination.rs +++ /dev/null @@ -1,102 +0,0 @@ -use futures::StreamExt; -use serde::{Deserialize, Serialize}; -use std::borrow::Cow; -use std::collections::HashMap; -use stream_flatten_iters::TryStreamExt; -use vila::pagination::{ - query::{QueryModifier, QueryPaginator}, - PaginatedRequest, -}; -use vila::{Client, Request, RequestData}; - -#[derive(Clone)] -struct Data { - page: usize, -} - -impl From for QueryModifier { - fn from(s: Data) -> QueryModifier { - let mut data = HashMap::new(); - data.insert("page".into(), s.page.to_string()); - QueryModifier { data } - } -} - -#[derive(Clone, Serialize)] -struct GetPassengers { - size: usize, - page: Option, -} - -#[derive(Deserialize, Debug)] -struct Passenger { - name: Option, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -struct PassengersWrapper { - total_passengers: usize, - total_pages: usize, - data: Vec, -} - -impl Request for GetPassengers { - type Data = Self; - type Response = PassengersWrapper; - - fn endpoint(&self) -> Cow { - "/v1/passenger".into() - } - - fn data(&self) -> RequestData<&Self> { - RequestData::Query(self) - } -} - -impl PaginatedRequest for GetPassengers { - type Data = Data; - type Paginator = QueryPaginator; - - fn initial_page(&self) -> Option { - self.page.map(|page| Data { page }) - } - - fn paginator(&self) -> Self::Paginator { - QueryPaginator::new(|prev: Option<&Data>, res: &PassengersWrapper| { - let max_page = res.total_pages; - match prev { - None => Some(Data { page: 1 }), - Some(x) => { - if x.page == max_page { - None - } else { - Some(Data { page: x.page + 1 }) - } - } - } - }) - } -} - -#[tokio::main] -pub async fn main() { - env_logger::init(); - let client = Client::new("https://api.instantwebtools.net"); - let req = GetPassengers { - page: None, - size: 1, - }; - - let reqs = [req.clone(), req]; - - // Can send paginated request, returning stream of results - futures::stream::select_all(client.send_all_paginated(&reqs).map(|streams| { - streams - .map(|maybe_wrapper| maybe_wrapper.map(|wrapper| wrapper.data)) - .try_flatten_iters() - .take(5) - })) - .for_each_concurrent(None, |x| async move { println!("{:?}", x) }) - .await; -} diff --git a/src/client.rs b/src/client.rs index f147131..fd3c6bb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -142,22 +142,6 @@ impl Client { self.send_raw(req).await } - /// Send multiple `Request`s, returing a stream of results - pub fn send_all<'a, I, R>( - &'a self, - requests: I, - ) -> impl Stream> + Unpin + 'a - where - I: IntoIterator + 'a, - R: Request + 'a, - { - Box::pin( - stream::iter(requests.into_iter()) - .map(move |r| self.send(r).map_into()) - .filter_map(|x| x), - ) - } - /// Send a paginated request, returning a stream of results pub fn send_paginated<'a, R: PaginatedRequest>( &'a self, @@ -221,16 +205,4 @@ impl Client { }, )) } - - /// Send multiple paginated requests, returning a stream of results - pub fn send_all_paginated<'a, I, R>( - &'a self, - requests: I, - ) -> impl Iterator> + 'a> - where - I: IntoIterator + 'a, - R: PaginatedRequest + 'a, - { - requests.into_iter().map(move |r| self.send_paginated(r)) - } } diff --git a/src/pagination.rs b/src/pagination.rs index 6c929d3..92aeb3a 100644 --- a/src/pagination.rs +++ b/src/pagination.rs @@ -190,3 +190,28 @@ pub mod path { } } } + +//enum PaginationFn { +// NoResult(Box) -> Option>), +// ResultNeeded(Box, &T) -> Option>), +//} +// +//impl<'u, T, U: 'u> From) -> Option> for PaginationFn { +// fn from(x: fn(Option<&'u U>) -> Option) -> PaginationFn { +// PaginationFn::NoResult(Box::new(x)) +// } +//} +// +//impl From, &T) -> Option> for PaginationFn { +// fn from(x: fn(Option<&U>, &T) -> Option) -> PaginationFn { +// PaginationFn::ResultNeeded(Box::new(x)) +// } +//} +// +//fn test(x: Option<&i32>) -> Option { +// x.cloned() +//} +// +//fn new_pagination() -> PaginationFn { +// test.into() +//} diff --git a/src/request.rs b/src/request.rs index 2ff6f88..5f01916 100644 --- a/src/request.rs +++ b/src/request.rs @@ -27,6 +27,7 @@ pub trait Request { type Data: Serialize; /// The type of the response from the server. type Response: for<'de> Deserialize<'de> + Unpin; + /// The HTTP method for the request. const METHOD: Method = Method::GET; diff --git a/tests/integration/main.rs b/tests/integration/main.rs index 85da744..03ac201 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -2,7 +2,6 @@ mod authorization; mod data; mod empty_response; mod errors; -mod multiple_queries; mod pagination; mod post; mod utils; diff --git a/tests/integration/multiple_queries.rs b/tests/integration/multiple_queries.rs deleted file mode 100644 index b5e0e26..0000000 --- a/tests/integration/multiple_queries.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::utils::{NameGreeting, QueryHello}; -use futures::StreamExt; -use vila::Client; -use wiremock::matchers::{method, path}; -use wiremock::{Mock, MockServer, Request as MockRequest, ResponseTemplate}; - -#[tokio::test] -async fn query_multiple() { - let _ = env_logger::try_init(); - let server = MockServer::start().await; - let uri = server.uri(); - let client = Client::new(&uri); - - Mock::given(method("GET")) - .and(path("/hello")) - .respond_with(|req: &MockRequest| { - let name = req - .url - .query_pairs() - .find(|(k, _)| k == "name") - .map(|(_, v)| v) - .unwrap(); - let body = NameGreeting { - message: format!("Hello, {}!", name), - }; - ResponseTemplate::new(200).set_body_json(body) - }) - .mount(&server) - .await; - - let reqs = &[ - QueryHello { - name: "world".into(), - }, - QueryHello { - name: "again".into(), - }, - ]; - - let mut response = client.send_all(reqs); - assert_eq!( - response.next().await.unwrap().unwrap(), - NameGreeting { - message: "Hello, world!".into(), - } - ); - assert_eq!( - response.next().await.unwrap().unwrap(), - NameGreeting { - message: "Hello, again!".into(), - } - ); - assert!(response.next().await.is_none()); -}