Compare commits
No commits in common. "50ccd6b339379b3ddfd298e5d3147cbeba25cf5d" and "2760c7c24fcd0ee4a3667bc1b8a63c8e8040b08d" have entirely different histories.
50ccd6b339
...
2760c7c24f
2 changed files with 205 additions and 221 deletions
10
Cargo.toml
10
Cargo.toml
|
|
@ -5,18 +5,20 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[features]
|
[features]
|
||||||
request = ["surf/h1-client-rustls"]
|
request = []
|
||||||
convert_from_notion = []
|
convert_from_notion = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-std = "1.12.0"
|
async-trait = "0.1.68"
|
||||||
# async-trait = "0.1.68"
|
|
||||||
base64 = "0.21.0"
|
base64 = "0.21.0"
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
futures-core = "0.3.28"
|
futures-core = "0.3.28"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
regex = "1.7.1"
|
regex = "1.7.1"
|
||||||
|
reqwest = { version = "0.11.14", features = ["json"] }
|
||||||
serde = { version = "^1.0", features = ["derive"], default-features = false }
|
serde = { version = "^1.0", features = ["derive"], default-features = false }
|
||||||
serde_json = { version = "^1.0", features = ["raw_value"], default-features = false }
|
serde_json = { version = "^1.0", features = ["raw_value"], default-features = false }
|
||||||
surf = { version = "2.3.2", default-features = false }
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1.28.1", features = ["macros"] }
|
||||||
|
|
|
||||||
414
src/lib.rs
414
src/lib.rs
|
|
@ -1,13 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::{DateTime, NaiveTime, Utc};
|
use chrono::{DateTime, NaiveTime, Utc};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
#[cfg(feature = "request")]
|
||||||
|
use reqwest::header::{HeaderMap, HeaderValue};
|
||||||
use serde::de::Error as SerdeError;
|
use serde::de::Error as SerdeError;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use surf::http::StatusCode;
|
|
||||||
|
|
||||||
use futures_core::future::BoxFuture;
|
use futures_core::future::BoxFuture;
|
||||||
|
|
||||||
|
|
@ -20,16 +22,18 @@ lazy_static! {
|
||||||
const NOTION_VERSION: &str = "2022-06-28";
|
const NOTION_VERSION: &str = "2022-06-28";
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
pub type Callback = dyn Fn(surf::RequestBuilder) -> BoxFuture<'static, std::result::Result<surf::Response, surf::Error>>
|
pub type Callback = dyn Fn(
|
||||||
|
&mut reqwest::RequestBuilder,
|
||||||
|
) -> BoxFuture<'_, std::result::Result<reqwest::Response, reqwest::Error>>
|
||||||
+ 'static
|
+ 'static
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync;
|
+ Sync;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Http(StatusCode, surf::Response),
|
Http(reqwest::Error, Option<Value>),
|
||||||
Surf(surf::Error),
|
|
||||||
Deserialization(serde_json::Error, Option<Value>),
|
Deserialization(serde_json::Error, Option<Value>),
|
||||||
|
Header(reqwest::header::InvalidHeaderValue),
|
||||||
ChronoParse(chrono::ParseError),
|
ChronoParse(chrono::ParseError),
|
||||||
UnexpectedType,
|
UnexpectedType,
|
||||||
}
|
}
|
||||||
|
|
@ -40,9 +44,15 @@ impl std::fmt::Display for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<surf::Error> for Error {
|
impl From<reqwest::Error> for Error {
|
||||||
fn from(error: surf::Error) -> Self {
|
fn from(error: reqwest::Error) -> Self {
|
||||||
Error::Surf(error)
|
Error::Http(error, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::header::InvalidHeaderValue> for Error {
|
||||||
|
fn from(error: reqwest::header::InvalidHeaderValue) -> Self {
|
||||||
|
Error::Header(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,31 +68,37 @@ impl From<chrono::ParseError> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fn try_to_parse_response<T: std::fmt::Debug + for<'de> serde::Deserialize<'de>>(
|
async fn try_to_parse_response<T: std::fmt::Debug + for<'de> serde::Deserialize<'de>>(
|
||||||
// mut response: surf::Response,
|
response: reqwest::Response,
|
||||||
// ) -> Result<T> {
|
) -> Result<T> {
|
||||||
// let text = response.body_string().await?;
|
let text = response.text().await?;
|
||||||
|
|
||||||
// match serde_json::from_str::<T>(&text) {
|
match serde_json::from_str::<T>(&text) {
|
||||||
// Ok(value) => Ok(value),
|
Ok(value) => Ok(value),
|
||||||
// Err(error) => match serde_json::from_str::<Value>(&text) {
|
Err(error) => match serde_json::from_str::<Value>(&text) {
|
||||||
// Ok(body) => Err(Error::Deserialization(error, Some(body))),
|
Ok(body) => Err(Error::Deserialization(error, Some(body))),
|
||||||
// _ => Err(Error::Deserialization(error, Some(Value::String(text)))),
|
_ => Err(Error::Deserialization(error, Some(Value::String(text)))),
|
||||||
// },
|
},
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
#[cfg(feature = "request")]
|
#[cfg(feature = "request")]
|
||||||
fn get_http_client(notion_api_key: &str) -> surf::Client {
|
fn get_http_client(notion_api_key: &str) -> reqwest::Client {
|
||||||
log::trace!("Readying HTTP Client");
|
let mut headers = HeaderMap::new();
|
||||||
surf::Config::new()
|
headers.insert(
|
||||||
.add_header("Authorization", format!("Bearer {notion_api_key}"))
|
"Authorization",
|
||||||
.expect("to add Authorization header")
|
HeaderValue::from_str(&format!("Bearer {notion_api_key}"))
|
||||||
.add_header("Notion-Version", NOTION_VERSION)
|
.expect("bearer token to be parsed into a header"),
|
||||||
.expect("to add Notion-Version header")
|
);
|
||||||
.add_header("Content-Type", "application/json")
|
headers.insert(
|
||||||
.expect("to add Content-Type header")
|
"Notion-Version",
|
||||||
.try_into()
|
HeaderValue::from_str(NOTION_VERSION).expect("notion version to be parsed into a header"),
|
||||||
|
);
|
||||||
|
headers.insert("Content-Type", HeaderValue::from_static("application/json"));
|
||||||
|
|
||||||
|
reqwest::ClientBuilder::new()
|
||||||
|
.default_headers(headers)
|
||||||
|
.build()
|
||||||
.expect("to build a valid client out of notion_api_key")
|
.expect("to build a valid client out of notion_api_key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +119,7 @@ pub struct SearchOptions<'a> {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClientBuilder {
|
pub struct ClientBuilder {
|
||||||
api_key: Option<String>,
|
api_key: Option<String>,
|
||||||
custom_request: Option<Box<Callback>>,
|
custom_request: Option<Arc<Callback>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientBuilder {
|
impl ClientBuilder {
|
||||||
|
|
@ -116,13 +132,13 @@ impl ClientBuilder {
|
||||||
pub fn custom_request<F>(mut self, callback: F) -> Self
|
pub fn custom_request<F>(mut self, callback: F) -> Self
|
||||||
where
|
where
|
||||||
for<'c> F: Fn(
|
for<'c> F: Fn(
|
||||||
surf::RequestBuilder,
|
&'c mut reqwest::RequestBuilder,
|
||||||
) -> BoxFuture<'static, std::result::Result<surf::Response, surf::Error>>
|
) -> BoxFuture<'c, std::result::Result<reqwest::Response, reqwest::Error>>
|
||||||
+ 'static
|
+ 'static
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync,
|
+ Sync,
|
||||||
{
|
{
|
||||||
self.custom_request = Some(Box::new(callback));
|
self.custom_request = Some(Arc::new(callback));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -131,72 +147,77 @@ impl ClientBuilder {
|
||||||
pub fn build(self) -> Client {
|
pub fn build(self) -> Client {
|
||||||
let notion_api_key = self.api_key.expect("api_key to be set");
|
let notion_api_key = self.api_key.expect("api_key to be set");
|
||||||
|
|
||||||
|
let request_handler = self.custom_request.unwrap_or(Arc::new(
|
||||||
|
|request_builder: &mut reqwest::RequestBuilder| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let request = request_builder
|
||||||
|
.try_clone()
|
||||||
|
.expect("non-stream body request clone to succeed");
|
||||||
|
|
||||||
|
request.send().await
|
||||||
|
})
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
let http_client = Arc::from(get_http_client(¬ion_api_key));
|
||||||
|
|
||||||
Client {
|
Client {
|
||||||
http_client: get_http_client(¬ion_api_key),
|
http_client: http_client.clone(),
|
||||||
request_handler: self.custom_request.unwrap_or(Box::new(
|
request_handler: request_handler.clone(),
|
||||||
|request_builder: surf::RequestBuilder| Box::pin(request_builder),
|
|
||||||
)),
|
pages: Pages {
|
||||||
|
http_client: http_client.clone(),
|
||||||
|
request_handler: request_handler.clone(),
|
||||||
|
},
|
||||||
|
blocks: Blocks {
|
||||||
|
http_client: http_client.clone(),
|
||||||
|
request_handler: request_handler.clone(),
|
||||||
|
},
|
||||||
|
databases: Databases {
|
||||||
|
http_client: http_client.clone(),
|
||||||
|
request_handler: request_handler.clone(),
|
||||||
|
},
|
||||||
|
users: Users {
|
||||||
|
http_client: http_client.clone(),
|
||||||
|
request_handler: request_handler.clone(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
http_client: surf::Client,
|
http_client: Arc<reqwest::Client>,
|
||||||
request_handler: Box<Callback>,
|
request_handler: Arc<Callback>,
|
||||||
|
|
||||||
|
pub pages: Pages,
|
||||||
|
pub blocks: Blocks,
|
||||||
|
pub databases: Databases,
|
||||||
|
pub users: Users,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Client {
|
impl<'a> Client {
|
||||||
#[allow(clippy::new_ret_no_self)]
|
|
||||||
pub fn new() -> ClientBuilder {
|
pub fn new() -> ClientBuilder {
|
||||||
ClientBuilder::default()
|
ClientBuilder::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search<'b, T: std::fmt::Debug + for<'de> serde::Deserialize<'de>>(
|
pub async fn search<'b, T: std::fmt::Debug + for<'de> serde::Deserialize<'de>>(
|
||||||
&self,
|
self,
|
||||||
options: SearchOptions<'b>,
|
options: SearchOptions<'b>,
|
||||||
) -> Result<QueryResponse<T>> {
|
) -> Result<QueryResponse<T>> {
|
||||||
let request = self
|
let mut request = self
|
||||||
.http_client
|
.http_client
|
||||||
.post("https://api.notion.com/v1/search")
|
.post("https://api.notion.com/v1/search")
|
||||||
.body_json(&options)
|
.json(&options);
|
||||||
.expect("to parse JSON for doing `search`");
|
|
||||||
|
|
||||||
let mut response = (self.request_handler)(request)
|
let response = (self.request_handler)(&mut request).await?;
|
||||||
.await
|
|
||||||
.expect("to request through a request handler");
|
|
||||||
|
|
||||||
match response.status() {
|
match response.error_for_status_ref() {
|
||||||
StatusCode::Ok => Ok(response.body_json().await?),
|
Ok(_) => Ok(response.json().await?),
|
||||||
status => Err(Error::Http(status, response)),
|
Err(error) => {
|
||||||
|
let body = response.json::<Value>().await?;
|
||||||
|
Err(Error::Http(error, Some(body)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pages(&'a self) -> Pages<'a> {
|
|
||||||
Pages {
|
|
||||||
http_client: &self.http_client,
|
|
||||||
request_handler: &self.request_handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blocks(&'a self) -> Blocks<'a> {
|
|
||||||
Blocks {
|
|
||||||
http_client: &self.http_client,
|
|
||||||
request_handler: &self.request_handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn databases(&'a self) -> Databases<'a> {
|
|
||||||
Databases {
|
|
||||||
http_client: &self.http_client,
|
|
||||||
request_handler: &self.request_handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn users(&'a self) -> Users<'a> {
|
|
||||||
Users {
|
|
||||||
http_client: &self.http_client,
|
|
||||||
request_handler: &self.request_handler,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,78 +225,88 @@ pub struct PageOptions<'a> {
|
||||||
pub page_id: &'a str,
|
pub page_id: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Pages<'a> {
|
#[derive(Clone)]
|
||||||
http_client: &'a surf::Client,
|
pub struct Pages {
|
||||||
request_handler: &'a Box<Callback>,
|
http_client: Arc<reqwest::Client>,
|
||||||
|
request_handler: Arc<Callback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pages<'_> {
|
impl Pages {
|
||||||
pub async fn retrieve(&self, options: PageOptions<'_>) -> Result<Page> {
|
pub async fn retrieve<'a>(self, options: PageOptions<'a>) -> Result<Page> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://api.notion.com/v1/pages/{page_id}",
|
"https://api.notion.com/v1/pages/{page_id}",
|
||||||
page_id = options.page_id
|
page_id = options.page_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = self.http_client.get(url);
|
let mut request = self.http_client.get(url);
|
||||||
let mut response = (self.request_handler)(request).await?;
|
|
||||||
|
|
||||||
match response.status() {
|
let response = (self.request_handler)(&mut request).await?;
|
||||||
StatusCode::Ok => Ok(response.body_json().await?),
|
|
||||||
status => Err(Error::Http(status, response)),
|
match response.error_for_status_ref() {
|
||||||
|
Ok(_) => Ok(response.json().await?),
|
||||||
|
Err(error) => {
|
||||||
|
let body = response.json::<Value>().await?;
|
||||||
|
Err(Error::Http(error, Some(body)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Blocks<'a> {
|
#[derive(Clone)]
|
||||||
http_client: &'a surf::Client,
|
pub struct Blocks {
|
||||||
request_handler: &'a Box<Callback>,
|
http_client: Arc<reqwest::Client>,
|
||||||
|
request_handler: Arc<Callback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blocks<'_> {
|
impl Blocks {
|
||||||
pub fn children(&self) -> BlockChildren {
|
pub fn children(&self) -> BlockChildren {
|
||||||
BlockChildren {
|
BlockChildren {
|
||||||
http_client: self.http_client,
|
http_client: self.http_client.clone(),
|
||||||
request_handler: self.request_handler,
|
request_handler: self.request_handler.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockChildren<'a> {
|
pub struct BlockChildren {
|
||||||
http_client: &'a surf::Client,
|
http_client: Arc<reqwest::Client>,
|
||||||
request_handler: &'a Box<Callback>,
|
request_handler: Arc<Callback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockChildrenListOptions<'a> {
|
pub struct BlockChildrenListOptions<'a> {
|
||||||
pub block_id: &'a str,
|
pub block_id: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockChildren<'_> {
|
impl BlockChildren {
|
||||||
pub async fn list(
|
pub async fn list<'a>(
|
||||||
&self,
|
self,
|
||||||
options: BlockChildrenListOptions<'_>,
|
options: BlockChildrenListOptions<'a>,
|
||||||
) -> Result<QueryResponse<Block>> {
|
) -> Result<QueryResponse<Block>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://api.notion.com/v1/blocks/{block_id}/children",
|
"https://api.notion.com/v1/blocks/{block_id}/children",
|
||||||
block_id = options.block_id
|
block_id = options.block_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = self.http_client.get(&url);
|
let mut request = self.http_client.get(&url);
|
||||||
|
|
||||||
let mut response = (self.request_handler)(request).await?;
|
let response = (self.request_handler)(&mut request).await?;
|
||||||
|
|
||||||
match response.status() {
|
match response.error_for_status_ref() {
|
||||||
StatusCode::Ok => Ok(response.body_json().await?),
|
Ok(_) => Ok(response.json().await?),
|
||||||
status => Err(Error::Http(status, response)),
|
Err(error) => {
|
||||||
|
let body = response.json::<Value>().await?;
|
||||||
|
Err(Error::Http(error, Some(body)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Databases<'a> {
|
#[derive(Clone)]
|
||||||
http_client: &'a surf::Client,
|
pub struct Databases {
|
||||||
request_handler: &'a Box<Callback>,
|
http_client: Arc<reqwest::Client>,
|
||||||
|
request_handler: Arc<Callback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Databases<'_> {
|
impl Databases {
|
||||||
pub async fn query<'a>(
|
pub async fn query<'a>(
|
||||||
&self,
|
&self,
|
||||||
options: DatabaseQueryOptions<'a>,
|
options: DatabaseQueryOptions<'a>,
|
||||||
|
|
@ -287,7 +318,11 @@ impl Databases<'_> {
|
||||||
|
|
||||||
let mut request = self.http_client.post(url);
|
let mut request = self.http_client.post(url);
|
||||||
|
|
||||||
let json = options.filter.map(|filter| json!({ "filter": filter }));
|
let json = if let Some(filter) = options.filter {
|
||||||
|
Some(json!({ "filter": filter }))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let json = if let Some(sorts) = options.sorts {
|
let json = if let Some(sorts) = options.sorts {
|
||||||
if let Some(mut json) = json {
|
if let Some(mut json) = json {
|
||||||
|
|
@ -317,19 +352,18 @@ impl Databases<'_> {
|
||||||
json
|
json
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref json) = json {
|
if let Some(json) = json {
|
||||||
request = request
|
request = request.json(&json);
|
||||||
.body_json(json)
|
|
||||||
.expect("to parse JSON for start_cursor");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("Querying database with request: {request:?} and body: {json:?}");
|
let response = (self.request_handler)(&mut request).await?;
|
||||||
|
|
||||||
let mut response = (self.request_handler)(request).await?;
|
match response.error_for_status_ref() {
|
||||||
|
Ok(_) => try_to_parse_response(response).await,
|
||||||
match response.status() {
|
Err(error) => {
|
||||||
StatusCode::Ok => Ok(response.body_json().await?),
|
let body = try_to_parse_response::<Value>(response).await?;
|
||||||
status => Err(Error::Http(status, response)),
|
Err(Error::Http(error, Some(body)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -338,9 +372,9 @@ impl Databases<'_> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[async_std::test]
|
#[tokio::test]
|
||||||
async fn check_database_query() {
|
async fn check_database_query() {
|
||||||
let _ = Client::new()
|
let databases = Client::new()
|
||||||
.api_key("secret_FuhJkAoOVZlk8YUT9ZOeYqWBRRZN6OMISJwhb4dTnud")
|
.api_key("secret_FuhJkAoOVZlk8YUT9ZOeYqWBRRZN6OMISJwhb4dTnud")
|
||||||
.build()
|
.build()
|
||||||
.search::<Database>(SearchOptions {
|
.search::<Database>(SearchOptions {
|
||||||
|
|
@ -356,19 +390,23 @@ mod tests {
|
||||||
start_cursor: None,
|
start_cursor: None,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
println!("{databases:#?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[tokio::test]
|
||||||
async fn test_blocks() {
|
async fn test_blocks() {
|
||||||
let _ = Client::new()
|
let blocks = Client::new()
|
||||||
.api_key("secret_FuhJkAoOVZlk8YUT9ZOeYqWBRRZN6OMISJwhb4dTnud")
|
.api_key("secret_FuhJkAoOVZlk8YUT9ZOeYqWBRRZN6OMISJwhb4dTnud")
|
||||||
.build()
|
.build()
|
||||||
.blocks()
|
.blocks
|
||||||
.children()
|
.children()
|
||||||
.list(BlockChildrenListOptions {
|
.list(BlockChildrenListOptions {
|
||||||
block_id: "0d253ab0f751443aafb9bcec14012897",
|
block_id: "0d253ab0f751443aafb9bcec14012897",
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
println!("{blocks:#?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -381,22 +419,26 @@ pub struct DatabaseQueryOptions<'a> {
|
||||||
pub start_cursor: Option<String>,
|
pub start_cursor: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Users<'a> {
|
#[derive(Clone)]
|
||||||
http_client: &'a surf::Client,
|
pub struct Users {
|
||||||
request_handler: &'a Box<Callback>,
|
http_client: Arc<reqwest::Client>,
|
||||||
|
request_handler: Arc<Callback>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Users<'_> {
|
impl Users {
|
||||||
pub async fn get(&self) -> Result<QueryResponse<User>> {
|
pub async fn get(&self) -> Result<QueryResponse<User>> {
|
||||||
let url = "https://api.notion.com/v1/users".to_owned();
|
let url = "https://api.notion.com/v1/users".to_owned();
|
||||||
|
|
||||||
let request = self.http_client.get(&url);
|
let mut request = self.http_client.get(&url);
|
||||||
|
|
||||||
let mut response = (self.request_handler)(request).await?;
|
let response = (self.request_handler)(&mut request).await?;
|
||||||
|
|
||||||
match response.status() {
|
match response.error_for_status_ref() {
|
||||||
StatusCode::Ok => Ok(response.body_json().await?),
|
Ok(_) => Ok(response.json().await?),
|
||||||
status => Err(Error::Http(status, response)),
|
Err(error) => {
|
||||||
|
let body = response.json::<Value>().await?;
|
||||||
|
Err(Error::Http(error, Some(body)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -685,9 +727,6 @@ pub enum CodeLanguage {
|
||||||
YAML,
|
YAML,
|
||||||
#[serde(rename = "java/c/c++/c#")]
|
#[serde(rename = "java/c/c++/c#")]
|
||||||
JavaCCppCSharp,
|
JavaCCppCSharp,
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
|
@ -917,8 +956,8 @@ where
|
||||||
|
|
||||||
serde_json::from_value::<DatabaseProperty>(value.to_owned()).unwrap_or_else(|error| {
|
serde_json::from_value::<DatabaseProperty>(value.to_owned()).unwrap_or_else(|error| {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Could not parse value because of error, defaulting to DatabaseProperty::Unsupported:\n= ERROR:\n{error:?}\n= JSON:\n{:?}\n---",
|
"Could not parse value because of error, defaulting to DatabaseProperty::Unsupported:\n= ERROR:\n{error:#?}\n= JSON:\n{:#?}\n---",
|
||||||
serde_json::to_string_pretty(&value).expect("to pretty print the database property error")
|
serde_json::to_string_pretty(&value).unwrap()
|
||||||
);
|
);
|
||||||
DatabaseProperty::Unsupported(value.to_owned())
|
DatabaseProperty::Unsupported(value.to_owned())
|
||||||
}),
|
}),
|
||||||
|
|
@ -944,7 +983,7 @@ pub struct Relation {
|
||||||
// TODO: Paginate all possible responses
|
// TODO: Paginate all possible responses
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct QueryResponse<T> {
|
pub struct QueryResponse<T> {
|
||||||
pub has_more: Option<bool>,
|
pub has_more: bool,
|
||||||
pub next_cursor: Option<String>,
|
pub next_cursor: Option<String>,
|
||||||
pub results: Vec<T>,
|
pub results: Vec<T>,
|
||||||
}
|
}
|
||||||
|
|
@ -1141,7 +1180,7 @@ where
|
||||||
// Notion forgets to set the formula type, so we're doing it's homework
|
// Notion forgets to set the formula type, so we're doing it's homework
|
||||||
"formula" => {
|
"formula" => {
|
||||||
if let Value::Object(object) = value {
|
if let Value::Object(object) = value {
|
||||||
if object.get("type").is_none() {
|
if let None = object.get("type") {
|
||||||
object.insert("type".to_owned(), json!("string"));
|
object.insert("type".to_owned(), json!("string"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1184,7 +1223,7 @@ where
|
||||||
serde_json::from_value::<Property>(value.to_owned()).unwrap_or_else(|error| {
|
serde_json::from_value::<Property>(value.to_owned()).unwrap_or_else(|error| {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Could not parse value because of error, defaulting to Property::Unsupported:\n= ERROR:\n{error:#?}\n= JSON:\n{}\n---",
|
"Could not parse value because of error, defaulting to Property::Unsupported:\n= ERROR:\n{error:#?}\n= JSON:\n{}\n---",
|
||||||
serde_json::to_string_pretty(&value).expect("to pretty print Property errors")
|
serde_json::to_string_pretty(&value).unwrap()
|
||||||
);
|
);
|
||||||
Property::Unsupported(value.to_owned())
|
Property::Unsupported(value.to_owned())
|
||||||
}),
|
}),
|
||||||
|
|
@ -1197,21 +1236,10 @@ where
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Formula {
|
pub enum Formula {
|
||||||
Boolean {
|
Boolean { boolean: Option<bool> },
|
||||||
boolean: Option<bool>,
|
Date { date: Option<Date> },
|
||||||
},
|
Number { number: Option<f32> },
|
||||||
Date {
|
String { string: Option<String> },
|
||||||
date: Option<Date>,
|
|
||||||
},
|
|
||||||
Number {
|
|
||||||
number: Option<f32>,
|
|
||||||
},
|
|
||||||
String {
|
|
||||||
string: Option<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
|
@ -1265,9 +1293,6 @@ pub enum RichText {
|
||||||
href: Option<String>,
|
href: Option<String>,
|
||||||
annotations: Annotations,
|
annotations: Annotations,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
|
@ -1280,24 +1305,11 @@ pub struct Text {
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Mention {
|
pub enum Mention {
|
||||||
Database {
|
Database { database: PartialDatabase },
|
||||||
database: PartialDatabase,
|
Date { date: Date },
|
||||||
},
|
LinkPreview { link_preview: LinkPreview },
|
||||||
Date {
|
Page { page: PartialPage },
|
||||||
date: Date,
|
User { user: PartialUser },
|
||||||
},
|
|
||||||
LinkPreview {
|
|
||||||
link_preview: LinkPreview,
|
|
||||||
},
|
|
||||||
Page {
|
|
||||||
page: PartialPage,
|
|
||||||
},
|
|
||||||
User {
|
|
||||||
user: PartialUser,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
|
@ -1423,60 +1435,33 @@ pub enum Color {
|
||||||
PurpleBackground,
|
PurpleBackground,
|
||||||
PinkBackground,
|
PinkBackground,
|
||||||
RedBackground,
|
RedBackground,
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Parent {
|
pub enum Parent {
|
||||||
PageId {
|
PageId { page_id: String },
|
||||||
page_id: String,
|
DatabaseId { database_id: String },
|
||||||
},
|
BlockId { block_id: String },
|
||||||
DatabaseId {
|
|
||||||
database_id: String,
|
|
||||||
},
|
|
||||||
BlockId {
|
|
||||||
block_id: String,
|
|
||||||
},
|
|
||||||
Workspace,
|
Workspace,
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum File {
|
pub enum File {
|
||||||
File {
|
File { file: NotionFile },
|
||||||
file: NotionFile,
|
External { external: ExternalFile },
|
||||||
},
|
|
||||||
External {
|
|
||||||
external: ExternalFile,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Icon {
|
pub enum Icon {
|
||||||
Emoji {
|
Emoji { emoji: String },
|
||||||
emoji: String,
|
File { file: NotionFile },
|
||||||
},
|
External { external: ExternalFile },
|
||||||
File {
|
|
||||||
file: NotionFile,
|
|
||||||
},
|
|
||||||
External {
|
|
||||||
external: ExternalFile,
|
|
||||||
},
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
|
@ -1497,7 +1482,4 @@ pub enum DatabaseFormulaType {
|
||||||
Date,
|
Date,
|
||||||
Number,
|
Number,
|
||||||
String,
|
String,
|
||||||
|
|
||||||
#[serde(other)]
|
|
||||||
Unsupported,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue