diff options
author | 2021-05-16 23:40:13 -0700 | |
---|---|---|
committer | 2021-05-16 23:40:13 -0700 | |
commit | 68819a887e9498c8786baaa6c3f098801b91e354 (patch) | |
tree | acfb6f1c0ba0eb3357afc143b4e8f7c45b298d23 | |
parent | e498b3c10e4975993352048b502cbcf55193850b (diff) | |
download | notion-68819a887e9498c8786baaa6c3f098801b91e354.tar.gz notion-68819a887e9498c8786baaa6c3f098801b91e354.tar.zst notion-68819a887e9498c8786baaa6c3f098801b91e354.zip |
Get Children Blocks (#9)
* wip
* generic block
* refactor
* all blocks
* format
* PR Feedback
* more feedback
Co-authored-by: Brett Spradling <bspradling@godaddy.com>
-rw-r--r-- | src/lib.rs | 45 | ||||
-rw-r--r-- | src/models.rs | 151 |
2 files changed, 189 insertions, 7 deletions
@@ -1,11 +1,11 @@ use crate::models::search::{DatabaseQuery, SearchRequest}; -use crate::models::{Database, DatabaseId, ListResponse, Object, Page}; +use crate::models::{Block, BlockId, Database, DatabaseId, ListResponse, Object, Page}; use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::{header, Client, ClientBuilder, RequestBuilder}; use serde::de::DeserializeOwned; use snafu::{ResultExt, Snafu}; -mod models; +pub mod models; const NOTION_API_VERSION: &str = "2021-05-13"; @@ -127,6 +127,17 @@ impl NotionApi { ) .await?) } + + pub async fn get_block_children<T: Identifiable<Type = BlockId>>( + &self, + block_id: T, + ) -> Result<ListResponse<Block>, Error> { + Ok(NotionApi::make_json_request(self.client.get(&format!( + "https://api.notion.com/v1/blocks/{block_id}/children", + block_id = block_id.id() + ))) + .await?) + } } #[cfg(test)] @@ -135,8 +146,8 @@ mod tests { use crate::models::search::{ DatabaseQuery, FilterCondition, FilterProperty, FilterValue, NotionSearch, TextCondition, }; - use crate::models::Object; - use crate::NotionApi; + use crate::models::{BlockId, Object}; + use crate::{Identifiable, NotionApi}; fn test_token() -> String { let token = { @@ -227,6 +238,32 @@ mod tests { } #[tokio::test] + async fn get_block_children() -> Result<(), Box<dyn std::error::Error>> { + let api = test_client(); + + let search_response = api + .search(NotionSearch::Filter { + value: FilterValue::Page, + property: FilterProperty::Object, + }) + .await?; + + println!("{:?}", search_response.results.len()); + + for object in search_response.results { + match object { + Object::Page { page } => api + .get_block_children(BlockId::from(page.id())) + .await + .unwrap(), + _ => panic!("Should not have received anything but pages!"), + }; + } + + Ok(()) + } + + #[tokio::test] async fn query_database() -> Result<(), Box<dyn std::error::Error>> { let api = test_client(); diff --git a/src/models.rs b/src/models.rs index f0909d9..99ee203 100644 --- a/src/models.rs +++ b/src/models.rs @@ -136,12 +136,130 @@ pub struct Page { } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] -pub struct Block {} +pub struct BlockCommon { + id: BlockId, + created_time: DateTime<Utc>, + last_edited_time: DateTime<Utc>, + has_children: bool, +} -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct TextAndChildren { + text: Vec<RichText>, + children: Option<Vec<Block>>, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct Text { + text: Vec<RichText>, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct ToDoFields { + text: Vec<RichText>, + checked: bool, + children: Option<Vec<Block>>, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct ChildPageFields { + title: String, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +#[serde(tag = "type")] +#[serde(rename_all = "snake_case")] +pub enum Block { + Paragraph { + #[serde(flatten)] + common: BlockCommon, + paragraph: TextAndChildren, + }, + #[serde(rename = "heading_1")] + Heading1 { + #[serde(flatten)] + common: BlockCommon, + heading_1: Text, + }, + #[serde(rename = "heading_2")] + Heading2 { + #[serde(flatten)] + common: BlockCommon, + heading_2: Text, + }, + #[serde(rename = "heading_3")] + Heading3 { + #[serde(flatten)] + common: BlockCommon, + heading_3: Text, + }, + BulletedListItem { + #[serde(flatten)] + common: BlockCommon, + bulleted_list_item: TextAndChildren, + }, + NumberedListItem { + #[serde(flatten)] + common: BlockCommon, + numbered_list_item: TextAndChildren, + }, + ToDo { + #[serde(flatten)] + common: BlockCommon, + to_do: ToDoFields, + }, + Toggle { + #[serde(flatten)] + common: BlockCommon, + toggle: TextAndChildren, + }, + ChildPage { + #[serde(flatten)] + common: BlockCommon, + child_page: ChildPageFields, + }, + #[serde(other)] + Unsupported, +} + +impl Identifiable for Block { + type Type = BlockId; + + fn id(&self) -> &Self::Type { + use Block::*; + match self { + Paragraph { common, .. } + | Heading1 { common, .. } + | Heading2 { common, .. } + | Heading3 { common, .. } + | BulletedListItem { common, .. } + | NumberedListItem { common, .. } + | ToDo { common, .. } + | Toggle { common, .. } + | ChildPage { common, .. } => &common.id, + Unsupported {} => { + panic!("Trying to reference identifier for unsupported block!") + } + } + } +} + +impl Identifiable for Page { + type Type = PageId; + + fn id(&self) -> &Self::Type { + &self.id + } +} + +#[derive(Eq, Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(tag = "object")] #[serde(rename_all = "snake_case")] pub enum Object { + Block { + #[serde(flatten)] + block: Block, + }, Database { #[serde(flatten)] database: Database, @@ -158,7 +276,34 @@ pub enum Object { #[serde(flatten)] user: User, }, - Block {}, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)] +#[serde(transparent)] +pub struct BlockId(String); + +impl BlockId { + pub fn id(&self) -> &str { + &self.0 + } + + pub fn from(page_id: &PageId) -> Self { + BlockId(page_id.clone().0) + } +} + +impl Identifiable for BlockId { + type Type = BlockId; + + fn id(&self) -> &Self::Type { + self + } +} + +impl Display for BlockId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } } impl Object { |