From 68819a887e9498c8786baaa6c3f098801b91e354 Mon Sep 17 00:00:00 2001 From: Brett Spradling Date: Sun, 16 May 2021 23:40:13 -0700 Subject: [PATCH] Get Children Blocks (#9) * wip * generic block * refactor * all blocks * format * PR Feedback * more feedback Co-authored-by: Brett Spradling --- src/lib.rs | 45 +++++++++++++-- src/models.rs | 151 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c74aadf..e059a48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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>( + &self, + block_id: T, + ) -> Result, 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 = { @@ -226,6 +237,32 @@ mod tests { Ok(()) } + #[tokio::test] + async fn get_block_children() -> Result<(), Box> { + 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> { 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, + last_edited_time: DateTime, + has_children: bool, +} -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct TextAndChildren { + text: Vec, + children: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct Text { + text: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct ToDoFields { + text: Vec, + checked: bool, + children: Option>, +} + +#[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 {