diff --git a/src/lib.rs b/src/lib.rs
index 8ad2c24..fec5bd5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,8 +3,9 @@ use crate::models::error::ErrorResponse;
use crate::models::search::{DatabaseQuery, SearchRequest};
use crate::models::{Database, ListResponse, Object, Page};
use ids::{AsIdentifier, PageId};
-use models::block::Block;
-use models::PageCreateRequest;
+use models::block::{Block, CreateBlock};
+use models::paging::PagingCursor;
+use models::{PageCreateRequest, PageUpdateRequest, UpdateBlockChildrenRequest};
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::{header, Client, ClientBuilder, RequestBuilder};
use tracing::Instrument;
@@ -199,6 +200,33 @@ impl NotionApi {
}
}
+ /// Updates a page and return the updated page
+ pub async fn update_page
(
+ &self,
+ page_id: P,
+ page: T,
+ ) -> Result
+ where
+ P: AsIdentifier,
+ T: Into,
+ {
+ let result = self
+ .make_json_request(
+ self.client
+ .patch(&format!(
+ "https://api.notion.com/v1/pages/{page_id}",
+ page_id = page_id.as_id()
+ ))
+ .json(&page.into()),
+ )
+ .await?;
+
+ match result {
+ Object::Page { page } => Ok(page),
+ response => Err(Error::UnexpectedResponse { response }),
+ }
+ }
+
/// Query a database and return the matching pages.
pub async fn query_database(
&self,
@@ -225,6 +253,25 @@ impl NotionApi {
}
}
+ /// Get a block by [BlockId].
+ pub async fn get_block>(
+ &self,
+ page_id: T,
+ ) -> Result {
+ let result = self
+ .make_json_request(self.client.get(format!(
+ "https://api.notion.com/v1/blocks/{}",
+ page_id.as_id()
+ )))
+ .await?;
+
+ match result {
+ Object::Block { block } => Ok(block),
+ response => Err(Error::UnexpectedResponse { response }),
+ }
+ }
+
+ /// Get block children a block by [BlockId].
pub async fn get_block_children>(
&self,
block_id: T,
@@ -241,4 +288,103 @@ impl NotionApi {
response => Err(Error::UnexpectedResponse { response }),
}
}
+
+ /// Get block children a block by [BlockId].
+ pub async fn get_block_children_with_cursor>(
+ &self,
+ block_id: T,
+ cursor: PagingCursor,
+ ) -> Result, Error> {
+ let result = self
+ .make_json_request(
+ self.client
+ .get(&format!(
+ "https://api.notion.com/v1/blocks/{block_id}/children",
+ block_id = block_id.as_id()
+ ))
+ .query(&[("start_cursor", cursor.0)]),
+ )
+ .await?;
+
+ match result {
+ Object::List { list } => Ok(list.expect_blocks()?),
+ response => Err(Error::UnexpectedResponse { response }),
+ }
+ }
+
+ /// Append block children under a block by [BlockId].
+ pub async fn append_block_children(
+ &self,
+ block_id: P,
+ request: T,
+ ) -> Result, Error>
+ where
+ P: AsIdentifier,
+ T: Into,
+ {
+ let result = self
+ .make_json_request(
+ self.client
+ .patch(&format!(
+ "https://api.notion.com/v1/blocks/{block_id}/children",
+ block_id = block_id.as_id()
+ ))
+ .json(&request.into()),
+ )
+ .await?;
+
+ match result {
+ Object::List { list } => Ok(list.expect_blocks()?),
+ response => Err(Error::UnexpectedResponse { response }),
+ }
+ }
+
+ /// Delete a block by [BlockId].
+ pub async fn delete_block>(
+ &self,
+ block_id: T,
+ ) -> Result {
+ let result = self
+ .make_json_request(self.client.delete(&format!(
+ "https://api.notion.com/v1/blocks/{block_id}",
+ block_id = block_id.as_id()
+ )))
+ .await?;
+
+ match result {
+ Object::Block { block } => Ok(block),
+ response => Err(Error::UnexpectedResponse { response }),
+ }
+ }
+
+ /// Update a block by [BlockId].
+ pub async fn update_block(
+ &self,
+ block_id: P,
+ block: T,
+ ) -> Result
+ where
+ P: AsIdentifier,
+ T: Into,
+ {
+ // using CreateBlock is not perfect
+ // technically speaking you can update text on a todo block and not touch checked by not settings it
+ // but I don't want to create a new type for this
+ // or make checked optional in CreateBlock
+ let result = self
+ .make_json_request(
+ self.client
+ .patch(&format!(
+ "https://api.notion.com/v1/blocks/{block_id}",
+ block_id = block_id.as_id()
+ ))
+ .json(&block.into()),
+ )
+ .await?;
+
+ match result {
+ Object::Block { block } => Ok(block),
+ response => Err(Error::UnexpectedResponse { response }),
+ }
+ }
}
diff --git a/src/models/mod.rs b/src/models/mod.rs
index 795accd..53b322c 100644
--- a/src/models/mod.rs
+++ b/src/models/mod.rs
@@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::ids::{AsIdentifier, DatabaseId, PageId};
-use crate::models::block::{Block, CreateBlock};
+use crate::models::block::{Block, CreateBlock, FileOrEmojiObject};
use crate::models::error::ErrorResponse;
use crate::models::paging::PagingCursor;
use crate::models::users::User;
@@ -182,7 +182,7 @@ impl Properties {
}
}
-#[derive(Serialize, Debug, Eq, PartialEq)]
+#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
pub struct PageCreateRequest {
pub parent: Parent,
pub properties: Properties,
@@ -190,6 +190,20 @@ pub struct PageCreateRequest {
pub children: Option>,
}
+#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
+pub struct UpdateBlockChildrenRequest {
+ pub children: Vec,
+}
+
+#[derive(Serialize, Debug, Eq, PartialEq)]
+pub struct PageUpdateRequest {
+ pub properties: Properties,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub archived: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub icon: Option,
+}
+
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct Page {
pub id: PageId,
diff --git a/src/models/paging.rs b/src/models/paging.rs
index c38ae11..043ff00 100644
--- a/src/models/paging.rs
+++ b/src/models/paging.rs
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
#[serde(transparent)]
-pub struct PagingCursor(String);
+pub struct PagingCursor(pub(crate) String);
#[derive(Serialize, Debug, Eq, PartialEq, Default, Clone)]
pub struct Paging {
diff --git a/src/models/search.rs b/src/models/search.rs
index c9e7e91..aaf373e 100644
--- a/src/models/search.rs
+++ b/src/models/search.rs
@@ -44,7 +44,7 @@ pub struct Filter {
value: FilterValue,
}
-#[derive(Serialize, Debug, Eq, PartialEq, Default)]
+#[derive(Serialize, Debug, Eq, PartialEq, Default, Clone)]
pub struct SearchRequest {
#[serde(skip_serializing_if = "Option::is_none")]
query: Option,
@@ -56,6 +56,21 @@ pub struct SearchRequest {
paging: Option,
}
+impl Pageable for SearchRequest {
+ fn start_from(
+ self,
+ starting_point: Option,
+ ) -> Self {
+ SearchRequest {
+ paging: Some(Paging {
+ start_cursor: starting_point,
+ page_size: self.paging.and_then(|p| p.page_size),
+ }),
+ ..self
+ }
+ }
+}
+
#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
#[serde(rename_all = "snake_case")]
pub enum TextCondition {
@@ -318,7 +333,7 @@ impl Pageable for DatabaseQuery {
}
}
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Debug, Eq, PartialEq, Clone)]
pub enum NotionSearch {
/// When supplied, limits which pages are returned by comparing the query to the page title.
Query(String),
diff --git a/src/models/text.rs b/src/models/text.rs
index 841e7d7..c2233e6 100644
--- a/src/models/text.rs
+++ b/src/models/text.rs
@@ -29,19 +29,25 @@ pub enum TextColor {
/// Rich text annotations
/// See
-#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Default)]
pub struct Annotations {
+ #[serde(skip_serializing_if = "Option::is_none")]
pub bold: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub code: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub color: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub italic: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub strikethrough: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub underline: Option,
}
/// Properties common on all rich text objects
/// See
-#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Default)]
pub struct RichTextCommon {
pub plain_text: String,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -55,9 +61,10 @@ pub struct Link {
pub url: String,
}
-#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Default)]
pub struct Text {
pub content: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub link: Option,
}
diff --git a/src/models/users.rs b/src/models/users.rs
index b7702d0..8bc3337 100644
--- a/src/models/users.rs
+++ b/src/models/users.rs
@@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
pub struct UserCommon {
pub id: UserId,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub name: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
pub avatar_url: Option,
}