Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Astrazione del body della request #9

Merged
merged 22 commits into from
Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ tokio-test = { version = "0.2", optional = true }
opentelemetry = { version = "0.8.0", optional = true }
tracing = { version = "0.1.19", optional = true }
tracing-opentelemetry = { version = "0.7.0", optional = true }
async-trait = "0.1.41"

[dev-dependencies]
mockito = "0.25.2"
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@
# bridge.rs
Prima bridge pattern implementation for rust

### Example


This is a WORK IN PROGRESS
```rust
use prima_bridge::prelude::*;
use serde::Deserialize;

#[derive(Deserialize)]
struct DeserializableData {
test: String
}

// using we make sure that `Bridge` get instantiated only once
fn bridge() -> &'static Bridge {
static BRIDGE: OnceCell<Bridge> = OnceCell::new();
BRIDGE.get_or_init(|| Bridge::new("https://prima.it/api"))
}

// Do not use expect in production! It will cause runtime errors. Use Result.
pub fn fetch_data() -> YourResult<DeserializableData> {
Request::get(bridge())
.send()?
.get_data(&["nested", "selector"])? // response is {"nested": {"selector": {"data": "test"}}}
}
```

To understand this example you should know:
- [once_cell](https://crates.io/crates/once_cell) library providing the cell type
- Rust error handling to use ? and convert it to a custom error type. See for example [thiserror](https://crates.io/crates/thiserror)
7 changes: 7 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use reqwest::{StatusCode, Url};
use serde_json::Value;
use std::convert::Infallible;
use thiserror::Error;

pub type PrimaBridgeResult<T> = Result<T, PrimaBridgeError>;
Expand All @@ -22,3 +23,9 @@ pub enum PrimaBridgeError {
#[error("empty body")]
EmptyBody,
}

impl From<Infallible> for PrimaBridgeError {
fn from(_: Infallible) -> Self {
unimplemented!()
}
}
15 changes: 9 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ mod errors;
pub mod prelude;
mod request;
mod response;
mod v2;

pub use self::{
request::{Request, RequestType},
response::Response,
};
pub use self::{response::Response, v2::GraphQLRequest, v2::Request};
use crate::request::{Request as OldRequest, RequestType};
use reqwest::Url;
use serde::Serialize;

Expand All @@ -49,7 +48,11 @@ impl Bridge {
}
}

pub fn request<S: Serialize>(&self, request_type: RequestType<S>) -> Request<S> {
Request::new(&self, request_type)
#[deprecated(
since = "0.2.0",
note = "Use the RestRequest and GraphQLRequest ::new constructor instead of this function"
)]
pub fn request<S: Serialize>(&self, request_type: RequestType<S>) -> OldRequest<S> {
OldRequest::new(&self, request_type)
}
}
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub use super::errors::*;
pub use super::{
request::{GraphQL, RequestType, Rest},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Così però chi usa la versione precedente importando dal prelude si rompe...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vero! Ma siamo in versione 0.*, così forziamo l'update...

response::Response,
v2::{DeliverableRequest, GraphQLRequest, Request, RestRequest},
Bridge,
};
22 changes: 21 additions & 1 deletion src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ impl<'a, S: Serialize> Request<'a, S> {
let mut additional_headers = vec![];
additional_headers.append(&mut self.custom_headers().to_vec());
additional_headers.append(&mut self.tracing_headers().to_vec());
dbg!(&additional_headers);
let request_builder = additional_headers
.iter()
.fold(request_builder, |request, (name, value)| {
Expand Down Expand Up @@ -304,6 +303,27 @@ pub struct GraphQLBody<T> {
variables: Option<T>,
}

impl<T: Serialize> From<(&str, Option<T>)> for GraphQLBody<T> {
matteosister marked this conversation as resolved.
Show resolved Hide resolved
fn from((query, variables): (&str, Option<T>)) -> Self {
Self {
query: query.to_owned(),
variables,
}
}
}

impl<T: Serialize> From<(String, Option<T>)> for GraphQLBody<T> {
fn from((query, variables): (String, Option<T>)) -> Self {
(query.as_str(), variables).into()
matteosister marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl<T: Serialize> From<(String, T)> for GraphQLBody<T> {
fn from((query, variables): (String, T)) -> Self {
(query.as_str(), Some(variables)).into()
matteosister marked this conversation as resolved.
Show resolved Hide resolved
}
}

#[derive(Debug)]
pub struct Rest<S: Serialize> {
request_id: Uuid,
Expand Down
7 changes: 7 additions & 0 deletions src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Response {
}

impl Response {
#[doc(hidden)]
pub fn rest(
url: Url,
response_body: String,
Expand All @@ -42,6 +43,7 @@ impl Response {
}
}

#[doc(hidden)]
pub fn graphql(
url: Url,
response_body: String,
Expand All @@ -59,13 +61,17 @@ impl Response {
}
}

#[doc(hidden)]
fn is_graphql(&self) -> bool {
self.request_type == RequestType::GraphQL
}

/// Returns an `HeaderMap` of response headers.
pub fn headers(&self) -> &HeaderMap {
&self.response_headers
}

/// Returns data from the function.
pub fn get_data<T>(self, response_extractor: &[&str]) -> PrimaBridgeResult<T>
where
for<'de> T: Deserialize<'de> + Debug,
Expand All @@ -83,6 +89,7 @@ impl Response {
Ok(extract_inner_json(self.url, selectors, json_value)?)
}

/// returns `true` if the response is successful
pub fn is_ok(&self) -> bool {
self.status_code.is_success()
}
Expand Down
38 changes: 38 additions & 0 deletions src/v2/body.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#[derive(Clone, Debug)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perché il body di una request dovrebbe essere clonabile? forse dovrebbe contenere solo un riferimento all'array di bytes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perchè credo che reqwest sotto si prenda l'ownership del body, quindi non posso passarlo per referenza....

Copy link
Contributor

@Guara92 Guara92 Oct 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prenderei in considerazione di consumare la DeliverableRequest implementando

impl<T: DeliverableRequest> From<T> for RequestBuilder 

dove consumi la "nostra" request e ottieni un builder di reqwest in un colpo solo evitando diversi clone

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interessante....da esplorare...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

è un po' un casino perchè nel trait ora ci sono le due implementazioni di send per le due feature async e blocking. Il RequestBuilder è diverso a sua volta in base ad async e blocking...quindi non hanno un interfaccia comune

pub struct Body {
inner: Vec<u8>,
}

impl Default for Body {
fn default() -> Self {
Self { inner: vec![] }
}
}

impl From<String> for Body {
fn from(val: String) -> Self {
Self {
inner: val.into_bytes(),
}
}
}

impl From<&str> for Body {
fn from(val: &str) -> Self {
Self {
inner: val.to_string().into_bytes(),
}
}
}

impl From<Vec<u8>> for Body {
fn from(value: Vec<u8>) -> Self {
Self { inner: value }
}
}

impl From<Body> for Vec<u8> {
fn from(body: Body) -> Self {
body.inner
}
}
Loading