Skip to content

Commit

Permalink
chore: move insert_header to client
Browse files Browse the repository at this point in the history
  • Loading branch information
soedirgo committed May 15, 2021
1 parent c46f9bd commit 8d645ca
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 69 deletions.
23 changes: 7 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,54 +49,45 @@ let body = resp
.await?;
```

Simplified example using an authenticated API gateway and JWT authorization (like SupaBase).
Simplified example using a custom header (e.g. for API gateway authentication with Supabase).

```rust
use postgrest::Postgrest;

static HEADER_KEY: &str = "apikey";
let header_value: String = "ExampleAPIKeyValue".to_string(); // EXAMPLE ONLY!
let client = Postgrest::new("https://your.supabase.endpoint/rest/v1/")
.insert_header("apikey", "ExampleAPIKeyValue"); // EXAMPLE ONLY!
// Don't actually hard code this value, that's really bad. Use environment
// variables like with the dotenv(https://crates.io/crates/dotenv) crate to inject

let client = Postgrest::new("https://your.supabase.endpoint/rest/v1/");
let resp = client
.from("your_table")
.auth(header_value)
.insert_header(HEADER_KEY, header_value)
.select("*")
.execute()
.await?;
let body = resp
.text()
.await?;

```

**Secure** example with authenticated API gateway and JWT authorization using the dotenv crate to correctly retrieve sensitive values.
**Secure** example with authenticated API gateway using the dotenv crate to correctly retrieve sensitive values.

```rust
use postgrest::Postgrest;
use dotenv;

dotenv().ok();

static HEADER_KEY: &str = "apikey";

let client = Postgrest::new("https://your.supabase.endpoint/rest/v1/");
.insert_header(
"apikey",
env::var("SUPABASE_PUBLIC_API_KEY").unwrap())
let resp = client
.from("your_table")
.auth(env::var("supabase_public_api_key").unwrap().to_string()))
.insert_header(
HEADER_KEY,
env::var("supabase_public_api_key").unwrap().to_string())
.select("*")
.execute()
.await?;
let body = resp
.text()
.await?;

```

### Building Queries
Expand Down
62 changes: 13 additions & 49 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct Builder {
// TODO: Test Unicode support
impl Builder {
/// Creates a new `Builder` with the specified `schema`.
pub fn new<T>(url: T, schema: Option<String>) -> Self
pub fn new<T>(url: T, schema: Option<String>, headers: HeaderMap) -> Self
where
T: Into<String>,
{
Expand All @@ -26,7 +26,7 @@ impl Builder {
url: url.into(),
schema,
queries: Vec::new(),
headers: HeaderMap::new(),
headers,
body: None,
is_rpc: false,
};
Expand Down Expand Up @@ -59,31 +59,6 @@ impl Builder {
self
}

/// Add arbitrary headers to the request. For instance when you may want to connect
/// through an api gateway that needs an API key header in addition to the JWT.
///
/// # Example
///
/// ```
/// use postgrest::Postgrest;
///
/// static header_name: &'static str = "foo";
/// let header_value: String = "bar".to_string();
///
/// let client = Postgrest::new("https://your.postgrest.endpoint/rest/v1/");
/// client
/// .from("table")
/// .insert_header(header_name, header_value);
/// ```
pub fn insert_header(mut self, header_name: &'static str, header_value: String) -> Self {
self.headers.insert(
header_name,
HeaderValue::from_str(&header_value)
.expect("Couldn't convert supplied header value from String to str."),
);
self
}

/// Performs horizontal filtering with SELECT.
///
/// # Note
Expand Down Expand Up @@ -478,7 +453,7 @@ mod tests {

#[test]
fn only_accept_json() {
let builder = Builder::new(TABLE_URL, None);
let builder = Builder::new(TABLE_URL, None, HeaderMap::new());
assert_eq!(
builder.headers.get("Accept").unwrap(),
HeaderValue::from_static("application/json")
Expand All @@ -487,27 +462,16 @@ mod tests {

#[test]
fn auth_with_token() {
let builder = Builder::new(TABLE_URL, None).auth("$Up3rS3crET");
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).auth("$Up3rS3crET");
assert_eq!(
builder.headers.get("Authorization").unwrap(),
HeaderValue::from_static("Bearer $Up3rS3crET")
);
}

#[test]
fn with_insert_header() {
static A_HEADER_KEY: &str = "foo";
let a_header_value: String = "bar".to_string();
let builder = Builder::new(TABLE_URL, None).insert_header(A_HEADER_KEY, a_header_value);
assert_eq!(
builder.headers.get("foo").unwrap(),
HeaderValue::from_static("bar")
);
}

#[test]
fn select_assert_query() {
let builder = Builder::new(TABLE_URL, None).select("some_table");
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).select("some_table");
assert_eq!(builder.method, Method::GET);
assert_eq!(
builder
Expand All @@ -519,7 +483,7 @@ mod tests {

#[test]
fn order_assert_query() {
let builder = Builder::new(TABLE_URL, None).order("id");
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).order("id");
assert_eq!(
builder
.queries
Expand All @@ -530,7 +494,7 @@ mod tests {

#[test]
fn limit_assert_range_header() {
let builder = Builder::new(TABLE_URL, None).limit(20);
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).limit(20);
assert_eq!(
builder.headers.get("Range").unwrap(),
HeaderValue::from_static("0-19")
Expand All @@ -539,7 +503,7 @@ mod tests {

#[test]
fn range_assert_range_header() {
let builder = Builder::new(TABLE_URL, None).range(10, 20);
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).range(10, 20);
assert_eq!(
builder.headers.get("Range").unwrap(),
HeaderValue::from_static("10-20")
Expand All @@ -548,7 +512,7 @@ mod tests {

#[test]
fn single_assert_accept_header() {
let builder = Builder::new(TABLE_URL, None).single();
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).single();
assert_eq!(
builder.headers.get("Accept").unwrap(),
HeaderValue::from_static("application/vnd.pgrst.object+json")
Expand All @@ -557,7 +521,7 @@ mod tests {

#[test]
fn upsert_assert_prefer_header() {
let builder = Builder::new(TABLE_URL, None).upsert("ignored");
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).upsert("ignored");
assert_eq!(
builder.headers.get("Prefer").unwrap(),
HeaderValue::from_static("return=representation,resolution=merge-duplicates")
Expand All @@ -566,20 +530,20 @@ mod tests {

#[test]
fn not_rpc_should_not_have_flag() {
let builder = Builder::new(TABLE_URL, None).select("ignored");
let builder = Builder::new(TABLE_URL, None, HeaderMap::new()).select("ignored");
assert_eq!(builder.is_rpc, false);
}

#[test]
fn rpc_should_have_body_and_flag() {
let builder = Builder::new(RPC_URL, None).rpc("{\"a\": 1, \"b\": 2}");
let builder = Builder::new(RPC_URL, None, HeaderMap::new()).rpc("{\"a\": 1, \"b\": 2}");
assert_eq!(builder.body.unwrap(), "{\"a\": 1, \"b\": 2}");
assert_eq!(builder.is_rpc, true);
}

#[test]
fn chain_filters() -> Result<(), Box<dyn std::error::Error>> {
let builder = Builder::new(TABLE_URL, None)
let builder = Builder::new(TABLE_URL, None, HeaderMap::new())
.eq("username", "supabot")
.neq("message", "hello world")
.gte("channel_id", "1")
Expand Down
45 changes: 41 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@
//! [postgrest]: https://postgrest.org
//! [readme]: https://github.com/supabase/postgrest-rs
extern crate reqwest;

mod builder;
mod filter;

pub use builder::Builder;
use reqwest::header::{HeaderMap, HeaderValue, IntoHeaderName};

pub struct Postgrest {
url: String,
schema: Option<String>,
headers: HeaderMap,
}

impl Postgrest {
Expand All @@ -103,6 +103,7 @@ impl Postgrest {
Postgrest {
url: url.into(),
schema: None,
headers: HeaderMap::new(),
}
}

Expand All @@ -128,6 +129,30 @@ impl Postgrest {
self
}

/// Add arbitrary headers to the request. For instance when you may want to connect
/// through an API gateway that needs an API key header.
///
/// # Example
///
/// ```
/// use postgrest::Postgrest;
///
/// let client = Postgrest::new("https://your.postgrest.endpoint")
/// .insert_header("apikey", "super.secret.key")
/// .from("table");
/// ```
pub fn insert_header(
mut self,
header_name: impl IntoHeaderName,
header_value: impl AsRef<str>,
) -> Self {
self.headers.insert(
header_name,
HeaderValue::from_str(header_value.as_ref()).expect("Invalid header value."),
);
self
}

/// Perform a table operation.
///
/// # Example
Expand All @@ -143,7 +168,7 @@ impl Postgrest {
T: AsRef<str>,
{
let url = format!("{}/{}", self.url, table.as_ref());
Builder::new(url, self.schema.clone())
Builder::new(url, self.schema.clone(), self.headers.clone())
}

/// Perform a stored procedure call.
Expand All @@ -162,7 +187,7 @@ impl Postgrest {
U: Into<String>,
{
let url = format!("{}/rpc/{}", self.url, function.as_ref());
Builder::new(url, self.schema.clone()).rpc(params)
Builder::new(url, self.schema.clone(), self.headers.clone()).rpc(params)
}
}

Expand All @@ -184,4 +209,16 @@ mod tests {
Some("private".to_string())
);
}

#[test]
fn with_insert_header() {
assert_eq!(
Postgrest::new(REST_URL)
.insert_header("apikey", "super.secret.key")
.headers
.get("apikey")
.unwrap(),
"super.secret.key"
);
}
}

0 comments on commit 8d645ca

Please sign in to comment.